From b71073c4bcbe95aea6a554f2f5eb0a1723b98d5b Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Sun, 8 Jul 2012 00:52:54 +0000 Subject: Fix always 0 tautologly revealed by gcc46 Approved by: cperciva MFC after: 1 week --- usr.sbin/ancontrol/ancontrol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ancontrol/ancontrol.c b/usr.sbin/ancontrol/ancontrol.c index 4a114538a19f9..839cdcb311ea1 100644 --- a/usr.sbin/ancontrol/ancontrol.c +++ b/usr.sbin/ancontrol/ancontrol.c @@ -1443,7 +1443,7 @@ an_enable_leap_mode(const char *iface, const char *username) caps = (struct an_ltv_caps *)&areq; - if (!caps->an_softcaps & AN_AUTHTYPE_LEAP) { + if (!(caps->an_softcaps & AN_AUTHTYPE_LEAP)) { fprintf(stderr, "Firmware does not support LEAP\n"); exit(1); } -- cgit v1.3 From f6c336fe660cba4c7803265fc6387ea75eb0c611 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Mon, 9 Jul 2012 06:21:46 +0000 Subject: Remove "prefer_source" address selection option. FreeBSD has had an implementation of RFC 3484 for this purpose for a long time and "prefer_source" was never implemented actually. ND6_IFF_PREFER_SOURCE macro is left intact. --- sbin/ifconfig/af_inet6.c | 2 -- sbin/ifconfig/ifconfig.8 | 8 +------- sys/netinet6/nd6.h | 2 +- usr.sbin/ndp/ndp.8 | 11 +---------- usr.sbin/ndp/ndp.c | 7 ------- 5 files changed, 3 insertions(+), 27 deletions(-) (limited to 'usr.sbin') diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index 0731238bf9bc2..d74b0d21492b7 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -509,8 +509,6 @@ static struct cmd inet6_cmds[] = { DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), - DEF_CMD("prefer_source",ND6_IFF_PREFER_SOURCE, setnd6flags), - DEF_CMD("-prefer_source",-ND6_IFF_PREFER_SOURCE,setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 44f6c8491e02f..f22cc50598ca1 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -28,7 +28,7 @@ .\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 .\" $FreeBSD$ .\" -.Dd May 27, 2012 +.Dd July 9, 2012 .Dt IFCONFIG 8 .Os .Sh NAME @@ -716,12 +716,6 @@ Set a flag to enable Neighbor Unreachability Detection. .It Cm -nud Clear a flag .Cm nud . -.It Cm prefer_source -Set a flag to prefer addresses on the interface as candidates of the -source address for outgoing packets. -.It Cm -prefer_source -Clear a flag -.Cm prefer_source . .El .Pp The following parameters are specific to cloning diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index c44e7048b8cc8..eb0bd44ccda44 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -79,7 +79,7 @@ struct nd_ifinfo { #define ND6_IFF_PERFORMNUD 0x1 #define ND6_IFF_ACCEPT_RTADV 0x2 -#define ND6_IFF_PREFER_SOURCE 0x4 /* XXX: not related to ND. */ +#define ND6_IFF_PREFER_SOURCE 0x4 /* Not used in FreeBSD. */ #define ND6_IFF_IFDISABLED 0x8 /* IPv6 operation is disabled due to * DAD failure. (XXX: not ND-specific) */ diff --git a/usr.sbin/ndp/ndp.8 b/usr.sbin/ndp/ndp.8 index 76f558ca2439a..1fb63f815af76 100644 --- a/usr.sbin/ndp/ndp.8 +++ b/usr.sbin/ndp/ndp.8 @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 2, 2009 +.Dd July 9, 2012 .Dt NDP 8 .Os .\" @@ -192,15 +192,6 @@ on This flag is set by .Va net.inet6.ip6.auto_linklocal sysctl variable. -.It Ic prefer_source -Prefer addresses on the -.Ar interface -as candidates of the source address for outgoing packets. -The default value of this flag is off. -For more details about the entire algorithm of source address -selection, see the -.Pa IMPLEMENTATION -file supplied with the KAME kit. .It Ic disabled Disable IPv6 operation on the interface. When disabled, the interface discards any IPv6 packets diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c index e245ac2976da1..be21d0fc24a35 100644 --- a/usr.sbin/ndp/ndp.c +++ b/usr.sbin/ndp/ndp.c @@ -1007,9 +1007,6 @@ ifinfo(ifname, argc, argv) #endif #ifdef ND6_IFF_AUTO_LINKLOCAL SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); -#endif -#ifdef ND6_IFF_PREFER_SOURCE - SETFLAG("prefer_source", ND6_IFF_PREFER_SOURCE); #endif SETVALUE("basereachable", ND.basereachable); SETVALUE("retrans", ND.retrans); @@ -1083,10 +1080,6 @@ ifinfo(ifname, argc, argv) #ifdef ND6_IFF_AUTO_LINKLOCAL if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) printf("auto_linklocal "); -#endif -#ifdef ND6_IFF_PREFER_SOURCE - if ((ND.flags & ND6_IFF_PREFER_SOURCE)) - printf("prefer_source "); #endif } putc('\n', stdout); -- cgit v1.3 From 739047446d4d7a52aeab4c7035f6ef819d8ea09d Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Mon, 9 Jul 2012 07:25:09 +0000 Subject: Make usbusN logging pseudo-interface used by usbdump(8) clonable. One is now created/destroyed automatically by usbdump(8). Note that "hw.usb.no_pf" loader tunable is now obsolete. Reviewed by: hselasky --- sys/dev/usb/usb_pf.c | 182 ++++++++++++++++++++++++++++++++++++++------- usr.sbin/usbdump/usbdump.c | 26 ++++++- 2 files changed, 181 insertions(+), 27 deletions(-) (limited to 'usr.sbin') diff --git a/sys/dev/usb/usb_pf.c b/sys/dev/usb/usb_pf.c index 6d654393668f8..4c88051b41f4a 100644 --- a/sys/dev/usb/usb_pf.c +++ b/sys/dev/usb/usb_pf.c @@ -44,8 +44,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include #include #include @@ -58,35 +60,144 @@ __FBSDID("$FreeBSD$"); #include #include -static int usb_no_pf; +#define USBUSNAME "usbus" + +static void usbpf_init(void); +static void usbpf_uninit(void); +static int usbpf_ioctl(struct ifnet *, u_long, caddr_t); +static int usbpf_clone_match(struct if_clone *, const char *); +static int usbpf_clone_create(struct if_clone *, char *, size_t, caddr_t); +static int usbpf_clone_destroy(struct if_clone *, struct ifnet *); +static struct usb_bus *usbpf_ifname2ubus(const char *); +static uint32_t usbpf_aggregate_xferflags(struct usb_xfer_flags *); +static uint32_t usbpf_aggregate_status(struct usb_xfer_flags_int *); +static int usbpf_xfer_frame_is_read(struct usb_xfer *, uint32_t); +static uint32_t usbpf_xfer_precompute_size(struct usb_xfer *, int); + +static struct if_clone usbpf_cloner = IFC_CLONE_INITIALIZER( + USBUSNAME, NULL, IF_MAXUNIT, + NULL, usbpf_clone_match, usbpf_clone_create, usbpf_clone_destroy); + +SYSINIT(usbpf_init, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, usbpf_init, NULL); +SYSUNINIT(usbpf_uninit, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, usbpf_uninit, NULL); + +static void +usbpf_init(void) +{ -SYSCTL_INT(_hw_usb, OID_AUTO, no_pf, CTLFLAG_RW, - &usb_no_pf, 0, "Set to disable USB packet filtering"); + if_clone_attach(&usbpf_cloner); +} -TUNABLE_INT("hw.usb.no_pf", &usb_no_pf); +static void +usbpf_uninit(void) +{ + int devlcnt; + device_t *devlp; + devclass_t dc; + struct usb_bus *ubus; + int error; + int i; + + if_clone_detach(&usbpf_cloner); + + dc = devclass_find(USBUSNAME); + if (dc == NULL) + return; + error = devclass_get_devices(dc, &devlp, &devlcnt); + if (error) + return; + for (i = 0; i < devlcnt; i++) { + ubus = device_get_softc(devlp[i]); + if (ubus != NULL && ubus->ifp != NULL) + usbpf_clone_destroy(&usbpf_cloner, ubus->ifp); + } +} -void -usbpf_attach(struct usb_bus *ubus) +static int +usbpf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + + /* No configuration allowed. */ + return (EINVAL); +} + +static struct usb_bus * +usbpf_ifname2ubus(const char *ifname) { + device_t dev; + devclass_t dc; + int unit; + int error; + + if (strncmp(ifname, USBUSNAME, sizeof(USBUSNAME)) <= 0) + return (NULL); + error = ifc_name2unit(ifname, &unit); + if (error || unit < 0) + return (NULL); + dc = devclass_find(USBUSNAME); + if (dc == NULL) + return (NULL); + dev = devclass_get_device(dc, unit); + if (dev == NULL) + return (NULL); + + return (device_get_softc(dev)); +} + +static int +usbpf_clone_match(struct if_clone *ifc, const char *name) +{ + struct usb_bus *ubus; + + ubus = usbpf_ifname2ubus(name); + if (ubus == NULL) + return (0); + if (ubus->ifp != NULL) + return (0); + + return (1); +} + +static int +usbpf_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) +{ + int error; + int unit; struct ifnet *ifp; + struct usb_bus *ubus; - if (usb_no_pf != 0) { - ubus->ifp = NULL; - return; - } + error = ifc_name2unit(name, &unit); + if (error || unit < 0) + return (error); + ubus = usbpf_ifname2ubus(name); + if (ubus == NULL) + return (1); + if (ubus->ifp != NULL) + return (1); + + error = ifc_alloc_unit(ifc, &unit); + if (error) { + ifc_free_unit(ifc, unit); + device_printf(ubus->parent, "usbpf: Could not allocate " + "instance\n"); + return (error); + } ifp = ubus->ifp = if_alloc(IFT_USB); if (ifp == NULL) { + ifc_free_unit(ifc, unit); device_printf(ubus->parent, "usbpf: Could not allocate " "instance\n"); - return; + return (ENOSPC); } - - if_initname(ifp, "usbus", device_get_unit(ubus->bdev)); - ifp->if_flags = IFF_CANTCONFIG; + strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname)); + ifp->if_softc = ubus; + ifp->if_dname = ifc->ifc_name; + ifp->if_dunit = unit; + ifp->if_ioctl = usbpf_ioctl; if_attach(ifp); - if_up(ifp); - + ifp->if_flags |= IFF_UP; + rt_ifmsg(ifp); /* * XXX According to the specification of DLT_USB, it indicates * packets beginning with USB setup header. But not sure all @@ -94,6 +205,31 @@ usbpf_attach(struct usb_bus *ubus) */ bpfattach(ifp, DLT_USB, USBPF_HDR_LEN); + return (0); +} + +static int +usbpf_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) +{ + struct usb_bus *ubus; + int unit; + + ubus = ifp->if_softc; + unit = ifp->if_dunit; + + ubus->ifp = NULL; + bpfdetach(ifp); + if_detach(ifp); + if_free(ifp); + ifc_free_unit(ifc, unit); + + return (0); +} + +void +usbpf_attach(struct usb_bus *ubus) +{ + if (bootverbose) device_printf(ubus->parent, "usbpf: Attached\n"); } @@ -101,15 +237,11 @@ usbpf_attach(struct usb_bus *ubus) void usbpf_detach(struct usb_bus *ubus) { - struct ifnet *ifp = ubus->ifp; - if (ifp != NULL) { - bpfdetach(ifp); - if_down(ifp); - if_detach(ifp); - if_free(ifp); - } - ubus->ifp = NULL; + if (ubus->ifp != NULL) + usbpf_clone_destroy(&usbpf_cloner, ubus->ifp); + if (bootverbose) + device_printf(ubus->parent, "usbpf: Detached\n"); } static uint32_t @@ -259,8 +391,6 @@ usbpf_xfertap(struct usb_xfer *xfer, int type) bus = xfer->xroot->bus; /* sanity checks */ - if (usb_no_pf != 0) - return; if (bus->ifp == NULL) return; if (!bpf_peers_present(bus->ifp->if_bpf)) diff --git a/usr.sbin/usbdump/usbdump.c b/usr.sbin/usbdump/usbdump.c index 1e8d171763b42..7e7eae923eea5 100644 --- a/usr.sbin/usbdump/usbdump.c +++ b/usr.sbin/usbdump/usbdump.c @@ -795,6 +795,8 @@ main(int argc, char *argv[]) int o; int filt_unit; int filt_ep; + int s; + int ifindex; const char *optstring; char *pp; @@ -887,9 +889,20 @@ main(int argc, char *argv[]) /* clear ifr structure */ memset(&ifr, 0, sizeof(ifr)); + /* Try to create usbusN interface if it is not available. */ + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s < 0) + errx(EXIT_FAILURE, "Could not open a socket"); + ifindex = if_nametoindex(i_arg); + if (ifindex == 0) { + (void)strlcpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + errx(EXIT_FAILURE, "Invalid bus interface: %s", i_arg); + } + for ( ; v >= USBPF_HDR_LEN; v >>= 1) { (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); - (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); + (void)strlcpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) break; } @@ -930,6 +943,17 @@ main(int argc, char *argv[]) printf("%d packets received by filter\n", us.bs_recv); printf("%d packets dropped by kernel\n", us.bs_drop); + /* + * Destroy the usbusN interface only if it was created by + * usbdump(8). Ignore when it was already destroyed. + */ + if (ifindex == 0 && if_nametoindex(i_arg) > 0) { + (void)strlcpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + warn("SIOCIFDESTROY ioctl failed"); + } + close(s); + if (p->fd > 0) close(p->fd); if (p->rfd > 0) -- cgit v1.3 From 131f6ef0344ed5793a1ec887775dfafee6b2e47a Mon Sep 17 00:00:00 2001 From: "Andrey V. Elsukov" Date: Mon, 9 Jul 2012 07:37:10 +0000 Subject: We don't need to check the result of sending signal when -R option is specified. Submitted by: Ilya A. Arkhipov MFC after: 1 week --- usr.sbin/newsyslog/newsyslog.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c index 227ea004d9f58..67a363e55114f 100644 --- a/usr.sbin/newsyslog/newsyslog.c +++ b/usr.sbin/newsyslog/newsyslog.c @@ -1972,7 +1972,8 @@ do_zipwork(struct zipwork_entry *zwork) else pgm_name++; - if (zwork->zw_swork != NULL && zwork->zw_swork->sw_pidok <= 0) { + if (zwork->zw_swork != NULL && zwork->zw_swork->run_cmd == 0 && + zwork->zw_swork->sw_pidok <= 0) { warnx( "log %s not compressed because daemon(s) not notified", zwork->zw_fname); -- cgit v1.3 From ef2319499196c59f3925cfd5b09a7001b203ee74 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Mon, 9 Jul 2012 08:11:16 +0000 Subject: - Add IFT_L2VLAN (vlan(4)) support. - Add -P option to support PID file. When -a is specified /var/run/rarpd.pid is used, and when an interface is specified /var/run/rarpd..pid is used by default. --- etc/rc.d/rarpd | 3 +- usr.sbin/rarpd/Makefile | 3 ++ usr.sbin/rarpd/rarpd.8 | 14 ++++++++- usr.sbin/rarpd/rarpd.c | 80 ++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 81 insertions(+), 19 deletions(-) (limited to 'usr.sbin') diff --git a/etc/rc.d/rarpd b/etc/rc.d/rarpd index db5f0c4c8c499..9dffe4302fb23 100755 --- a/etc/rc.d/rarpd +++ b/etc/rc.d/rarpd @@ -13,8 +13,9 @@ name="rarpd" rcvar="rarpd_enable" command="/usr/sbin/${name}" -pidfile="/var/run/${name}.pid" required_files="/etc/ethers" load_rc_config $name +pidfile="${rarpd_pidfile:-/var/run/${name}.pid}" + run_rc_command "$1" diff --git a/usr.sbin/rarpd/Makefile b/usr.sbin/rarpd/Makefile index 7f34457697e35..52a9f92d76882 100644 --- a/usr.sbin/rarpd/Makefile +++ b/usr.sbin/rarpd/Makefile @@ -4,6 +4,9 @@ PROG= rarpd MAN= rarpd.8 +DPADD= ${LIBUTIL} +LDADD= -lutil + WARNS?= 3 # This breaks with format strings returned by expand_syslog_m().. argh! #FORMAT_AUDIT?= 1 diff --git a/usr.sbin/rarpd/rarpd.8 b/usr.sbin/rarpd/rarpd.8 index 8552b69689a89..78412b3076820 100644 --- a/usr.sbin/rarpd/rarpd.8 +++ b/usr.sbin/rarpd/rarpd.8 @@ -16,7 +16,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 16, 2001 +.Dd July 9, 2012 .Dt RARPD 8 .Os .Sh NAME @@ -27,9 +27,11 @@ .Fl a .Op Fl dfsv .Op Fl t Ar directory +.Op Fl P Ar pidfile .Nm .Op Fl dfsv .Op Fl t Ar directory +.Op Fl P Ar pidfile .Ar interface .Sh DESCRIPTION The @@ -92,6 +94,15 @@ instead of via .Xr syslog 3 . .It Fl f Run in the foreground. +.It Fl P +Specify the pathname of the PID file. +If not specified, +.Pa /var/run/rarpd.pid +or +.Pa /var/run/rarpd.ifname.pid +will be used depending on the +.Fl a +flag or the specified interface name. .It Fl s Supply a response to any RARP request for which an ethernet to IP address mapping exists; do not depend on the existence of @@ -115,6 +126,7 @@ Enable verbose syslogging. .It Pa /etc/ethers .It Pa /etc/hosts .It Pa /tftpboot +.It Pa /var/run/rarpd.pid .El .Sh SEE ALSO .Xr bpf 4 diff --git a/usr.sbin/rarpd/rarpd.c b/usr.sbin/rarpd/rarpd.c index 25d98c6e0b549..f10a7da527f17 100644 --- a/usr.sbin/rarpd/rarpd.c +++ b/usr.sbin/rarpd/rarpd.c @@ -27,8 +27,8 @@ __FBSDID("$FreeBSD$"); /* * rarpd - Reverse ARP Daemon * - * Usage: rarpd -a [-dfsv] [-t directory] [hostname] - * rarpd [-dfsv] [-t directory] interface [hostname] + * Usage: rarpd -a [-dfsv] [-t directory] [-P pidfile] [hostname] + * rarpd [-dfsv] [-t directory] [-P pidfile] interface [hostname] * * 'hostname' is optional solely for backwards compatibility with Sun's rarpd. * Currently, the argument is ignored. @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include /* Cast a struct sockaddr to a struct sockaddr_in */ #define SATOSIN(sa) ((struct sockaddr_in *)(sa)) @@ -99,6 +100,11 @@ int sflag; /* ignore /tftpboot */ static u_char zero[6]; +static char pidfile_buf[PATH_MAX]; +static char *pidfile; +#define RARPD_PIDFILE "/var/run/rarpd.%s.pid" +static struct pidfh *pidfile_fh; + static int bpf_open(void); static in_addr_t choose_ipaddr(in_addr_t **, in_addr_t, in_addr_t); static char *eatoa(u_char *); @@ -140,7 +146,7 @@ main(int argc, char *argv[]) openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON); opterr = 0; - while ((op = getopt(argc, argv, "adfst:v")) != -1) + while ((op = getopt(argc, argv, "adfsP:t:v")) != -1) switch (op) { case 'a': ++aflag; @@ -158,6 +164,12 @@ main(int argc, char *argv[]) ++sflag; break; + case 'P': + strncpy(pidfile_buf, optarg, sizeof(pidfile_buf) - 1); + pidfile_buf[sizeof(pidfile_buf) - 1] = '\0'; + pidfile = pidfile_buf; + break; + case 't': tftp_dir = optarg; break; @@ -181,10 +193,23 @@ main(int argc, char *argv[]) init(ifname); if (!fflag) { + if (pidfile == NULL && ifname != NULL && aflag == 0) { + snprintf(pidfile_buf, sizeof(pidfile_buf) - 1, + RARPD_PIDFILE, ifname); + pidfile_buf[sizeof(pidfile_buf) - 1] = '\0'; + pidfile = pidfile_buf; + } + /* If pidfile == NULL, /var/run/.pid will be used. */ + pidfile_fh = pidfile_open(pidfile, 0600, NULL); + if (pidfile_fh == NULL) + logmsg(LOG_ERR, "Cannot open or create pidfile: %s", + (pidfile == NULL) ? "/var/run/rarpd.pid" : pidfile); if (daemon(0,0)) { logmsg(LOG_ERR, "cannot fork"); + pidfile_remove(pidfile_fh); exit(1); } + pidfile_write(pidfile_fh); } rarp_loop(); return(0); @@ -234,6 +259,7 @@ init_one(struct ifaddrs *ifa, char *target, int pass1) ii = (struct if_info *)malloc(sizeof(*ii)); if (ii == NULL) { logmsg(LOG_ERR, "malloc: %m"); + pidfile_remove(pidfile_fh); exit(1); } bzero(ii, sizeof(*ii)); @@ -251,6 +277,7 @@ init_one(struct ifaddrs *ifa, char *target, int pass1) ii2 = (struct if_info *)malloc(sizeof(*ii2)); if (ii2 == NULL) { logmsg(LOG_ERR, "malloc: %m"); + pidfile_remove(pidfile_fh); exit(1); } memcpy(ii2, ii, sizeof(*ii2)); @@ -273,8 +300,11 @@ init_one(struct ifaddrs *ifa, char *target, int pass1) case AF_LINK: ll = (struct sockaddr_dl *)ifa->ifa_addr; - if (ll->sdl_type == IFT_ETHER) + switch (ll->sdl_type) { + case IFT_ETHER: + case IFT_L2VLAN: bcopy(LLADDR(ll), ii->ii_eaddr, 6); + } break; } } @@ -293,6 +323,7 @@ init(char *target) error = getifaddrs(&ifhead); if (error) { logmsg(LOG_ERR, "getifaddrs: %m"); + pidfile_remove(pidfile_fh); exit(1); } /* @@ -339,8 +370,8 @@ static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", - "usage: rarpd -a [-dfsv] [-t directory]", - " rarpd [-dfsv] [-t directory] interface"); + "usage: rarpd -a [-dfsv] [-t directory] [-P pidfile]", + " rarpd [-dfsv] [-t directory] [-P pidfile] interface"); exit(1); } @@ -361,6 +392,7 @@ bpf_open(void) if (fd == -1) { logmsg(LOG_ERR, "%s: %m", device); + pidfile_remove(pidfile_fh); exit(1); } return fd; @@ -399,12 +431,12 @@ rarp_open(char *device) immediate = 1; if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) { logmsg(LOG_ERR, "BIOCIMMEDIATE: %m"); - exit(1); + goto rarp_open_err; } strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) { logmsg(LOG_ERR, "BIOCSETIF: %m"); - exit(1); + goto rarp_open_err; } /* * Check that the data link layer is an Ethernet; this code won't @@ -412,20 +444,24 @@ rarp_open(char *device) */ if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) == -1) { logmsg(LOG_ERR, "BIOCGDLT: %m"); - exit(1); + goto rarp_open_err; } if (dlt != DLT_EN10MB) { logmsg(LOG_ERR, "%s is not an ethernet", device); - exit(1); + goto rarp_open_err; } /* * Set filter program. */ if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) { logmsg(LOG_ERR, "BIOCSETF: %m"); - exit(1); + goto rarp_open_err; } return fd; + +rarp_open_err: + pidfile_remove(pidfile_fh); + exit(1); } /* @@ -480,16 +516,16 @@ rarp_loop(void) if (iflist == NULL) { logmsg(LOG_ERR, "no interfaces"); - exit(1); + goto rarpd_loop_err; } if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) { logmsg(LOG_ERR, "BIOCGBLEN: %m"); - exit(1); + goto rarpd_loop_err; } buf = malloc(bufsize); if (buf == NULL) { logmsg(LOG_ERR, "malloc: %m"); - exit(1); + goto rarpd_loop_err; } while (1) { @@ -509,7 +545,7 @@ rarp_loop(void) if (errno == EINTR) continue; logmsg(LOG_ERR, "select: %m"); - exit(1); + goto rarpd_loop_err; } for (ii = iflist; ii != NULL; ii = ii->ii_next) { fd = ii->ii_fd; @@ -537,6 +573,11 @@ rarp_loop(void) } } #undef bhp + return; + +rarpd_loop_err: + pidfile_remove(pidfile_fh); + exit(1); } /* @@ -562,12 +603,12 @@ rarp_bootable(in_addr_t addr) else { if (chdir(tftp_dir) == -1) { logmsg(LOG_ERR, "chdir: %s: %m", tftp_dir); - exit(1); + goto rarp_bootable_err; } d = opendir("."); if (d == NULL) { logmsg(LOG_ERR, "opendir: %m"); - exit(1); + goto rarp_bootable_err; } dd = d; } @@ -575,6 +616,10 @@ rarp_bootable(in_addr_t addr) if (strncmp(dent->d_name, ipname, 8) == 0) return 1; return 0; + +rarp_bootable_err: + pidfile_remove(pidfile_fh); + exit(1); } /* @@ -678,6 +723,7 @@ update_arptab(u_char *ep, in_addr_t ipaddr) r = socket(PF_ROUTE, SOCK_RAW, 0); if (r == -1) { logmsg(LOG_ERR, "raw route socket: %m"); + pidfile_remove(pidfile_fh); exit(1); } pid = getpid(); -- cgit v1.3 From 2ef23c6d3b433e00bda7cf6dfa993c492ba842a8 Mon Sep 17 00:00:00 2001 From: Takanori Watanabe Date: Mon, 9 Jul 2012 09:38:53 +0000 Subject: Add range and table revision checking to avoid abend. PR:bin/169707 Submitted by:Dan Lukes MFC after:3 days. --- usr.sbin/acpi/acpidump/acpi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/acpi/acpidump/acpi.c b/usr.sbin/acpi/acpidump/acpi.c index f8ac2a3781010..8a05ef1e73262 100644 --- a/usr.sbin/acpi/acpidump/acpi.c +++ b/usr.sbin/acpi/acpidump/acpi.c @@ -654,16 +654,24 @@ acpi_handle_tcpa(ACPI_TABLE_HEADER *sdp) printf(END_COMMENT); return; } + if(sdp->Revision == 1){ + printf("\tOLD TCPA spec log found. Dumping not supported.\n"); + printf(END_COMMENT); + return; + } vaddr = (unsigned char *)acpi_map_physical(paddr, len); vend = vaddr + len; while (vaddr != NULL) { - if (vaddr + sizeof(struct TCPAevent) >= vend) + if ((vaddr + sizeof(struct TCPAevent) >= vend)|| + (vaddr + sizeof(struct TCPAevent) < vaddr)) break; event = (struct TCPAevent *)(void *)vaddr; if (vaddr + event->event_size >= vend) break; + if (vaddr + event->event_size < vaddr) + break; if (event->event_type == 0 && event->event_size == 0) break; #if 0 -- cgit v1.3 From bb21242625fbd60eedf36d9bd45588d8439eb8cc Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Mon, 9 Jul 2012 15:44:35 +0000 Subject: Remove end of line whitespace. --- usr.sbin/rarpd/rarpd.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/rarpd/rarpd.8 b/usr.sbin/rarpd/rarpd.8 index 78412b3076820..3c75de0032fa6 100644 --- a/usr.sbin/rarpd/rarpd.8 +++ b/usr.sbin/rarpd/rarpd.8 @@ -96,7 +96,7 @@ instead of via Run in the foreground. .It Fl P Specify the pathname of the PID file. -If not specified, +If not specified, .Pa /var/run/rarpd.pid or .Pa /var/run/rarpd.ifname.pid -- cgit v1.3 From 6f9cd2a9dc84b8309f19939ccfb1ada88ca07905 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 13 Jul 2012 13:23:48 +0000 Subject: The etcupdate utility is a tool for managing updates to files that are not updated as part of `make installworld' such as files in /etc. It manages updates by doing a three-way merge of changes made to these files against the local versions. It is also designed to minimize the amount of user intervention with the goal of simplifying upgrades for clusters of machines. The primary difference from mergemaster is that etcupdate requires less manual work. The primary difference from etcmerge is that etcupdate updates files in-place similar to mergemaster rather than building a separate /etc tree. Requested by: obrien, kib, theraven, joeld (among others) --- tools/regression/usr.sbin/etcupdate/always.sh | 608 ++++++++ tools/regression/usr.sbin/etcupdate/conflicts.sh | 274 ++++ tools/regression/usr.sbin/etcupdate/fbsdid.sh | 327 +++++ tools/regression/usr.sbin/etcupdate/ignore.sh | 255 ++++ tools/regression/usr.sbin/etcupdate/tests.sh | 979 +++++++++++++ usr.sbin/Makefile | 1 + usr.sbin/etcupdate/Makefile | 6 + usr.sbin/etcupdate/etcupdate.8 | 784 ++++++++++ usr.sbin/etcupdate/etcupdate.sh | 1675 ++++++++++++++++++++++ 9 files changed, 4909 insertions(+) create mode 100755 tools/regression/usr.sbin/etcupdate/always.sh create mode 100755 tools/regression/usr.sbin/etcupdate/conflicts.sh create mode 100755 tools/regression/usr.sbin/etcupdate/fbsdid.sh create mode 100755 tools/regression/usr.sbin/etcupdate/ignore.sh create mode 100755 tools/regression/usr.sbin/etcupdate/tests.sh create mode 100644 usr.sbin/etcupdate/Makefile create mode 100644 usr.sbin/etcupdate/etcupdate.8 create mode 100755 usr.sbin/etcupdate/etcupdate.sh (limited to 'usr.sbin') diff --git a/tools/regression/usr.sbin/etcupdate/always.sh b/tools/regression/usr.sbin/etcupdate/always.sh new file mode 100755 index 0000000000000..5f5e586819f7e --- /dev/null +++ b/tools/regression/usr.sbin/etcupdate/always.sh @@ -0,0 +1,608 @@ +#!/bin/sh +# +# Copyright (c) 2010 Advanced Computing Technologies LLC +# Written by: John H. Baldwin +# 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. +# +# $FreeBSD$ + +# Various regression tests to test the -A flag to the 'update' command. + +WORKDIR=work + +usage() +{ + echo "Usage: always.sh [-w workdir]" + exit 1 +} + +# Allow the user to specify an alternate work directory. +while getopts "w:" option; do + case $option in + w) + WORKDIR=$OPTARG + ;; + *) + echo + usage + ;; + esac +done +shift $((OPTIND - 1)) +if [ $# -ne 0 ]; then + usage +fi + +CONFLICTS=$WORKDIR/conflicts +OLD=$WORKDIR/old +NEW=$WORKDIR/current +TEST=$WORKDIR/test + +# The various states of the comparison of a file between two trees. +states="equal first second difftype difflinks difffiles" + +# These tests deal with ignoring certain patterns of files. We run +# the test multiple times forcing the install of different patterns. +build_trees() +{ + local i + + rm -rf $OLD $NEW $TEST $CONFLICTS + + for i in $states; do + for j in $states; do + for k in $states; do + mkdir -p $OLD/$i/$j/$k $NEW/$i/$j/$k \ + $TEST/$i/$j/$k + done + done + done + + # What follows are the various warning/conflict cases from the + # larger regression tests. These results of many of these + # tests should be changed when installation is forced. The + # cases when these updates should still fail even when forced + # are: 1) it should not force the removal of a modified file + # and 2) it should not remove a subdirectory that contains a + # modified or added file. + + # /first/difftype/second: File with different local type + # removed. Should generate a warning. + mkfifo $OLD/first/difftype/second/fifo + mkdir $TEST/first/difftype/second/fifo + + # /first/difflinks/second: Modified link removed. Should + # generate a warning. + ln -s "old link" $OLD/first/difflinks/second/link + ln -s "test link" $TEST/first/difflinks/second/link + + # /first/difffiles/second: Modified file removed. Should + # generate a warning. + echo "foo" > $OLD/first/difffiles/second/file + echo "bar" > $TEST/first/difffiles/second/file + + # /second/second/difftype: Newly added file conflicts with + # existing file in test tree of a different type. Should + # generate a warning. + mkdir $NEW/second/second/difftype/dir + mkfifo $TEST/second/second/difftype/dir + + # /second/second/difflinks: Newly added link conflicts with + # existing link in test tree. Should generate a warning. + ln -s "new link" $NEW/second/second/difflinks/link + ln -s "test link" $TEST/second/second/difflinks/link + + # /second/second/difffiles: Newly added file conflicts with + # existing file in test tree. Should generate a warning. + echo "new" > $NEW/second/second/difffiles/file + echo "test" > $TEST/second/second/difffiles/file + + # /difftype/first/first: A removed file has changed type. + # This should generate a warning. + mkfifo $OLD/difftype/first/first/fifo + mkdir $NEW/difftype/first/first/fifo + + # /difftype/difftype/difftype: All three files (old, new, and + # test) are different types from each other. This should + # generate a warning. + mkfifo $OLD/difftype/difftype/difftype/one + mkdir $NEW/difftype/difftype/difftype/one + echo "foo" > $TEST/difftype/difftype/difftype/one + mkdir $OLD/difftype/difftype/difftype/two + echo "baz" > $NEW/difftype/difftype/difftype/two + ln -s "bar" $TEST/difftype/difftype/difftype/two + + # /difftype/difftype/difflinks: A file has changed from a + # non-link to a link in both the new and test trees, but the + # target of the new and test links differ. This should + # generate a new link conflict. + mkfifo $OLD/difftype/difftype/difflinks/link + ln -s "new" $NEW/difftype/difftype/difflinks/link + ln -s "test" $TEST/difftype/difftype/difflinks/link + + # /difftype/difftype/difffile: A file has changed from a + # non-regular file to a regular file in both the new and test + # trees, but the contents in the new and test files differ. + # This should generate a new file conflict. + ln -s "old" $OLD/difftype/difftype/difffiles/file + echo "foo" > $NEW/difftype/difftype/difffiles/file + echo "bar" > $TEST/difftype/difftype/difffiles/file + + # /difflinks/first/first: A modified link is missing in the + # test tree. This should generate a warning. + ln -s "old" $OLD/difflinks/first/first/link + ln -s "new" $NEW/difflinks/first/first/link + + # /difflinks/difftype/difftype: An updated link has been + # changed to a different file type in the test tree. This + # should generate a warning. + ln -s "old" $OLD/difflinks/difftype/difftype/link + ln -s "new" $NEW/difflinks/difftype/difftype/link + echo "test" > $TEST/difflinks/difftype/difftype/link + + # /difflinks/difflinks/difflinks: An updated link has been + # modified in the test tree and doesn't match either the old + # or new links. This should generate a warning. + ln -s "old" $OLD/difflinks/difflinks/difflinks/link + ln -s "new" $NEW/difflinks/difflinks/difflinks/link + ln -s "test" $TEST/difflinks/difflinks/difflinks/link + + # /difffiles/first/first: A removed file has been changed in + # the new tree. This should generate a warning. + echo "foo" > $OLD/difffiles/first/first/file + echo "bar" > $NEW/difffiles/first/first/file + + # /difffiles/difftype/difftype: An updated regular file has + # been changed to a different file type in the test tree. + # This should generate a warning. + echo "old" > $OLD/difffiles/difftype/difftype/file + echo "new" > $NEW/difffiles/difftype/difftype/file + mkfifo $TEST/difffiles/difftype/difftype/file + + # /difffiles/difffiles/difffiles: A modified regular file was + # updated in the new tree. The changes should be merged into + # to the new file if possible. If the merge fails, a conflict + # should be generated. For this test we just include the + # conflict case. + cat > $OLD/difffiles/difffiles/difffiles/conflict < $NEW/difffiles/difffiles/difffiles/conflict < $TEST/difffiles/difffiles/difffiles/conflict < $TEST/rmdir/extra/localfile.txt + + # /rmdir/conflict: Do not remove a directory with a conflicted + # remove file. This should generate a warning. + for i in $OLD $TEST; do + mkdir $i/rmdir/conflict + done + mkfifo $OLD/rmdir/conflict/difftype + mkdir $TEST/rmdir/conflict/difftype + + ## Tests for converting files to directories and vice versa + for i in $OLD $NEW $TEST; do + for j in already old fromdir todir; do + mkdir -p $i/dirchange/$j + done + done + + # /dirchange/fromdir/extradir: Convert a directory tree to a + # file. The test tree includes an extra file in the directory + # that is not present in the old tree. This should generate a + # warning. + for i in $OLD $TEST; do + mkdir $i/dirchange/fromdir/extradir + echo "foo" > $i/dirchange/fromdir/extradir/file + done + mkfifo $TEST/dirchange/fromdir/extradir/fifo + ln -s "bar" $NEW/dirchange/fromdir/extradir + + # /dirchange/fromdir/conflict: Convert a directory tree to a + # file. The test tree includes a local change that generates + # a warning and prevents the removal of the directory. + for i in $OLD $TEST; do + mkdir $i/dirchange/fromdir/conflict + done + echo "foo" > $OLD/dirchange/fromdir/conflict/somefile + echo "bar" > $TEST/dirchange/fromdir/conflict/somefile + mkfifo $NEW/dirchange/fromdir/conflict + + # /dirchange/todir/difffile: Convert a file to a directory + # tree. The test tree has a locally modified version of the + # file so that the conversion fails with a warning. + echo "foo" > $OLD/dirchange/todir/difffile + mkdir $NEW/dirchange/todir/difffile + echo "baz" > $NEW/dirchange/todir/difffile/file + echo "bar" > $TEST/dirchange/todir/difffile + + # /dirchange/todir/difftype: Similar to the previous test, but + # the conflict is due to a change in the file type. + echo "foo" > $OLD/dirchange/todir/difftype + mkdir $NEW/dirchange/todir/difftype + echo "baz" > $NEW/dirchange/todir/difftype/file + mkfifo $TEST/dirchange/todir/difftype +} + +# $1 - relative path to file that should be missing from TEST +missing() +{ + if [ -e $TEST/$1 -o -L $TEST/$1 ]; then + echo "File $1 should be missing" + fi +} + +# $1 - relative path to file that should be present in TEST +present() +{ + if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then + echo "File $1 should be present" + fi +} + +# $1 - relative path to file that should be a fifo in TEST +fifo() +{ + if ! [ -p $TEST/$1 ]; then + echo "File $1 should be a FIFO" + fi +} + +# $1 - relative path to file that should be a directory in TEST +dir() +{ + if ! [ -d $TEST/$1 ]; then + echo "File $1 should be a directory" + fi +} + +# $1 - relative path to file that should be a symlink in TEST +# $2 - optional value of the link +link() +{ + local val + + if ! [ -L $TEST/$1 ]; then + echo "File $1 should be a link" + elif [ $# -gt 1 ]; then + val=`readlink $TEST/$1` + if [ "$val" != "$2" ]; then + echo "Link $1 should link to \"$2\"" + fi + fi +} + +# $1 - relative path to regular file that should be present in TEST +# $2 - optional string that should match file contents +# $3 - optional MD5 of the flie contents, overrides $2 if present +file() +{ + local contents sum + + if ! [ -f $TEST/$1 ]; then + echo "File $1 should be a regular file" + elif [ $# -eq 2 ]; then + contents=`cat $TEST/$1` + if [ "$contents" != "$2" ]; then + echo "File $1 has wrong contents" + fi + elif [ $# -eq 3 ]; then + sum=`md5 -q $TEST/$1` + if [ "$sum" != "$3" ]; then + echo "File $1 has wrong contents" + fi + fi +} + +# $1 - relative path to a regular file that should have a conflict +# $2 - optional MD5 of the conflict file contents +conflict() +{ + local sum + + if ! [ -f $CONFLICTS/$1 ]; then + echo "File $1 missing conflict" + elif [ $# -gt 1 ]; then + sum=`md5 -q $CONFLICTS/$1` + if [ "$sum" != "$2" ]; then + echo "Conflict $1 has wrong contents" + fi + fi +} + +# $1 - relative path to a regular file that should not have a conflict +noconflict() +{ + if [ -f $CONFLICTS/$1 ]; then + echo "File $1 should not have a conflict" + fi +} + +if [ `id -u` -ne 0 ]; then + echo "must be root" +fi + +if [ -r /etc/etcupdate.conf ]; then + echo "WARNING: /etc/etcupdate.conf settings may break some tests." +fi + +# First run the test ignoring no patterns. + +build_trees + +etcupdate -r -d $WORKDIR -D $TEST > $WORKDIR/test.out + +cat > $WORKDIR/correct.out < \ + $WORKDIR/test1.out + +cat > $WORKDIR/correct1.out < +# 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. +# +# $FreeBSD$ + +# Various regression tests to run for the 'resolve' command. + +WORKDIR=work + +usage() +{ + echo "Usage: tests.sh [-w workdir]" + exit 1 +} + +# Allow the user to specify an alternate work directory. +while getopts "w:" option; do + case $option in + w) + WORKDIR=$OPTARG + ;; + *) + echo + usage + ;; + esac +done +shift $((OPTIND - 1)) +if [ $# -ne 0 ]; then + usage +fi + +CONFLICTS=$WORKDIR/conflicts +OLD=$WORKDIR/old +NEW=$WORKDIR/current +TEST=$WORKDIR/test + +# These tests deal with conflicts to a single file. For each test, we +# generate a conflict in /etc/login.conf. Each resolve option is tested +# to ensure it DTRT. +build_login_conflict() +{ + + rm -rf $OLD $NEW $TEST $CONFLICTS + mkdir -p $OLD/etc $NEW/etc $TEST/etc + + # Generate a conflict in /etc/login.conf. + cat > $OLD/etc/login.conf < $NEW/etc/login.conf < $TEST/etc/login.conf </dev/null +} + +# This is used to verify special handling for /etc/mail/aliases and +# the newaliases warning. +build_aliases_conflict() +{ + + rm -rf $OLD $NEW $TEST $CONFLICTS + mkdir -p $OLD/etc/mail $NEW/etc/mail $TEST/etc/mail + + # Generate a conflict in /etc/mail/aliases + cat > $OLD/etc/mail/aliases < $NEW/etc/mail/aliases < $TEST/etc/mail/aliases </dev/null +} + +# $1 - relative path to file that should be missing from TEST +missing() +{ + if [ -e $TEST/$1 -o -L $TEST/$1 ]; then + echo "File $1 should be missing" + fi +} + +# $1 - relative path to file that should be present in TEST +present() +{ + if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then + echo "File $1 should be present" + fi +} + +# $1 - relative path to regular file that should be present in TEST +# $2 - optional string that should match file contents +# $3 - optional MD5 of the flie contents, overrides $2 if present +file() +{ + local contents sum + + if ! [ -f $TEST/$1 ]; then + echo "File $1 should be a regular file" + elif [ $# -eq 2 ]; then + contents=`cat $TEST/$1` + if [ "$contents" != "$2" ]; then + echo "File $1 has wrong contents" + fi + elif [ $# -eq 3 ]; then + sum=`md5 -q $TEST/$1` + if [ "$sum" != "$3" ]; then + echo "File $1 has wrong contents" + fi + fi +} + +# $1 - relative path to a regular file that should have a conflict +# $2 - optional MD5 of the conflict file contents +conflict() +{ + local sum + + if ! [ -f $CONFLICTS/$1 ]; then + echo "File $1 missing conflict" + elif [ $# -gt 1 ]; then + sum=`md5 -q $CONFLICTS/$1` + if [ "$sum" != "$2" ]; then + echo "Conflict $1 has wrong contents" + fi + fi +} + +# $1 - relative path to a regular file that should no longer have a conflict +resolved() +{ + if [ -f $CONFLICTS/$1 ]; then + echo "Conflict $1 should be resolved" + fi +} + +if [ `id -u` -ne 0 ]; then + echo "must be root" +fi + +if [ -r /etc/etcupdate.conf ]; then + echo "WARNING: /etc/etcupdate.conf settings may break some tests." +fi + +# Test each of the following resolve options: 'p', 'mf', 'tf', 'r'. + +build_login_conflict + +# Verify that 'p' doesn't do anything. +echo "Checking 'p':" +echo 'p' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null + +file /etc/login.conf "" 95de92ea3f1bb1bf4f612a8b5908cddd +missing /etc/login.conf.db +conflict /etc/login.conf + +# Verify that 'mf' removes the conflict, but does nothing else. +echo "Checking 'mf':" +echo 'mf' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null + +file /etc/login.conf "" 95de92ea3f1bb1bf4f612a8b5908cddd +missing /etc/login.conf.db +resolved /etc/login.conf + +build_login_conflict + +# Verify that 'tf' installs the new version of the file. +echo "Checking 'tf':" +echo 'tf' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null + +file /etc/login.conf "" 7774a0f9a3a372c7c109c32fd31c4b6b +file /etc/login.conf.db +resolved /etc/login.conf + +build_login_conflict + +# Verify that 'r' installs the resolved version of the file. To +# simulate this, manually edit the merged file so that it doesn't +# contain conflict markers. +echo "Checking 'r':" +cat > $CONFLICTS/etc/login.conf </dev/null + +file /etc/login.conf "" 966e25984b9b63da8eaac8479dcb0d4d +file /etc/login.conf.db +resolved /etc/login.conf + +build_aliases_conflict + +# Verify that 'p' and 'mf' do not generate the newaliases warning. +echo "Checking newalias warning for 'p'": +echo 'p' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias +if [ $? -eq 0 ]; then + echo "+ Extra warning" +fi +echo "Checking newalias warning for 'mf'": +echo 'mf' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias +if [ $? -eq 0 ]; then + echo "+ Extra warning" +fi + +# Verify that 'tf' and 'r' do generate the newaliases warning. +build_aliases_conflict +echo "Checking newalias warning for 'tf'": +echo 'tf' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias +if [ $? -ne 0 ]; then + echo "- Missing warning" +fi + +build_aliases_conflict +cp $TEST/etc/mail/aliases $CONFLICTS/etc/mail/aliases +echo 'r' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias +if [ $? -ne 0 ]; then + echo "- Missing warning" +fi diff --git a/tools/regression/usr.sbin/etcupdate/fbsdid.sh b/tools/regression/usr.sbin/etcupdate/fbsdid.sh new file mode 100755 index 0000000000000..3a90a1e280c04 --- /dev/null +++ b/tools/regression/usr.sbin/etcupdate/fbsdid.sh @@ -0,0 +1,327 @@ +#!/bin/sh +# +# Copyright (c) 2010 Advanced Computing Technologies LLC +# Written by: John H. Baldwin +# 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. +# +# $FreeBSD$ + +# Various regression tests to test the -F flag to the 'update' command. + +WORKDIR=work + +usage() +{ + echo "Usage: fbsdid.sh [-w workdir]" + exit 1 +} + +# Allow the user to specify an alternate work directory. +while getopts "w:" option; do + case $option in + w) + WORKDIR=$OPTARG + ;; + *) + echo + usage + ;; + esac +done +shift $((OPTIND - 1)) +if [ $# -ne 0 ]; then + usage +fi + +CONFLICTS=$WORKDIR/conflicts +OLD=$WORKDIR/old +NEW=$WORKDIR/current +TEST=$WORKDIR/test + +# Store a FreeBSD ID string in a specified file. The first argument +# is the file, the remaining arguments are the comment to use. +store_id() +{ + local file + + file=$1 + shift + + echo -n '# $FreeBSD' >> $file + echo -n "$@" >> $file + echo '$' >> $file +} + +# These tests deal with FreeBSD ID string conflicts. We run the test +# twice, once without -F and once with -F. +build_trees() +{ + local i + + rm -rf $OLD $NEW $TEST $CONFLICTS + mkdir -p $OLD $NEW $TEST + + # remove: Remove a file where the only local difference is a + # change in the FreeBSD ID string. + store_id $OLD/remove + store_id $TEST/remove ": head/remove 12345 jhb " + + # old: Modify a file where the only local difference between + # the old and test files is a change in the FreeBSD ID string. + store_id $OLD/old ": src/old,v 1.1 jhb Exp " + store_id $NEW/old ": head/old 12345 jhb " + store_id $TEST/old ": head/old 12000 jhb " + for i in $OLD $TEST; do + cat >> $i/old <> $NEW/old <> $OLD/already <> $i/already <> $OLD/conflict <> $NEW/conflict <> $TEST/conflict <> $i/local <> $i/local <> $TEST/local <> $i/local-already <> $TEST/local-already < $WORKDIR/test.out + +cat > $WORKDIR/correct.out < $WORKDIR/testF.out + +cat > $WORKDIR/correctF.out < +# 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. +# +# $FreeBSD$ + +# Various regression tests to test the -I flag to the 'update' command. + +WORKDIR=work + +usage() +{ + echo "Usage: ignore.sh [-w workdir]" + exit 1 +} + +# Allow the user to specify an alternate work directory. +while getopts "w:" option; do + case $option in + w) + WORKDIR=$OPTARG + ;; + *) + echo + usage + ;; + esac +done +shift $((OPTIND - 1)) +if [ $# -ne 0 ]; then + usage +fi + +CONFLICTS=$WORKDIR/conflicts +OLD=$WORKDIR/old +NEW=$WORKDIR/current +TEST=$WORKDIR/test + +# These tests deal with ignoring certain patterns of files. We run the +# test multiple times ignoring different patterns. +build_trees() +{ + local i + + rm -rf $OLD $NEW $TEST $CONFLICTS + mkdir -p $OLD $NEW $TEST + + for i in $OLD $NEW $TEST; do + mkdir -p $i/tree + done + + # tree: Test three different cases (add, modify, remove) that all + # match the tree/* glob. + echo "foo" > $NEW/tree/add + for i in $OLD $TEST; do + echo "old" > $i/tree/modify + done + echo "new" > $NEW/tree/modify + for i in $OLD $TEST; do + echo "old" > $i/tree/remove + done + + # rmdir: Remove a whole tree. + for i in $OLD $TEST; do + mkdir $i/rmdir + echo "foo" > $i/rmdir/file + done +} + +# $1 - relative path to file that should be missing from TEST +missing() +{ + if [ -e $TEST/$1 -o -L $TEST/$1 ]; then + echo "File $1 should be missing" + fi +} + +# $1 - relative path to file that should be present in TEST +present() +{ + if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then + echo "File $1 should be present" + fi +} + +# $1 - relative path to file that should be a directory in TEST +dir() +{ + if ! [ -d $TEST/$1 ]; then + echo "File $1 should be a directory" + fi +} + +# $1 - relative path to regular file that should be present in TEST +# $2 - optional string that should match file contents +# $3 - optional MD5 of the flie contents, overrides $2 if present +file() +{ + local contents sum + + if ! [ -f $TEST/$1 ]; then + echo "File $1 should be a regular file" + elif [ $# -eq 2 ]; then + contents=`cat $TEST/$1` + if [ "$contents" != "$2" ]; then + echo "File $1 has wrong contents" + fi + elif [ $# -eq 3 ]; then + sum=`md5 -q $TEST/$1` + if [ "$sum" != "$3" ]; then + echo "File $1 has wrong contents" + fi + fi +} + +# $1 - relative path to a regular file that should have a conflict +# $2 - optional MD5 of the conflict file contents +conflict() +{ + local sum + + if ! [ -f $CONFLICTS/$1 ]; then + echo "File $1 missing conflict" + elif [ $# -gt 1 ]; then + sum=`md5 -q $CONFLICTS/$1` + if [ "$sum" != "$2" ]; then + echo "Conflict $1 has wrong contents" + fi + fi +} + +# $1 - relative path to a regular file that should not have a conflict +noconflict() +{ + if [ -f $CONFLICTS/$1 ]; then + echo "File $1 should not have a conflict" + fi +} + +if [ `id -u` -ne 0 ]; then + echo "must be root" +fi + +if [ -r /etc/etcupdate.conf ]; then + echo "WARNING: /etc/etcupdate.conf settings may break some tests." +fi + +# First run the test ignoring no patterns. + +build_trees + +etcupdate -r -d $WORKDIR -D $TEST > $WORKDIR/test.out + +cat > $WORKDIR/correct.out < $WORKDIR/test1.out + +cat > $WORKDIR/correct1.out < \ + $WORKDIR/test2.out + +cat > $WORKDIR/correct2.out < \ + $WORKDIR/test3.out + +cat > $WORKDIR/correct3.out < +# 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. +# +# $FreeBSD$ + +# Various regression tests to run for the 'update' command. + +WORKDIR=work + +usage() +{ + echo "Usage: tests.sh [-w workdir]" + exit 1 +} + +# Allow the user to specify an alternate work directory. +while getopts "w:" option; do + case $option in + w) + WORKDIR=$OPTARG + ;; + *) + echo + usage + ;; + esac +done +shift $((OPTIND - 1)) +if [ $# -ne 0 ]; then + usage +fi + +CONFLICTS=$WORKDIR/conflicts +OLD=$WORKDIR/old +NEW=$WORKDIR/current +TEST=$WORKDIR/test + +# The various states of the comparison of a file between two trees. +states="equal first second difftype difflinks difffiles" + +build_trees() +{ + local i j k + + rm -rf $OLD $NEW $TEST $CONFLICTS + mkdir -p $OLD/etc $NEW/etc $TEST/etc + + # For an given file, there are three different pair-wise + # relations between the three threes (old, new, and test): old + # vs new, old vs test, and new vs test. Each of these + # relations takes on one of six different states from the + # 'compare()' function in etcupdate: equal, onlyfirst, + # onlysecond, difftype, difflinks, difffiles. In addition, + # there are special considerations for considering cases such + # as a file merge that results in conflicts versus one that + # does not, special treatment of directories, etc. The tests + # below attempt to enumerate the three dimensional test matrix + # by having the path name use the three different tree states + # for the parent directories. + # + # Note that if the old and new files are identical (so first + # compare is "equal"), then the second and third comparisons + # will be the same. + # + # Note also that etcupdate only cares about files that are + # present in at least one of the old or new trees. Thus, none + # of the '*/second/second' cases are relevant. + + for i in $states; do + for j in $states; do + for k in $states; do + mkdir -p $OLD/$i/$j/$k $NEW/$i/$j/$k \ + $TEST/$i/$j/$k + done + done + done + + # /equal/equal/equal: Everything is equal. Nothing should happen. + for i in $OLD $NEW $TEST; do + mkfifo $i/equal/equal/equal/fifo + echo "foo" > $i/equal/equal/equal/file + mkdir $i/equal/equal/equal/dir + ln -s "bar" $i/equal/equal/equal/link + done + + # /equal/first/first: The file is missing from the test + # directory. Nothing should happen. + for i in $OLD $NEW; do + mkfifo $i/equal/first/first/fifo + echo "foo" > $i/equal/first/first/file + mkdir $i/equal/first/first/dir + ln -s "bar" $i/equal/first/first/link + done + + # /equal/difftype/difftype: The local file is a different + # type. Nothing should happen. + for i in $OLD $NEW; do + mkfifo $i/equal/difftype/difftype/fifo + mkdir $i/equal/difftype/difftype/fromdir + done + echo "bar" > $TEST/equal/difftype/difftype/fifo + ln -s "test" $TEST/equal/difftype/difftype/fromdir + + # /equal/difflinks/difflinks: The local file is a modified + # link. Nothing should happen. + for i in $OLD $NEW; do + ln -s "foo" $i/equal/difflinks/difflinks/link + done + ln -s "bar" $TEST/equal/difflinks/difflinks/link + + # /equal/difffiles/difffiles: The local file is a modified + # file. Nothing should happen. + for i in $OLD $NEW; do + echo "foo" > $i/equal/difffiles/difffiles/file + done + echo "bar" > $TEST/equal/difffiles/difffiles/file + + # /first/equal/second: Remove unmodified files. The files + # should all be removed. + for i in $OLD $TEST; do + mkfifo $i/first/equal/second/fifo + echo "foo" > $i/first/equal/second/file + mkdir $i/first/equal/second/emptydir + ln -s "bar" $i/first/equal/second/link + mkdir $i/first/equal/second/fulldir + echo "foo" > $i/first/equal/second/fulldir/file + done + + # /first/equal/*: Cannot occur. If the file is missing from + # new, then new vs test will always be 'second'. + + # /first/first/equal: Removed files are already removed. + # Nothing should happen. + mkfifo $OLD/first/first/equal/fifo + echo "foo" > $OLD/first/first/equal/file + mkdir $OLD/first/first/equal/dir + ln -s "bar" $OLD/first/first/equal/link + + # /first/first/*: Cannot occur. The files are missing from + # both new and test. + + # /first/second/*: Cannot happen, if the file is in old for + # old vs new, it cannot be missing for old vs test. + + # /first/difftype/second: File with different local type + # removed. Should generate a warning. + mkfifo $OLD/first/difftype/second/fifo + mkdir $TEST/first/difftype/second/fifo + + # /first/difftype/*: Cannot happen since the file is missing + # from new but present in test. + + # /first/difflinks/second: Modified link removed. Should + # generate a warning. + ln -s "old link" $OLD/first/difflinks/second/link + ln -s "test link" $TEST/first/difflinks/second/link + + # /first/difflinks/*: Cannot happen since the file is missing + # from new but present in test. + + # /first/difffiles/second: Modified file removed. Should + # generate a warning. + echo "foo" > $OLD/first/difffiles/second/file + echo "bar" > $TEST/first/difffiles/second/file + + # /first/difffiles/*: Cannot happen since the file is missing + # from new but present in test. + + # /second/equal/first: Added a new file that isn't present in + # test. The empty directory should be ignored. + echo "bar" > $NEW/second/equal/first/file + mkfifo $NEW/second/equal/first/fifo + ln -s "new" $NEW/second/equal/first/link + mkdir $NEW/second/equal/first/emptydir + mkdir $NEW/second/equal/first/fulldir + echo "foo" > $NEW/second/equal/first/fulldir/file + + # /second/equal/*: Cannot happen since the file is missing + # from test but present in new. + + # /second/first/*: Cannot happen since the file is missing + # from old. + + # /second/second/equal: Newly added file is already present in + # the test directory and identical to the new file. Nothing + # should happen. + for i in $NEW $TEST; do + mkfifo $i/second/second/equal/fifo + echo "foo" > $i/second/second/equal/file + mkdir $i/second/second/equal/dir + ln -s "bar" $i/second/second/equal/link + done + + # /second/second/first: Cannot happen. The file is in dest in + # the second test, so it can't be missing from the third test. + + # /second/second/second: Cannot happen. The file is in new in + # the first test, so it can't be missing from the third test. + + # /second/second/difftype: Newly added file conflicts with + # existing file in test tree of a different type. Should + # generate a warning. + mkdir $NEW/second/second/difftype/dir + mkfifo $TEST/second/second/difftype/dir + + # /second/second/difflinks: Newly added link conflicts with + # existing link in test tree. Should generate a warning. + ln -s "new link" $NEW/second/second/difflinks/link + ln -s "test link" $TEST/second/second/difflinks/link + + # /second/second/difffiles: Newly added file conflicts with + # existing file in test tree. Should generate a warning. + echo "new" > $NEW/second/second/difffiles/file + echo "test" > $TEST/second/second/difffiles/file + + # /second/difftype/*: Cannot happen since the file is missing + # from old. + + # /second/difflinks/*: Cannot happen since the file is missing + # from old. + + # /second/difffiles/*: Cannot happen since the file is missing + # from old. + + # /difftype/equal/difftype: Unmodified file has changed type. + # File should be updated to the new file. In the 'todir' case + # the directory won't actually be created because it is empty. + for i in $OLD $TEST; do + echo "foo" > $i/difftype/equal/difftype/file + mkdir $i/difftype/equal/difftype/fromdir + ln -s "old" $i/difftype/equal/difftype/todir + done + ln -s "test" $NEW/difftype/equal/difftype/file + mkfifo $NEW/difftype/equal/difftype/fromdir + mkdir $NEW/difftype/equal/difftype/todir + + # /difftype/equal/*: Cannot happen. Since the old file is a + # difftype from the new file and the test file is identical to + # the old file, the test file must be a difftype from the new + # file. + + # /difftype/first/first: A removed file has changed type. + # This should generate a warning. + mkfifo $OLD/difftype/first/first/fifo + mkdir $NEW/difftype/first/first/fifo + + # /difftype/first/*: Cannot happen. Since the new file exists + # and the dest file is missing, the last test must be 'first'. + + # /difftype/second/*: Cannot happen. The old file exists in + # the first test, so it cannot be missing in the second test. + + # /difftype/difftype/equal: A file has changed type, but the + # file in the test directory already matches the new file. Do + # nothing. + echo "foo" > $OLD/difftype/difftype/equal/fifo + mkfifo $OLD/difftype/difftype/equal/file + for i in $NEW $TEST; do + mkfifo $i/difftype/difftype/equal/fifo + echo "bar" > $i/difftype/difftype/equal/file + done + + # /difftype/difftype/first: Cannot happen. The dest file + # exists in the second test. + + # /difftype/difftype/second: Cannot happen. The new file + # exists in the first test. + + # /difftype/difftype/difftype: All three files (old, new, and + # test) are different types from each other. This should + # generate a warning. + mkfifo $OLD/difftype/difftype/difftype/one + mkdir $NEW/difftype/difftype/difftype/one + echo "foo" > $TEST/difftype/difftype/difftype/one + mkdir $OLD/difftype/difftype/difftype/two + echo "baz" > $NEW/difftype/difftype/difftype/two + ln -s "bar" $TEST/difftype/difftype/difftype/two + + # /difftype/difftype/difflinks: A file has changed from a + # non-link to a link in both the new and test trees, but the + # target of the new and test links differ. This should + # generate a new link conflict. + mkfifo $OLD/difftype/difftype/difflinks/link + ln -s "new" $NEW/difftype/difftype/difflinks/link + ln -s "test" $TEST/difftype/difftype/difflinks/link + + # /difftype/difftype/difffile: A file has changed from a + # non-regular file to a regular file in both the new and test + # trees, but the contents in the new and test files differ. + # This should generate a new file conflict. + ln -s "old" $OLD/difftype/difftype/difffiles/file + echo "foo" > $NEW/difftype/difftype/difffiles/file + echo "bar" > $TEST/difftype/difftype/difffiles/file + + # /difflinks/equal/difflinks: An unmodified symlink has + # changed. The link should be updated. + for i in $OLD $TEST; do + ln -s "old" $i/difflinks/equal/difflinks/link + done + ln -s "new" $NEW/difflinks/equal/difflinks/link + + # /difflinks/equal/*: Cannot happen. Since old is identical + # to test, the third test must be 'difflinks'. + + # /difflinks/first/first: A modified link is missing in the + # test tree. This should generate a warning. + ln -s "old" $OLD/difflinks/first/first/link + ln -s "new" $NEW/difflinks/first/first/link + + # /difflinks/first/*: Cannot happen. Since the test file is + # missing in the second test, it must be missing in the third + # test. + + # /difflinks/second/*: Cannot happen. The old link is present + # in the first test, so it cannot be missing in the second + # test. + + # /difflinks/difftype/difftype: An updated link has been + # changed to a different file type in the test tree. This + # should generate a warning. + ln -s "old" $OLD/difflinks/difftype/difftype/link + ln -s "new" $NEW/difflinks/difftype/difftype/link + echo "test" > $TEST/difflinks/difftype/difftype/link + + # /difflinks/difftype/*: Cannot happen. The old and new files + # are both links and the test file is not a link, so the third + # test must be 'difftype'. + + # /difflinks/difflinks/equal: An updated link has already been + # updated to the new target in the test tree. Nothing should + # happen. + ln -s "old" $OLD/difflinks/difflinks/equal/link + for i in $NEW $TEST; do + ln -s "new" $i/difflinks/difflinks/equal/link + done + + # /difflinks/difflinks/difflinks: An updated link has been + # modified in the test tree and doesn't match either the old + # or new links. This should generate a warning. + ln -s "old" $OLD/difflinks/difflinks/difflinks/link + ln -s "new" $NEW/difflinks/difflinks/difflinks/link + ln -s "test" $TEST/difflinks/difflinks/difflinks/link + + # /difflinks/difflinks/*: Cannot happen. All three files are + # links from the first two tests, so the third test can only + # be 'equal' or 'difflink'. + + # /difflinks/difffiles/*: Cannot happen. The old file is a + # link in the first test, so it cannot be a regular file in + # the second. + + # /difffiles/equal/difffiles: An unmodified file has been + # changed in new tree. The file should be updated to the new + # version. + for i in $OLD $TEST; do + echo "foo" > $i/difffiles/equal/difffiles/file + done + echo "bar" > $NEW/difffiles/equal/difffiles/file + + # /difffiles/equal/*: Cannot happen. Since the old file is + # identical to the test file, the third test must be + # 'difffiles'. + + # /difffiles/first/first: A removed file has been changed in + # the new tree. This should generate a warning. + echo "foo" > $OLD/difffiles/first/first/file + echo "bar" > $NEW/difffiles/first/first/file + + # /difffiles/first/*: Cannot happen. The new file is a + # regular file from the first test and the test file is + # missing in the second test, so the third test must be + # 'first'. + + # /difffiles/second/*: Cannot happen. The old file is present + # in the first test, so it must be present in the second test. + + # /difffiles/difftype/difftype: An updated regular file has + # been changed to a different file type in the test tree. + # This should generate a warning. + echo "old" > $OLD/difffiles/difftype/difftype/file + echo "new" > $NEW/difffiles/difftype/difftype/file + mkfifo $TEST/difffiles/difftype/difftype/file + + # /difffiles/difftype/*: Cannot happen. The new file is known + # to be a regular file from the first test, and the test file + # is known to exist as a different file type from the second + # test. The third test must be 'difftype'. + + # /difffiles/difflink/*: Cannot happen. The old file is known + # to be a regular file from the first test, so it cannot be a + # link in the second test. + + # /difffiles/difffiles/equal: An updated regular file has + # already been updated to match the new file in the test tree. + # Nothing should happen. + echo "foo" > $OLD/difffiles/difffiles/equal/file + for i in $NEW $TEST; do + echo "bar" > $i/difffiles/difffiles/equal/file + done + + # /difffiles/difffiles/difffiles: A modified regular file was + # updated in the new tree. The changes should be merged into + # to the new file if possible. If the merge fails, a conflict + # should be generated. + cat > $OLD/difffiles/difffiles/difffiles/simple < $NEW/difffiles/difffiles/difffiles/simple < $TEST/difffiles/difffiles/difffiles/simple < $OLD/difffiles/difffiles/difffiles/conflict < $NEW/difffiles/difffiles/difffiles/conflict < $TEST/difffiles/difffiles/difffiles/conflict < $NEW/adddir/partial/file + mkfifo $TEST/adddir/partial/fifo + + ## Tests for removing directories + mkdir -p $OLD/rmdir $NEW/rmdir $TEST/rmdir + + # /rmdir/extra: Do not remove a directory with an extra local file. + # This should generate a warning. + for i in $OLD $TEST; do + mkdir $i/rmdir/extra + done + echo "foo" > $TEST/rmdir/extra/localfile.txt + + # /rmdir/conflict: Do not remove a directory with a conflicted + # remove file. This should generate a warning. + for i in $OLD $TEST; do + mkdir $i/rmdir/conflict + done + mkfifo $OLD/rmdir/conflict/difftype + mkdir $TEST/rmdir/conflict/difftype + + # /rmdir/partial: Remove a complete hierarchy when part of the + # tree has already been removed locally. + for i in $OLD $TEST; do + mkdir -p $i/rmdir/partial/subdir + mkfifo $i/rmdir/partial/subdir/fifo + done + echo "foo" > $OLD/rmdir/partial/subdir/file + + ## Tests for converting files to directories and vice versa + for i in $OLD $NEW $TEST; do + for j in already old fromdir todir; do + mkdir -p $i/dirchange/$j + done + done + + # /dirchange/already/fromdir: Convert a directory tree to a + # file without conflicts where the test tree already has the + # new file. Nothing should happen. + mkdir $OLD/dirchange/already/fromdir + echo "blah" > $OLD/dirchange/already/fromdir/somefile + for i in $NEW $TEST; do + echo "bar" > $i/dirchange/already/fromdir + done + + # /dirchange/already/todir: Convert an unmodified file to a + # directory tree where the test tree already has the new + # tree. Nothing should happen. + echo "baz" > $OLD/dirchange/already/todir + for i in $NEW $TEST; do + mkdir $i/dirchange/already/todir + echo "blah" > $i/dirchange/already/todir/somefile + done + + # /dirchange/old/fromdir: Convert a directory tree to a file. + # The old files are unmodified and should be changed to the new tree. + for i in $OLD $TEST; do + mkdir $i/dirchange/old/fromdir + echo "blah" > $i/dirchange/old/fromdir/somefile + done + echo "bar" > $NEW/dirchange/old/fromdir + + # /dirchange/old/todir: Convert a file to a directory tree. + # The old file is unmodified and should be changed to the new + # tree. + for i in $OLD $TEST; do + echo "foo" > $i/dirchange/old/todir + done + mkdir $NEW/dirchange/old/todir + echo "bar" > $NEW/dirchange/old/todir/file + + # /dirchange/fromdir/extradir: Convert a directory tree to a + # file. The test tree includes an extra file in the directory + # that is not present in the old tree. This should generate a + # warning. + for i in $OLD $TEST; do + mkdir $i/dirchange/fromdir/extradir + echo "foo" > $i/dirchange/fromdir/extradir/file + done + mkfifo $TEST/dirchange/fromdir/extradir/fifo + ln -s "bar" $NEW/dirchange/fromdir/extradir + + # /dirchange/fromdir/conflict: Convert a directory tree to a + # file. The test tree includes a local change that generates + # a warning and prevents the removal of the directory. + for i in $OLD $TEST; do + mkdir $i/dirchange/fromdir/conflict + done + echo "foo" > $OLD/dirchange/fromdir/conflict/somefile + echo "bar" > $TEST/dirchange/fromdir/conflict/somefile + mkfifo $NEW/dirchange/fromdir/conflict + + # /dirchange/todir/difffile: Convert a file to a directory + # tree. The test tree has a locally modified version of the + # file so that the conversion fails with a warning. + echo "foo" > $OLD/dirchange/todir/difffile + mkdir $NEW/dirchange/todir/difffile + echo "baz" > $NEW/dirchange/todir/difffile/file + echo "bar" > $TEST/dirchange/todir/difffile + + # /dirchange/todir/difftype: Similar to the previous test, but + # the conflict is due to a change in the file type. + echo "foo" > $OLD/dirchange/todir/difftype + mkdir $NEW/dirchange/todir/difftype + echo "baz" > $NEW/dirchange/todir/difftype/file + mkfifo $TEST/dirchange/todir/difftype + + ## Tests for post-install actions + + # - Adding /etc/master.passwd should cause pwd_mkdb to be run + echo "foo:*:16000:100::0:0:& user:/home/foo:/bin/tcsh" > \ + $NEW/etc/master.passwd + + # - Verify that updating an unmodified /etc/login.conf builds + # /etc/login.conf.db. + cat > $OLD/etc/login.conf < $NEW/etc/login.conf < $OLD/etc/mail/aliases < $NEW/etc/mail/aliases < $TEST/etc/mail/aliases < $WORKDIR/testn.out + +cat > $WORKDIR/correct.out < $WORKDIR/test.out + +echo "Differences for real:" +diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out + +check_trees diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index c16821f7101d4..7df7e7d3a9dcb 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -23,6 +23,7 @@ SUBDIR= adduser \ digictl \ diskinfo \ dumpcis \ + etcupdate \ extattr \ extattrctl \ fifolog \ diff --git a/usr.sbin/etcupdate/Makefile b/usr.sbin/etcupdate/Makefile new file mode 100644 index 0000000000000..9f6d17ef2300e --- /dev/null +++ b/usr.sbin/etcupdate/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SCRIPTS=etcupdate.sh +MAN= etcupdate.8 + +.include diff --git a/usr.sbin/etcupdate/etcupdate.8 b/usr.sbin/etcupdate/etcupdate.8 new file mode 100644 index 0000000000000..a81ba86c26b62 --- /dev/null +++ b/usr.sbin/etcupdate/etcupdate.8 @@ -0,0 +1,784 @@ +.\" Copyright (c) 2010-2012 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd March 16, 2012 +.Dt ETCUPDATE 8 +.Os +.Sh NAME +.Nm etcupdate +.Nd "manage updates to system files not updated by installworld" +.Sh SYNOPSIS +.Nm +.Op Fl nBF +.Op Fl d Ar workdir +.Op Fl r | Fl s Ar source | Fl t Ar tarball +.Op Fl A Ar patterns +.Op Fl D Ar destdir +.Op Fl I Ar patterns +.Op Fl L Ar logfile +.Op Fl M Ar options +.Nm +.Cm build +.Op Fl B +.Op Fl d Ar workdir +.Op Fl s Ar source +.Op Fl L Ar logfile +.Op Fl M Ar options +.Ar tarball +.Nm +.Cm diff +.Op Fl d Ar workdir +.Op Fl D Ar destdir +.Op Fl I Ar patterns +.Op Fl L Ar logfile +.Nm +.Cm extract +.Op Fl B +.Op Fl d Ar workdir +.Op Fl s Ar source | Fl t Ar tarball +.Op Fl L Ar logfile +.Op Fl M Ar options +.Nm +.Cm resolve +.Op Fl d Ar workdir +.Op Fl D Ar destdir +.Op Fl L Ar logfile +.Nm +.Cm status +.Op Fl d Ar workdir +.Op Fl D Ar destdir +.Sh DESCRIPTION +The +.Nm +utility is a tool for managing updates to files that are not updated as +part of +.Sq make installworld +such as files in +.Pa /etc . +It manages updates by doing a three-way merge of changes made to these +files against the local versions. +It is also designed to minimize the amount of user intervention with +the goal of simplifying upgrades for clusters of machines. +.Pp +To perform a three-way merge, +.Nm +keeps copies of the current and previous versions of files that it manages. +These copies are stored in two trees known as the +.Dq current +and +.Dq previous +trees. +During a merge, +.Nm +compares the +.Dq current +and +.Dq previous +copies of each file to determine which changes need to be merged into the +local version of each file. +If a file can be updated without generating a conflict, +.Nm +will update the file automatically. +If the local changes to a file conflict with the changes made to a file in +the source tree, +then a merge conflict is generated. +The conflict must be resolved after the merge has finished. +The +.Nm +utility will not perform a new merge until all conflicts from an earlier +merge are resolved. +.Sh MODES +.Pp +The +.Nm +utility supports several modes of operation. +The mode is specified via an optional command argument. +If present, the command must be the first argument on the command line. +If a command is not specified, the default mode is used. +.Ss Default Mode +The default mode merges changes from the source tree to the destination +directory. +First, +it updates the +.Dq current +and +.Dq previous +trees. +Next, +it compares the two trees merging changes into the destination directory. +Finally, +it displays warnings for any conditions it could not handle automatically. +.Pp +If the +.Fl r +option is not specified, +then the first step taken is to update the +.Dq current +and +.Dq previous +trees. +If a +.Dq current +tree already exists, +then that tree is saved as the +.Dq previous +tree. +An older +.Dq previous +tree is removed if it exists. +By default the new +.Dq current +tree is built from a source tree. +However, +if a tarball is specified via the +.Fl t +option, +then the tree is extracted from that tarball instead. +.Pp +Next, +.Nm +compares the files in the +.Dq current +and +.Dq previous +trees. +If a file was removed from the +.Dq current +tree, +then it will be removed from the destination directory only if it +does not have any local modifications. +If a file was added to the +.Dq current +tree, +then it will be copied to the destination directory only if it +would not clobber an existing file. +If a file is changed in the +.Dq current +tree, +then +.Nm +will attempt to merge the changes into the version of the file in the +destination directory. +If the merge encounters conflicts, +then a version of the file with conflict markers will be saved for +future resolution. +If the merge does not encounter conflicts, +then the merged version of the file will be saved in the destination +directory. +If +.Nm +is not able to safely merge in changes to a file other than a merge conflict, +it will generate a warning. +.Pp +For each file that is updated a line will be output with a leading character +to indicate the action taken. +The possible actions follow: +.Pp +.Bl -tag -width "A" -compact -offset indent +.It A +Added +.It C +Conflict +.It D +Deleted +.It M +Merged +.It U +Updated +.El +.Pp +Finally, +if any warnings were encountered they are displayed after the merge has +completed. +.Pp +Note that for certain files +.Nm +will perform post-install actions any time that the file is updated. +Specifically, +.Xr pwd_mkdb 8 +is invoked if +.Pa /etc/master.passwd +is changed, +.Xr cap_mkdb 1 +is invoked to update +.Pa /etc/login.conf.db +if +.Pa /etc/login.conf +is changed, +.Xr newaliases 1 +is invoked if +.Pa /etc/mail/aliases +is changed, +and +.Pa /etc/rc.d/motd +is invoked if +.Pa /etc/motd +is changed. +One exception is that if +.Pa /etc/mail/aliases +is changed and the destination directory is not the default, +then a warning will be issued instead. +This is due to a limitation of the +.Xr newaliases 1 +command. +Similarly, +if +.Pa /etc/motd +is changed and the destination directory is not the default, +then +.Pa /etc/rc.d/motd +will not be executed due to a limitation of that script. +In this case no warning is issued as the result of +.Pa /etc/rc.d/motd +is merely cosmetic and will be corrected on the next reboot. +.Ss Build Mode +The +.Cm build +mode is used to build a tarball that contains a snapshot of a +.Dq current +tree. +This tarball can be used by the default and extract modes. +Using a tarball can allow +.Nm +to perform a merge without requiring a source tree that matches the +currently installed world. +The +.Fa tarball +argument specifies the name of the file to create. +The file will be a +.Xr tar 5 +file compressed with +.Xr bzip2 1 . +.Ss Diff Mode +The +.Cm diff +mode compares the versions of files in the destination directory to the +.Dq current +tree and generates a unified format diff of the changes. +This can be used to determine which files have been locally modified and how. +Note that +.Nm +does not manage files that are not maintained in the source tree such as +.Pa /etc/fstab +and +.Pa /etc/rc.conf . +.Ss Extract Mode +The +.Cm extract +mode generates a new +.Dq current +tree. +Unlike the default mode, +it does not save any existing +.Dq current +tree and does not modify any existing +.Dq previous +tree. +The new +.Dq current +tree can either be built from a source tree or extracted from a tarball. +.Ss Resolve Mode +The +.Cm resolve +mode is used to resolve any conflicts encountered during a merge. +In this mode, +.Nm +iterates over any existing conflicts prompting the user for actions to take +on each conflicted file. +For each file, the following actions are available: +.Pp +.Bl -tag -width "(tf) theirs-full" -compact +.It (p) postpone +Ignore this conflict for now. +.It (df) diff-full +Show all changes made to the merged file as a unified diff. +.It (e) edit +Change the merged file in an editor. +.It (r) resolved +Install the merged version of the file into the destination directory. +.It (mf) mine-full +Use the version of the file in the destination directory and ignore any +changes made to the file in the +.Dq current +tree. +.It (tf) theirs-full +Use the version of the file from the +.Dq current +tree and discard any local changes made to the file. +.It (h) help +Display the list of commands. +.El +.Ss Status Mode +The +.Cm status +mode shows a summary of the results of the most recent merge. +First it lists any files for which there are unresolved conflicts. +Next it lists any warnings generated during the last merge. +If the last merge did not generate any conflicts or warnings, +then nothing will be output. +.Sh OPTIONS +The following options are available. +Note that most options do not apply to all modes. +.Bl -tag -width ".Fl d Ar workdir" +.It Fl B +Do not build generated files in a private object tree. +Instead, +reuse the generated files from a previously built object tree that matches +the source tree. +This can be useful to avoid gratuitous conflicts in sendmail configuration +files when bootstrapping. +It can also be useful for building a tarball that matches a specific +world build. +.It Fl d Ar workdir +Specify an alternate directory to use as the work directory. +The work directory is used to store the +.Dq current +and +.Dq previous +trees as well as unresolved conflicts. +The default work directory is +.Pa /var/db/etcupdate . +.It Fl A Ar patterns +Always install the new version of any files that match any of the patterns +listed in +.Ar patterns . +Each pattern is evaluated as an +.Xr sh 1 +shell pattern. +This option may be specified multiple times to specify multiple patterns. +Multiple space-separated patterns may also be specified in a single +option. +Note that ignored files specified via the +.Ev IGNORE_FILES +variable or the +.Fl I +option will not be installed. +.It Fl D Ar destdir +Specify an alternate destination directory as the target of a merge. +This is analagous to the +.Dv DESTDIR +variable used with +.Sq make installworld . +The default destination directory is an empty string which results in +merges updating +.Pa /etc +on the local machine. +.It Fl F +Ignore changes in the FreeBSD ID string when comparing files in the +destination directory to files in either of the +.Dq current +or +.Dq previous +trees. +In +.Cm diff +mode, +this reduces noise due to FreeBSD ID string changes in the output. +During an update this can simplify handling for harmless conflicts caused +by FreeBSD ID string changes. +.Pp +Specifically, +if a file in the destination directory is identical to the same file in the +.Dq previous +tree modulo the FreeBSD ID string, +then the file is treated as if it was unmodified and the +.Dq current +version of the file will be installed. +Similarly, +if a file in the destination directory is identical to the same file in the +.Dq current +tree modulo the FreeBSD ID string, +then the +.Dq current +version of the file will be installed to update the ID string. +If the +.Dq previous +and +.Dq current +versions of the file are identical, +then +.Nm +will not change the file in the destination directory. +.Pp +Due to limitations in the +.Xr diff 1 +command, +this option may not have an effect if there are other changes in a file that +are close to the FreeBSD ID string. +.It Fl I Ar patterns +Ignore any files that match any of the patterns listed in +.Ar patterns . +No warnings or other messages will be generated for those files during a +merge. +Each pattern is evaluated as an +.Xr sh 1 +shell pattern. +This option may be specified multiple times to specify multiple patterns. +Multiple space-separated patterns may also be specified in a single +option. +.It Fl L Ar logfile +Specify an alternate path for the log file. +The +.Nm +utility logs each command that it invokes along with the standard output +and standard error to this file. +By default the log file is stored in a file named +.Pa log +in the work directory. +.It Fl M Ar options +Pass +.Ar options +as additional parameters to +.Xr make 1 +when building a +.Dq current +tree. +This can be used for to set the +.Dv TARGET +or +.Dv TARGET_ARCH +variables for a cross-build. +.It Fl n +Enable +.Dq dry-run +mode. +Do not merge any changes to the destination directory. +Instead, +report what actions would be taken during a merge. +Note that the existing +.Dq current +and +.Dq previous +trees will not be changed. +If the +.Fl r +option is not specified, +then a temporary +.Dq current +tree will be extracted to perform the comparison. +.It Fl r +Do not update the +.Dq current +and +.Dq previous +trees during a merge. +This can be used to +.Dq re-run +a previous merge operation. +.It Fl s Ar source +Specify an alternate source tree to use when building or extracting a +.Dq current +tree. +The default source tree is +.Pa /usr/src . +.It Fl t Ar tarball +Extract a new +.Dq current +tree from a tarball previously generated by the +.Cm build +command rather than building the tree from a source tree. +.El +.Sh CONFIG FILE +The +.Nm +utility can also be configured by setting variables in an optional +configuration file named +.Pa /etc/etcupdate.conf . +Note that command line options override settings in the configuration file. +The configuration file is executed by +.Xr sh 1 , +so it uses that syntax to set configuration variables. +The following variables can be set: +.Bl -tag -width ".Ev ALWAYS_INSTALL" +.It Ev ALWAYS_INSTALL +Always install files that match any of the patterns listed in this variable +similar to the +.Fl A +option. +.It Ev DESTDIR +Specify an alternate destination directory similar to the +.Fl D +option. +.It Ev EDITOR +Specify a program to edit merge conflicts. +.It Ev FREEBSD_ID +Ignore changes in the FreeBSD ID string similar to the +.Fl F +option. +This is enabled by setting the variable to a non-empty value. +.It Ev IGNORE_FILES +Ignore files that match any of the patterns listed in this variable +similar to the +.Fl I +option. +.It Ev LOGFILE +Specify an alternate path for the log file similar to the +.Fl L +option. +.It Ev MAKE_OPTIONS +Pass additional options to +.Xr make 1 +when building a +.Dq current +tree similar to the +.Fl M +option. +.It Ev SRCDIR +Specify an alternate source tree similar to the +.Fl s +option. +.It Ev WORKDIR +Specify an alternate work directory similar to the +.Fl d +option. +.El +.Sh ENVIRONMENT +The +.Nm +utility uses the program identified in the +.Ev EDITOR +environment variable to edit merge conflicts. +If +.Ev EDITOR +is not set, +.Xr vi 1 +is used as the default editor. +.Sh FILES +.Bl -tag -width ".Pa /var/db/etcupdate/log" -compact +.It Pa /etc/etcupdate.conf +Optional config file. +.It Pa /var/db/etcupdate +Default work directory used to store trees and other data. +.It Pa /var/db/etcupdate/log +Default log file. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +If the source tree matches the currently installed world, +then the following can be used to bootstrap +.Nm +so that it can be used for future upgrades: +.Pp +.Dl "etcupdate extract" +.Pp +To merge changes after an upgrade via the buildworld and installworld process: +.Pp +.Dl "etcupdate" +.Pp +To resolve any conflicts generated during a merge: +.Pp +.Dl "etcupdate resolve" +.Sh DIAGNOSTICS +The following warning messages may be generated during a merge. +Note that several of these warnings cover obscure cases that should occur +rarely if at all in practice. +For example, +if a file changes from a file to a directory in the +.Dq current +tree +and the file was modified in the destination directory, +then a warning will be triggered. +In general, +when a warning references a pathname, +the corresponding file in the destination directory is not changed by a +merge operation. +.Bl -diag +.It "Directory mismatch: ()" +An attempt was made to create a directory at +.Pa path +but an existing file of type +.Dq type +already exists for that path name. +.It "Modified link changed: ( became )" +The target of a symbolic link named +.Pa file +was changed from +.Dq old +to +.Dq new +in the +.Dq current +tree. +The symbolic link has been modified to point to a target that is neither +.Dq old +nor +.Dq new +in the destination directory. +.It "Modified mismatch: ( vs )" +A file named +.Pa file +of type +.Dq new +was modified in the +.Dq current +tree, +but the file exists as a different type +.Dq dest +in the destination directory. +.It "Modified changed: ( became )" +A file named +.Pa file +changed type from +.Dq old +in the +.Dq previous +tree to type +.Dq new +in the +.Dq current +tree. +The file in the destination directory of type +.Dq type +has been modified, +so it could not be merged automatically. +.It "Modified remains: " +The file of type +.Dq type +named +.Pa file +has been removed from the +.Dq current +tree, +but it has been locally modified. +The modified version of the file remains in the destination directory. +.It "Needs update: /etc/mail/aliases.db (required manual update via newaliases(1))" +The file +.Pa /etc/mail/aliases +was updated during a merge with a non-empty destination directory. +Due to a limitation of the +.Xr newaliases 1 +command, +.Nm +was not able to automatically update the corresponding aliases database. +.It "New file mismatch: ( vs )" +A new file named +.Pa file +of type +.Dq new +has been added to the +.Dq current +tree. +A file of that name already exists in the destination directory, +but it is of a different type +.Dq dest . +.It "New link conflict: ( vs )" +A symbolic link named +.Pa file +has been added to the +.Dq current +tree that links to +.Dq new . +A symbolic link of the same name already exists in the destination +directory, +but it links to a different target +.Dq dest . +.It "Non-empty directory remains: " +The directory +.Pa file +was removed from the +.Dq current +tree, +but it contains additional files in the destination directory. +These additional files as well as the directory remain. +.It "Remove mismatch: ( became )" +A file named +.Pa file +changed from type +.Dq old +in the +.Dq previous +tree to type +.Dq new +in the +.Dq current +tree, +but it has been removed in the destination directory. +.It "Removed file changed: " +A file named +.Pa file +was modified in the +.Dq current +tree, +but it has been removed in the destination directory. +.It "Removed link changed: ( became )" +The target of a symbolic link named +.Pa file +was changed from +.Dq old +to +.Dq new +in the +.Dq current +tree, +but it has been removed in the destination directory. +.El +.Sh SEE ALSO +.Xr cap_mkdb 1 , +.Xr diff 1 , +.Xr make 1 , +.Xr newaliases 1 , +.Xr sh 1 , +.Xr pwd_mkdb 8 +.\".Sh HISTORY +.Sh AUTHORS +The +.Nm +utility was written by +.An John Baldwin Aq jhb@FreeBSD.org . +.Sh BUGS +Rerunning a merge does not automatically delete conflicts left over from a +previous merge. +Any conflicts must be resolved before the merge can be rerun. +It it is not clear if this is a feature or a bug. +.Pp +There is no way to easily automate conflict resolution for specific files. +For example, one can imagine a syntax along the lines of +.Pp +.Dl "etcupdate resolve tf /some/file" +.Pp +to resolve a specific conflict in an automated fashion. +.Pp +It might be nice to have something like a +.Sq revert +command to replace a locally modified version of a file with the stock +version of the file. +For example: +.Pp +.Dl "etcupdate revert /etc/mail/freebsd.cf" +.Pp +Bootstrapping +.Nm +often results in gratuitous diffs in +.Pa /etc/mail/*.cf +that cause conflicts in the first merge. +If an object tree that matches the source tree is present when bootstrapping, +then passing the +.Fl B +flag to the +.Cm extract +command can work around this. diff --git a/usr.sbin/etcupdate/etcupdate.sh b/usr.sbin/etcupdate/etcupdate.sh new file mode 100755 index 0000000000000..22168360d796b --- /dev/null +++ b/usr.sbin/etcupdate/etcupdate.sh @@ -0,0 +1,1675 @@ +#!/bin/sh +# +# Copyright (c) 2010 Advanced Computing Technologies LLC +# Written by: John H. Baldwin +# 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. +# +# $FreeBSD$ + +# This is a tool to manage updating files that are not updated as part +# of 'make installworld' such as files in /etc. Unlike other tools, +# this one is specifically tailored to assisting with mass upgrades. +# To that end it does not require user intervention while running. +# +# Theory of operation: +# +# The most reliable way to update changes to files that have local +# modifications is to perform a three-way merge between the original +# unmodified file, the new version of the file, and the modified file. +# This requires having all three versions of the file available when +# performing an update. +# +# To that end, etcupdate uses a strategy where the current unmodified +# tree is kept in WORKDIR/current and the previous unmodified tree is +# kept in WORKDIR/old. When performing a merge, a new tree is built +# if needed and then the changes are merged into DESTDIR. Any files +# with unresolved conflicts after the merge are left in a tree rooted +# at WORKDIR/conflicts. +# +# To provide extra flexibility, etcupdate can also build tarballs of +# root trees that can later be used. It can also use a tarball as the +# source of a new tree instead of building it from /usr/src. + +# Global settings. These can be adjusted by config files and in some +# cases by command line options. + +# TODO: +# - automatable conflict resolution +# - a 'revert' command to make a file "stock" + +usage() +{ + cat < + etcupdate diff [-d workdir] [-D destdir] [-I patterns] [-L logfile] + etcupdate extract [-B] [-d workdir] [-s source | -t tarball] [-L logfile] + [-M options] + etcupdate resolve [-d workdir] [-D destdir] [-L logfile] + etcupdate status [-d workdir] [-D destdir] +EOF + exit 1 +} + +# Used to write a message prepended with '>>>' to the logfile. +log() +{ + echo ">>>" "$@" >&3 +} + +# Used for assertion conditions that should never happen. +panic() +{ + echo "PANIC:" "$@" + exit 10 +} + +# Used to write a warning message. These are saved to the WARNINGS +# file with " " prepended. +warn() +{ + echo -n " " >> $WARNINGS + echo "$@" >> $WARNINGS +} + +# Output a horizontal rule using the passed-in character. Matches the +# length used for Index lines in CVS and SVN diffs. +# +# $1 - character +rule() +{ + jot -b "$1" -s "" 67 +} + +# Output a text description of a specified file's type. +# +# $1 - file pathname. +file_type() +{ + stat -f "%HT" $1 | tr "[:upper:]" "[:lower:]" +} + +# Returns true (0) if a file exists +# +# $1 - file pathname. +exists() +{ + [ -e $1 -o -L $1 ] +} + +# Returns true (0) if a file should be ignored, false otherwise. +# +# $1 - file pathname +ignore() +{ + local pattern - + + set -o noglob + for pattern in $IGNORE_FILES; do + set +o noglob + case $1 in + $pattern) + return 0 + ;; + esac + set -o noglob + done + + # Ignore /.cshrc and /.profile if they are hardlinked to the + # same file in /root. This ensures we only compare those + # files once in that case. + case $1 in + /.cshrc|/.profile) + if [ ${DESTDIR}$1 -ef ${DESTDIR}/root$1 ]; then + return 0 + fi + ;; + *) + ;; + esac + + return 1 +} + +# Returns true (0) if the new version of a file should always be +# installed rather than attempting to do a merge. +# +# $1 - file pathname +always_install() +{ + local pattern - + + set -o noglob + for pattern in $ALWAYS_INSTALL; do + set +o noglob + case $1 in + $pattern) + return 0 + ;; + esac + set -o noglob + done + + return 1 +} + +# Build a new tree +# +# $1 - directory to store new tree in +build_tree() +{ + local make + + make="make $MAKE_OPTIONS" + + log "Building tree at $1 with $make" + mkdir -p $1/usr/obj >&3 2>&1 + (cd $SRCDIR; $make DESTDIR=$1 distrib-dirs) >&3 2>&1 || return 1 + + if ! [ -n "$nobuild" ]; then + (cd $SRCDIR; \ + MAKEOBJDIRPREFIX=$1/usr/obj $make _obj SUBDIR_OVERRIDE=etc && + MAKEOBJDIRPREFIX=$1/usr/obj $make everything SUBDIR_OVERRIDE=etc && + MAKEOBJDIRPREFIX=$1/usr/obj $make DESTDIR=$1 distribution) \ + >&3 2>&1 || return 1 + else + (cd $SRCDIR; $make DESTDIR=$1 distribution) >&3 2>&1 || return 1 + fi + chflags -R noschg $1 >&3 2>&1 || return 1 + rm -rf $1/usr/obj >&3 2>&1 || return 1 + + # Purge auto-generated files. Only the source files need to + # be updated after which these files are regenerated. + rm -f $1/etc/*.db $1/etc/passwd >&3 2>&1 || return 1 + + # Remove empty files. These just clutter the output of 'diff'. + find $1 -type f -size 0 -delete >&3 2>&1 || return 1 + + # Trim empty directories. + find -d $1 -type d -empty -delete >&3 2>&1 || return 1 + return 0 +} + +# Generate a new NEWTREE tree. If tarball is set, then the tree is +# extracted from the tarball. Otherwise the tree is built from a +# source tree. +extract_tree() +{ + # If we have a tarball, extract that into the new directory. + if [ -n "$tarball" ]; then + if ! (mkdir -p $NEWTREE && tar xf $tarball -C $NEWTREE) \ + >&3 2>&1; then + echo "Failed to extract new tree." + remove_tree $NEWTREE + exit 1 + fi + else + if ! build_tree $NEWTREE; then + echo "Failed to build new tree." + remove_tree $NEWTREE + exit 1 + fi + fi +} + +# Forcefully remove a tree. Returns true (0) if the operation succeeds. +# +# $1 - path to tree +remove_tree() +{ + + rm -rf $1 >&3 2>&1 + if [ -e $1 ]; then + chflags -R noschg $1 >&3 2>&1 + rm -rf $1 >&3 2>&1 + fi + [ ! -e $1 ] +} + +# Return values for compare() +COMPARE_EQUAL=0 +COMPARE_ONLYFIRST=1 +COMPARE_ONLYSECOND=2 +COMPARE_DIFFTYPE=3 +COMPARE_DIFFLINKS=4 +COMPARE_DIFFFILES=5 + +# Compare two files/directories/symlinks. Note that this does not +# recurse into subdirectories. Instead, if two nodes are both +# directories, they are assumed to be equivalent. +# +# Returns true (0) if the nodes are identical. If only one of the two +# nodes are present, return one of the COMPARE_ONLY* constants. If +# the nodes are different, return one of the COMPARE_DIFF* constants +# to indicate the type of difference. +# +# $1 - first node +# $2 - second node +compare() +{ + local first second + + # If the first node doesn't exist, then check for the second + # node. Note that -e will fail for a symbolic link that + # points to a missing target. + if ! exists $1; then + if exists $2; then + return $COMPARE_ONLYSECOND + else + return $COMPARE_EQUAL + fi + elif ! exists $2; then + return $COMPARE_ONLYFIRST + fi + + # If the two nodes are different file types fail. + first=`stat -f "%Hp" $1` + second=`stat -f "%Hp" $2` + if [ "$first" != "$second" ]; then + return $COMPARE_DIFFTYPE + fi + + # If both are symlinks, compare the link values. + if [ -L $1 ]; then + first=`readlink $1` + second=`readlink $2` + if [ "$first" = "$second" ]; then + return $COMPARE_EQUAL + else + return $COMPARE_DIFFLINKS + fi + fi + + # If both are files, compare the file contents. + if [ -f $1 ]; then + if cmp -s $1 $2; then + return $COMPARE_EQUAL + else + return $COMPARE_DIFFFILES + fi + fi + + # As long as the two nodes are the same type of file, consider + # them equivalent. + return $COMPARE_EQUAL +} + +# Returns true (0) if the only difference between two regular files is a +# change in the FreeBSD ID string. +# +# $1 - path of first file +# $2 - path of second file +fbsdid_only() +{ + + diff -qI '\$FreeBSD.*\$' $1 $2 >/dev/null 2>&1 +} + +# This is a wrapper around compare that will return COMPARE_EQUAL if +# the only difference between two regular files is a change in the +# FreeBSD ID string. It only makes this adjustment if the -F flag has +# been specified. +# +# $1 - first node +# $2 - second node +compare_fbsdid() +{ + local cmp + + compare $1 $2 + cmp=$? + + if [ -n "$FREEBSD_ID" -a "$cmp" -eq $COMPARE_DIFFFILES ] && \ + fbsdid_only $1 $2; then + return $COMPARE_EQUAL + fi + + return $cmp +} + +# Returns true (0) if a directory is empty. +# +# $1 - pathname of the directory to check +empty_dir() +{ + local contents + + contents=`ls -A $1` + [ -z "$contents" ] +} + +# Returns true (0) if one directories contents are a subset of the +# other. This will recurse to handle subdirectories and compares +# individual files in the trees. Its purpose is to quiet spurious +# directory warnings for dryrun invocations. +# +# $1 - first directory (sub) +# $2 - second directory (super) +dir_subset() +{ + local contents file + + if ! [ -d $1 -a -d $2 ]; then + return 1 + fi + + # Ignore files that are present in the second directory but not + # in the first. + contents=`ls -A $1` + for file in $contents; do + if ! compare $1/$file $2/$file; then + return 1 + fi + + if [ -d $1/$file ]; then + if ! dir_subset $1/$file $2/$file; then + return 1 + fi + fi + done + return 0 +} + +# Returns true (0) if a directory in the destination tree is empty. +# If this is a dryrun, then this returns true as long as the contents +# of the directory are a subset of the contents in the old tree +# (meaning that the directory would be empty in a non-dryrun when this +# was invoked) to quiet spurious warnings. +# +# $1 - pathname of the directory to check relative to DESTDIR. +empty_destdir() +{ + + if [ -n "$dryrun" ]; then + dir_subset $DESTDIR/$1 $OLDTREE/$1 + return + fi + + empty_dir $DESTDIR/$1 +} + +# Output a diff of two directory entries with the same relative name +# in different trees. Note that as with compare(), this does not +# recurse into subdirectories. If the nodes are identical, nothing is +# output. +# +# $1 - first tree +# $2 - second tree +# $3 - node name +# $4 - label for first tree +# $5 - label for second tree +diffnode() +{ + local first second file old new diffargs + + if [ -n "$FREEBSD_ID" ]; then + diffargs="-I \\\$FreeBSD.*\\\$" + else + diffargs="" + fi + + compare_fbsdid $1/$3 $2/$3 + case $? in + $COMPARE_EQUAL) + ;; + $COMPARE_ONLYFIRST) + echo + echo "Removed: $3" + echo + ;; + $COMPARE_ONLYSECOND) + echo + echo "Added: $3" + echo + ;; + $COMPARE_DIFFTYPE) + first=`file_type $1/$3` + second=`file_type $2/$3` + echo + echo "Node changed from a $first to a $second: $3" + echo + ;; + $COMPARE_DIFFLINKS) + first=`readlink $1/$file` + second=`readlink $2/$file` + echo + echo "Link changed: $file" + rule "=" + echo "-$first" + echo "+$second" + echo + ;; + $COMPARE_DIFFFILES) + echo "Index: $3" + rule "=" + diff -u $diffargs -L "$3 ($4)" $1/$3 -L "$3 ($5)" $2/$3 + ;; + esac +} + +# Create missing parent directories of a node in a target tree +# preserving the owner, group, and permissions from a specified +# template tree. +# +# $1 - template tree +# $2 - target tree +# $3 - pathname of the node (relative to both trees) +install_dirs() +{ + local args dir + + dir=`dirname $3` + + # Nothing to do if the parent directory exists. This also + # catches the degenerate cases when the path is just a simple + # filename. + if [ -d ${2}$dir ]; then + return 0 + fi + + # If non-directory file exists with the desired directory + # name, then fail. + if exists ${2}$dir; then + # If this is a dryrun and we are installing the + # directory in the DESTDIR and the file in the DESTDIR + # matches the file in the old tree, then fake success + # to quiet spurious warnings. + if [ -n "$dryrun" -a "$2" = "$DESTDIR" ]; then + if compare $OLDTREE/$dir $DESTDIR/$dir; then + return 0 + fi + fi + + args=`file_type ${2}$dir` + warn "Directory mismatch: ${2}$dir ($args)" + return 1 + fi + + # Ensure the parent directory of the directory is present + # first. + if ! install_dirs $1 "$2" $dir; then + return 1 + fi + + # Format attributes from template directory as install(1) + # arguments. + args=`stat -f "-o %Su -g %Sg -m %0Mp%0Lp" $1/$dir` + + log "install -d $args ${2}$dir" + if [ -z "$dryrun" ]; then + install -d $args ${2}$dir >&3 2>&1 + fi + return 0 +} + +# Perform post-install fixups for a file. This largely consists of +# regenerating any files that depend on the newly installed file. +# +# $1 - pathname of the updated file (relative to DESTDIR) +post_install_file() +{ + case $1 in + /etc/mail/aliases) + # Grr, newaliases only works for an empty DESTDIR. + if [ -z "$DESTDIR" ]; then + log "newaliases" + if [ -z "$dryrun" ]; then + newaliases >&3 2>&1 + fi + else + NEWALIAS_WARN=yes + fi + ;; + /etc/login.conf) + log "cap_mkdb ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + cap_mkdb ${DESTDIR}$1 >&3 2>&1 + fi + ;; + /etc/master.passwd) + log "pwd_mkdb -p -d $DESTDIR/etc ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + pwd_mkdb -p -d $DESTDIR/etc ${DESTDIR}$1 \ + >&3 2>&1 + fi + ;; + /etc/motd) + # /etc/rc.d/motd hardcodes the /etc/motd path. + # Don't warn about non-empty DESTDIR's since this + # change is only cosmetic anyway. + if [ -z "$DESTDIR" ]; then + log "sh /etc/rc.d/motd start" + if [ -z "$dryrun" ]; then + sh /etc/rc.d/motd start >&3 2>&1 + fi + fi + ;; + esac +} + +# Install the "new" version of a file. Returns true if it succeeds +# and false otherwise. +# +# $1 - pathname of the file to install (relative to DESTDIR) +install_new() +{ + + if ! install_dirs $NEWTREE "$DESTDIR" $1; then + return 1 + fi + log "cp -Rp ${NEWTREE}$1 ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + cp -Rp ${NEWTREE}$1 ${DESTDIR}$1 >&3 2>&1 + fi + post_install_file $1 + return 0 +} + +# Install the "resolved" version of a file. Returns true if it succeeds +# and false otherwise. +# +# $1 - pathname of the file to install (relative to DESTDIR) +install_resolved() +{ + + # This should always be present since the file is already + # there (it caused a conflict). However, it doesn't hurt to + # just be safe. + if ! install_dirs $NEWTREE "$DESTDIR" $1; then + return 1 + fi + + log "cp -Rp ${CONFLICTS}$1 ${DESTDIR}$1" + cp -Rp ${CONFLICTS}$1 ${DESTDIR}$1 >&3 2>&1 + post_install_file $1 + return 0 +} + +# Generate a conflict file when a "new" file conflicts with an +# existing file in DESTDIR. +# +# $1 - pathname of the file that conflicts (relative to DESTDIR) +new_conflict() +{ + + if [ -n "$dryrun" ]; then + return + fi + + install_dirs $NEWTREE $CONFLICTS $1 + diff --changed-group-format='<<<<<<< (local) +%<======= +%>>>>>>>> (stock) +' $DESTDIR/$1 $NEWTREE/$1 > $CONFLICTS/$1 +} + +# Remove the "old" version of a file. +# +# $1 - pathname of the old file to remove (relative to DESTDIR) +remove_old() +{ + log "rm -f ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + rm -f ${DESTDIR}$1 >&3 2>&1 + fi + echo " D $1" +} + +# Update a file that has no local modifications. +# +# $1 - pathname of the file to update (relative to DESTDIR) +update_unmodified() +{ + local new old + + # If the old file is a directory, then remove it with rmdir + # (this should only happen if the file has changed its type + # from a directory to a non-directory). If the directory + # isn't empty, then fail. This will be reported as a warning + # later. + if [ -d $DESTDIR/$1 ]; then + if empty_destdir $1; then + log "rmdir ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + rmdir ${DESTDIR}$1 >&3 2>&1 + fi + else + return 1 + fi + + # If both the old and new files are regular files, leave the + # existing file. This avoids breaking hard links for /.cshrc + # and /.profile. Otherwise, explicitly remove the old file. + elif ! [ -f ${DESTDIR}$1 -a -f ${NEWTREE}$1 ]; then + log "rm -f ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + rm -f ${DESTDIR}$1 >&3 2>&1 + fi + fi + + # If the new file is a directory, note that the old file has + # been removed, but don't do anything else for now. The + # directory will be installed if needed when new files within + # that directory are installed. + if [ -d $NEWTREE/$1 ]; then + if empty_dir $NEWTREE/$1; then + echo " D $file" + else + echo " U $file" + fi + elif install_new $1; then + echo " U $file" + fi + return 0 +} + +# Update the FreeBSD ID string in a locally modified file to match the +# FreeBSD ID string from the "new" version of the file. +# +# $1 - pathname of the file to update (relative to DESTDIR) +update_freebsdid() +{ + local new dest file + + # If the FreeBSD ID string is removed from the local file, + # there is nothing to do. In this case, treat the file as + # updated. Otherwise, if either file has more than one + # FreeBSD ID string, just punt and let the user handle the + # conflict manually. + new=`grep -c '\$FreeBSD.*\$' ${NEWTREE}$1` + dest=`grep -c '\$FreeBSD.*\$' ${DESTDIR}$1` + if [ "$dest" -eq 0 ]; then + return 0 + fi + if [ "$dest" -ne 1 -o "$dest" -ne 1 ]; then + return 1 + fi + + # If the FreeBSD ID string in the new file matches the FreeBSD ID + # string in the local file, there is nothing to do. + new=`grep '\$FreeBSD.*\$' ${NEWTREE}$1` + dest=`grep '\$FreeBSD.*\$' ${DESTDIR}$1` + if [ "$new" = "$dest" ]; then + return 0 + fi + + # Build the new file in three passes. First, copy all the + # lines preceding the FreeBSD ID string from the local version + # of the file. Second, append the FreeBSD ID string line from + # the new version. Finally, append all the lines after the + # FreeBSD ID string from the local version of the file. + file=`mktemp $WORKDIR/etcupdate-XXXXXXX` + awk '/\$FreeBSD.*\$/ { exit } { print }' ${DESTDIR}$1 >> $file + awk '/\$FreeBSD.*\$/ { print }' ${NEWTREE}$1 >> $file + awk '/\$FreeBSD.*\$/ { ok = 1; next } { if (ok) print }' \ + ${DESTDIR}$1 >> $file + + # As an extra sanity check, fail the attempt if the updated + # version of the file has any differences aside from the + # FreeBSD ID string. + if ! fbsdid_only ${DESTDIR}$1 $file; then + rm -f $file + return 1 + fi + + log "cp $file ${DESTDIR}$1" + if [ -z "$dryrun" ]; then + cp $file ${DESTDIR}$1 >&3 2>&1 + fi + rm -f $file + post_install_file $1 + echo " M $1" + return 0 +} + +# Attempt to update a file that has local modifications. This routine +# only handles regular files. If the 3-way merge succeeds without +# conflicts, the updated file is installed. If the merge fails, the +# merged version with conflict markers is left in the CONFLICTS tree. +# +# $1 - pathname of the file to merge (relative to DESTDIR) +merge_file() +{ + local res + + # Try the merge to see if there is a conflict. + merge -q -p ${DESTDIR}$1 ${OLDTREE}$1 ${NEWTREE}$1 >/dev/null 2>&3 + res=$? + case $res in + 0) + # No conflicts, so just redo the merge to the + # real file. + log "merge ${DESTDIR}$1 ${OLDTREE}$1 ${NEWTREE}$1" + if [ -z "$dryrun" ]; then + merge ${DESTDIR}$1 ${OLDTREE}$1 ${NEWTREE}$1 + fi + post_install_file $1 + echo " M $1" + ;; + 1) + # Conflicts, save a version with conflict markers in + # the conflicts directory. + if [ -z "$dryrun" ]; then + install_dirs $NEWTREE $CONFLICTS $1 + log "cp -Rp ${DESTDIR}$1 ${CONFLICTS}$1" + cp -Rp ${DESTDIR}$1 ${CONFLICTS}$1 >&3 2>&1 + merge -A -q -L "yours" -L "original" -L "new" \ + ${CONFLICTS}$1 ${OLDTREE}$1 ${NEWTREE}$1 + fi + echo " C $1" + ;; + *) + panic "merge failed with status $res" + ;; + esac +} + +# Returns true if a file contains conflict markers from a merge conflict. +# +# $1 - pathname of the file to resolve (relative to DESTDIR) +has_conflicts() +{ + + egrep -q '^(<{7}|\|{7}|={7}|>{7}) ' $CONFLICTS/$1 +} + +# Attempt to resolve a conflict. The user is prompted to choose an +# action for each conflict. If the user edits the file, they are +# prompted again for an action. The process is very similar to +# resolving conflicts after an update or merge with Perforce or +# Subversion. The prompts are modelled on a subset of the available +# commands for resolving conflicts with Subversion. +# +# $1 - pathname of the file to resolve (relative to DESTDIR) +resolve_conflict() +{ + local command junk + + echo "Resolving conflict in '$1':" + edit= + while true; do + # Only display the resolved command if the file + # doesn't contain any conflicts. + echo -n "Select: (p) postpone, (df) diff-full, (e) edit," + if ! has_conflicts $1; then + echo -n " (r) resolved," + fi + echo + echo -n " (h) help for more options: " + read command + case $command in + df) + diff -u ${DESTDIR}$1 ${CONFLICTS}$1 + ;; + e) + $EDITOR ${CONFLICTS}$1 + ;; + h) + cat </dev/null 2>&1 + fi + echo " D $dir" + else + warn "Non-empty directory remains: $dir" + fi + fi +} + +# Handle a file that exists in both the old and new trees. If the +# file has not changed in the old and new trees, there is nothing to +# do. If the file in the destination directory matches the new file, +# there is nothing to do. If the file in the destination directory +# matches the old file, then the new file should be installed. +# Everything else becomes some sort of conflict with more detailed +# handling. +# +# $1 - pathname of the file (relative to DESTDIR) +handle_modified_file() +{ + local cmp dest file new newdestcmp old + + file=$1 + if ignore $file; then + log "IGNORE: modified file $file" + return + fi + + compare $OLDTREE/$file $NEWTREE/$file + cmp=$? + if [ $cmp -eq $COMPARE_EQUAL ]; then + return + fi + + if [ $cmp -eq $COMPARE_ONLYFIRST -o $cmp -eq $COMPARE_ONLYSECOND ]; then + panic "Changed file now missing" + fi + + compare $NEWTREE/$file $DESTDIR/$file + newdestcmp=$? + if [ $newdestcmp -eq $COMPARE_EQUAL ]; then + return + fi + + # If the only change in the new file versus the destination + # file is a change in the FreeBSD ID string and -F is + # specified, just install the new file. + if [ -n "$FREEBSD_ID" -a $newdestcmp -eq $COMPARE_DIFFFILES ] && \ + fbsdid_only $NEWTREE/$file $DESTDIR/$file; then + if update_unmodified $file; then + return + else + panic "Updating FreeBSD ID string failed" + fi + fi + + # If the local file is the same as the old file, install the + # new file. If -F is specified and the only local change is + # in the FreeBSD ID string, then install the new file as well. + if compare_fbsdid $OLDTREE/$file $DESTDIR/$file; then + if update_unmodified $file; then + return + fi + fi + + # If the only change in the new file versus the old file is a + # change in the FreeBSD ID string and -F is specified, just + # update the FreeBSD ID string in the local file. + if [ -n "$FREEBSD_ID" -a $cmp -eq $COMPARE_DIFFFILES ] && \ + fbsdid_only $OLDTREE/$file $NEWTREE/$file; then + if update_freebsdid $file; then + continue + fi + fi + + # If the file was removed from the dest tree, just whine. + if [ $newdestcmp -eq $COMPARE_ONLYFIRST ]; then + # If the removed file matches an ALWAYS_INSTALL glob, + # then just install the new version of the file. + if always_install $file; then + log "ALWAYS: adding $file" + if ! [ -d $NEWTREE/$file ]; then + if install_new $file; then + echo " A $file" + fi + fi + return + fi + + case $cmp in + $COMPARE_DIFFTYPE) + old=`file_type $OLDTREE/$file` + new=`file_type $NEWTREE/$file` + warn "Remove mismatch: $file ($old became $new)" + ;; + $COMPARE_DIFFLINKS) + old=`readlink $OLDTREE/$file` + new=`readlink $NEWTREE/$file` + warn \ + "Removed link changed: $file (\"$old\" became \"$new\")" + ;; + $COMPARE_DIFFFILES) + warn "Removed file changed: $file" + ;; + esac + return + fi + + # Treat the file as unmodified and force install of the new + # file if it matches an ALWAYS_INSTALL glob. If the update + # attempt fails, then fall through to the normal case so a + # warning is generated. + if always_install $file; then + log "ALWAYS: updating $file" + if update_unmodified $file; then + return + fi + fi + + # If the file changed types between the old and new trees but + # the files in the new and dest tree are both of the same + # type, treat it like an added file just comparing the new and + # dest files. + if [ $cmp -eq $COMPARE_DIFFTYPE ]; then + case $newdestcmp in + $COMPARE_DIFFLINKS) + new=`readlink $NEWTREE/$file` + dest=`readlink $DESTDIR/$file` + warn \ + "New link conflict: $file (\"$new\" vs \"$dest\")" + return + ;; + $COMPARE_DIFFFILES) + new_conflict $file + echo " C $file" + return + ;; + esac + else + # If the file has not changed types between the old + # and new trees, but it is a different type in + # DESTDIR, then just warn. + if [ $newdestcmp -eq $COMPARE_DIFFTYPE ]; then + new=`file_type $NEWTREE/$file` + dest=`file_type $DESTDIR/$file` + warn "Modified mismatch: $file ($new vs $dest)" + return + fi + fi + + case $cmp in + $COMPARE_DIFFTYPE) + old=`file_type $OLDTREE/$file` + new=`file_type $NEWTREE/$file` + dest=`file_type $DESTDIR/$file` + warn "Modified $dest changed: $file ($old became $new)" + ;; + $COMPARE_DIFFLINKS) + old=`readlink $OLDTREE/$file` + new=`readlink $NEWTREE/$file` + warn \ + "Modified link changed: $file (\"$old\" became \"$new\")" + ;; + $COMPARE_DIFFFILES) + merge_file $file + ;; + esac +} + +# Handle a file that has been added in the new tree. If the file does +# not exist in DESTDIR, simply copy the file into DESTDIR. If the +# file exists in the DESTDIR and is identical to the new version, do +# nothing. Otherwise, generate a diff of the two versions of the file +# and mark it as a conflict. +# +# $1 - pathname of the file (relative to DESTDIR) +handle_added_file() +{ + local cmp dest file new + + file=$1 + if ignore $file; then + log "IGNORE: added file $file" + return + fi + + compare $DESTDIR/$file $NEWTREE/$file + cmp=$? + case $cmp in + $COMPARE_EQUAL) + return + ;; + $COMPARE_ONLYFIRST) + panic "Added file now missing" + ;; + $COMPARE_ONLYSECOND) + # Ignore new directories. They will be + # created as needed when non-directory nodes + # are installed. + if ! [ -d $NEWTREE/$file ]; then + if install_new $file; then + echo " A $file" + fi + fi + return + ;; + esac + + + # Treat the file as unmodified and force install of the new + # file if it matches an ALWAYS_INSTALL glob. If the update + # attempt fails, then fall through to the normal case so a + # warning is generated. + if always_install $file; then + log "ALWAYS: updating $file" + if update_unmodified $file; then + return + fi + fi + + case $cmp in + $COMPARE_DIFFTYPE) + new=`file_type $NEWTREE/$file` + dest=`file_type $DESTDIR/$file` + warn "New file mismatch: $file ($new vs $dest)" + ;; + $COMPARE_DIFFLINKS) + new=`readlink $NEWTREE/$file` + dest=`readlink $DESTDIR/$file` + warn "New link conflict: $file (\"$new\" vs \"$dest\")" + ;; + $COMPARE_DIFFFILES) + # If the only change in the new file versus + # the destination file is a change in the + # FreeBSD ID string and -F is specified, just + # install the new file. + if [ -n "$FREEBSD_ID" ] && \ + fbsdid_only $NEWTREE/$file $DESTDIR/$file; then + if update_unmodified $file; then + return + else + panic \ + "Updating FreeBSD ID string failed" + fi + fi + + new_conflict $file + echo " C $file" + ;; + esac +} + +# Main routines for each command + +# Build a new tree and save it in a tarball. +build_cmd() +{ + local dir + + if [ $# -ne 1 ]; then + echo "Missing required tarball." + echo + usage + fi + + log "build command: $1" + + # Create a temporary directory to hold the tree + dir=`mktemp -d $WORKDIR/etcupdate-XXXXXXX` + if [ $? -ne 0 ]; then + echo "Unable to create temporary directory." + exit 1 + fi + if ! build_tree $dir; then + echo "Failed to build tree." + remove_tree $dir + exit 1 + fi + if ! tar cfj $1 -C $dir . >&3 2>&1; then + echo "Failed to create tarball." + remove_tree $dir + exit 1 + fi + remove_tree $dir +} + +# Output a diff comparing the tree at DESTDIR to the current +# unmodified tree. Note that this diff does not include files that +# are present in DESTDIR but not in the unmodified tree. +diff_cmd() +{ + local file + + if [ $# -ne 0 ]; then + usage + fi + + # Requires an unmodified tree to diff against. + if ! [ -d $NEWTREE ]; then + echo "Reference tree to diff against unavailable." + exit 1 + fi + + # Unfortunately, diff alone does not quite provide the right + # level of options that we want, so improvise. + for file in `(cd $NEWTREE; find .) | sed -e 's/^\.//'`; do + if ignore $file; then + continue + fi + + diffnode $NEWTREE "$DESTDIR" $file "stock" "local" + done +} + +# Just extract a new tree into NEWTREE either by building a tree or +# extracting a tarball. This can be used to bootstrap updates by +# initializing the current "stock" tree to match the currently +# installed system. +# +# Unlike 'update', this command does not rotate or preserve an +# existing NEWTREE, it just replaces any existing tree. +extract_cmd() +{ + + if [ $# -ne 0 ]; then + usage + fi + + log "extract command: tarball=$tarball" + + if [ -d $NEWTREE ]; then + if ! remove_tree $NEWTREE; then + echo "Unable to remove current tree." + exit 1 + fi + fi + + extract_tree +} + +# Resolve conflicts left from an earlier merge. +resolve_cmd() +{ + local conflicts + + if [ $# -ne 0 ]; then + usage + fi + + if ! [ -d $CONFLICTS ]; then + return + fi + + conflicts=`(cd $CONFLICTS; find . ! -type d) | sed -e 's/^\.//'` + for file in $conflicts; do + resolve_conflict $file + done + + if [ -n "$NEWALIAS_WARN" ]; then + warn "Needs update: /etc/mail/aliases.db" \ + "(requires manual update via newaliases(1))" + echo + echo "Warnings:" + echo " Needs update: /etc/mail/aliases.db" \ + "(requires manual update via newaliases(1))" + fi +} + +# Report a summary of the previous merge. Specifically, list any +# remaining conflicts followed by any warnings from the previous +# update. +status_cmd() +{ + + if [ $# -ne 0 ]; then + usage + fi + + if [ -d $CONFLICTS ]; then + (cd $CONFLICTS; find . ! -type d) | sed -e 's/^\./ C /' + fi + if [ -s $WARNINGS ]; then + echo "Warnings:" + cat $WARNINGS + fi +} + +# Perform an actual merge. The new tree can either already exist (if +# rerunning a merge), be extracted from a tarball, or generated from a +# source tree. +update_cmd() +{ + local dir + + if [ $# -ne 0 ]; then + usage + fi + + log "update command: rerun=$rerun tarball=$tarball" + + if [ `id -u` -ne 0 ]; then + echo "Must be root to update a tree." + exit 1 + fi + + # Enforce a sane umask + umask 022 + + # XXX: Should existing conflicts be ignored and removed during + # a rerun? + + # Trim the conflicts tree. Whine if there is anything left. + if [ -e $CONFLICTS ]; then + find -d $CONFLICTS -type d -empty -delete >&3 2>&1 + rmdir $CONFLICTS >&3 2>&1 + fi + if [ -d $CONFLICTS ]; then + echo "Conflicts remain from previous update, aborting." + exit 1 + fi + + if [ -z "$rerun" ]; then + # For a dryrun that is not a rerun, do not rotate the existing + # stock tree. Instead, extract a tree to a temporary directory + # and use that for the comparison. + if [ -n "$dryrun" ]; then + dir=`mktemp -d $WORKDIR/etcupdate-XXXXXXX` + if [ $? -ne 0 ]; then + echo "Unable to create temporary directory." + exit 1 + fi + OLDTREE=$NEWTREE + NEWTREE=$dir + + # Rotate the existing stock tree to the old tree. + elif [ -d $NEWTREE ]; then + # First, delete the previous old tree if it exists. + if ! remove_tree $OLDTREE; then + echo "Unable to remove old tree." + exit 1 + fi + + # Move the current stock tree. + if ! mv $NEWTREE $OLDTREE >&3 2>&1; then + echo "Unable to rename current stock tree." + exit 1 + fi + fi + + if ! [ -d $OLDTREE ]; then + cat < $WORKDIR/old.files + (cd $NEWTREE; find .) | sed -e 's/^\.//' | sort > $WORKDIR/new.files + + # Split the files up into three groups using comm. + comm -23 $WORKDIR/old.files $WORKDIR/new.files > $WORKDIR/removed.files + comm -13 $WORKDIR/old.files $WORKDIR/new.files > $WORKDIR/added.files + comm -12 $WORKDIR/old.files $WORKDIR/new.files > $WORKDIR/both.files + + # Initialize conflicts and warnings handling. + rm -f $WARNINGS + mkdir -p $CONFLICTS + + # The order for the following sections is important. In the + # odd case that a directory is converted into a file, the + # existing subfiles need to be removed if possible before the + # file is converted. Similarly, in the case that a file is + # converted into a directory, the file needs to be converted + # into a directory if possible before the new files are added. + + # First, handle removed files. + for file in `cat $WORKDIR/removed.files`; do + handle_removed_file $file + done + + # For the directory pass, reverse sort the list to effect a + # depth-first traversal. This is needed to ensure that if a + # directory with subdirectories is removed, the entire + # directory is removed if there are no local modifications. + for file in `sort -r $WORKDIR/removed.files`; do + handle_removed_directory $file + done + + # Second, handle files that exist in both the old and new + # trees. + for file in `cat $WORKDIR/both.files`; do + handle_modified_file $file + done + + # Finally, handle newly added files. + for file in `cat $WORKDIR/added.files`; do + handle_added_file $file + done + + if [ -n "$NEWALIAS_WARN" ]; then + warn "Needs update: /etc/mail/aliases.db" \ + "(requires manual update via newaliases(1))" + fi + + if [ -s $WARNINGS ]; then + echo "Warnings:" + cat $WARNINGS + fi + + if [ -n "$dir" ]; then + if [ -z "$dryrun" -o -n "$rerun" ]; then + panic "Should not have a temporary directory" + fi + + remove_tree $dir + fi +} + +# Determine which command we are executing. A command may be +# specified as the first word. If one is not specified then 'update' +# is assumed as the default command. +command="update" +if [ $# -gt 0 ]; then + case "$1" in + build|diff|extract|status|resolve) + command="$1" + shift + ;; + -*) + # If first arg is an option, assume the + # default command. + ;; + *) + usage + ;; + esac +fi + +# Set default variable values. + +# The path to the source tree used to build trees. +SRCDIR=/usr/src + +# The destination directory where the modified files live. +DESTDIR= + +# Ignore changes in the FreeBSD ID string. +FREEBSD_ID= + +# Files that should always have the new version of the file installed. +ALWAYS_INSTALL= + +# Files to ignore and never update during a merge. +IGNORE_FILES= + +# Flags to pass to 'make' when building a tree. +MAKE_OPTIONS= + +# Include a config file if it exists. Note that command line options +# override any settings in the config file. More details are in the +# manual, but in general the following variables can be set: +# - ALWAYS_INSTALL +# - DESTDIR +# - EDITOR +# - FREEBSD_ID +# - IGNORE_FILES +# - LOGFILE +# - MAKE_OPTIONS +# - SRCDIR +# - WORKDIR +if [ -r /etc/etcupdate.conf ]; then + . /etc/etcupdate.conf +fi + +# Parse command line options +tarball= +rerun= +always= +dryrun= +ignore= +nobuild= +while getopts "d:nrs:t:A:BD:FI:L:M:" option; do + case "$option" in + d) + WORKDIR=$OPTARG + ;; + n) + dryrun=YES + ;; + r) + rerun=YES + ;; + s) + SRCDIR=$OPTARG + ;; + t) + tarball=$OPTARG + ;; + A) + # To allow this option to be specified + # multiple times, accumulate command-line + # specified patterns in an 'always' variable + # and use that to overwrite ALWAYS_INSTALL + # after parsing all options. Need to be + # careful here with globbing expansion. + set -o noglob + always="$always $OPTARG" + set +o noglob + ;; + B) + nobuild=YES + ;; + D) + DESTDIR=$OPTARG + ;; + F) + FREEBSD_ID=YES + ;; + I) + # To allow this option to be specified + # multiple times, accumulate command-line + # specified patterns in an 'ignore' variable + # and use that to overwrite IGNORE_FILES after + # parsing all options. Need to be careful + # here with globbing expansion. + set -o noglob + ignore="$ignore $OPTARG" + set +o noglob + ;; + L) + LOGFILE=$OPTARG + ;; + M) + MAKE_OPTIONS="$OPTARG" + ;; + *) + echo + usage + ;; + esac +done +shift $((OPTIND - 1)) + +# Allow -A command line options to override ALWAYS_INSTALL set from +# the config file. +set -o noglob +if [ -n "$always" ]; then + ALWAYS_INSTALL="$always" +fi + +# Allow -I command line options to override IGNORE_FILES set from the +# config file. +if [ -n "$ignore" ]; then + IGNORE_FILES="$ignore" +fi +set +o noglob + +# Where the "old" and "new" trees are stored. +WORKDIR=${WORKDIR:-$DESTDIR/var/db/etcupdate} + +# Log file for verbose output from program that are run. The log file +# is opened on fd '3'. +LOGFILE=${LOGFILE:-$WORKDIR/log} + +# The path of the "old" tree +OLDTREE=$WORKDIR/old + +# The path of the "new" tree +NEWTREE=$WORKDIR/current + +# The path of the "conflicts" tree where files with merge conflicts are saved. +CONFLICTS=$WORKDIR/conflicts + +# The path of the "warnings" file that accumulates warning notes from an update. +WARNINGS=$WORKDIR/warnings + +# Use $EDITOR for resolving conflicts. If it is not set, default to vi. +EDITOR=${EDITOR:-/usr/bin/vi} + +# Handle command-specific argument processing such as complaining +# about unsupported options. Since the configuration file is always +# included, do not complain about extra command line arguments that +# may have been set via the config file rather than the command line. +case $command in + update) + if [ -n "$rerun" -a -n "$tarball" ]; then + echo "Only one of -r or -t can be specified." + echo + usage + fi + ;; + build|diff|resolve|status) + if [ -n "$dryrun" -o -n "$rerun" -o -n "$tarball" ]; then + usage + fi + ;; + extract) + if [ -n "$dryrun" -o -n "$rerun" ]; then + usage + fi + ;; +esac + +# Open the log file. Don't truncate it if doing a minor operation so +# that a minor operation doesn't lose log info from a major operation. +if ! mkdir -p $WORKDIR 2>/dev/null; then + echo "Failed to create work directory $WORKDIR" +fi + +case $command in + diff|resolve|status) + exec 3>>$LOGFILE + ;; + *) + exec 3>$LOGFILE + ;; +esac + +${command}_cmd "$@" -- cgit v1.3 From 9efc00598b0076f38811d6356218eafce988a4c0 Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Fri, 13 Jul 2012 15:25:10 +0000 Subject: Minor mdoc, spelling and end of line whitespace fixes. --- usr.sbin/etcupdate/etcupdate.8 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/etcupdate/etcupdate.8 b/usr.sbin/etcupdate/etcupdate.8 index a81ba86c26b62..34459294a764b 100644 --- a/usr.sbin/etcupdate/etcupdate.8 +++ b/usr.sbin/etcupdate/etcupdate.8 @@ -112,7 +112,6 @@ The utility will not perform a new merge until all conflicts from an earlier merge are resolved. .Sh MODES -.Pp The .Nm utility supports several modes of operation. @@ -150,7 +149,7 @@ tree. An older .Dq previous tree is removed if it exists. -By default the new +By default the new .Dq current tree is built from a source tree. However, @@ -349,7 +348,9 @@ Do not build generated files in a private object tree. Instead, reuse the generated files from a previously built object tree that matches the source tree. -This can be useful to avoid gratuitous conflicts in sendmail configuration +This can be useful to avoid gratuitous conflicts in +.Xr sendmail 8 +configuration files when bootstrapping. It can also be useful for building a tarball that matches a specific world build. @@ -379,7 +380,7 @@ variable or the option will not be installed. .It Fl D Ar destdir Specify an alternate destination directory as the target of a merge. -This is analagous to the +This is analogous to the .Dv DESTDIR variable used with .Sq make installworld . @@ -739,12 +740,11 @@ but it has been removed in the destination directory. .El .Sh SEE ALSO .Xr cap_mkdb 1 , -.Xr diff 1 , +.Xr diff 1 , .Xr make 1 , .Xr newaliases 1 , .Xr sh 1 , .Xr pwd_mkdb 8 -.\".Sh HISTORY .Sh AUTHORS The .Nm -- cgit v1.3 From cc59cb76224354b2595aaa47dde4d53372ddd246 Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Fri, 13 Jul 2012 21:27:18 +0000 Subject: Add HISTORY section. --- usr.sbin/etcupdate/etcupdate.8 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/etcupdate/etcupdate.8 b/usr.sbin/etcupdate/etcupdate.8 index 34459294a764b..a61f181536261 100644 --- a/usr.sbin/etcupdate/etcupdate.8 +++ b/usr.sbin/etcupdate/etcupdate.8 @@ -745,6 +745,11 @@ but it has been removed in the destination directory. .Xr newaliases 1 , .Xr sh 1 , .Xr pwd_mkdb 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 10.0 . .Sh AUTHORS The .Nm -- cgit v1.3 From 641a6cfb86023499caafe26a4d821a0b885cf00b Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 14 Jul 2012 03:16:57 +0000 Subject: Import bsdconfig(8) as a replacement for the post-install abilities of deprecated sysinstall(8). NOTE: WITH_BSDCONFIG is currently required. Submitted by: Devin Teske (dteske), Ron McDowell Reviewed by: Ron McDowell Approved by: Ed Maste (emaste) --- share/mk/bsd.own.mk | 1 + tools/build/options/WITH_BSDCONFIG | 2 + usr.sbin/Makefile | 4 + usr.sbin/bsdconfig/Makefile | 32 + usr.sbin/bsdconfig/USAGE | 40 + usr.sbin/bsdconfig/bsdconfig | 313 +++++ usr.sbin/bsdconfig/bsdconfig.8 | 240 ++++ usr.sbin/bsdconfig/console/INDEX | 59 + usr.sbin/bsdconfig/console/Makefile | 16 + usr.sbin/bsdconfig/console/USAGE | 33 + usr.sbin/bsdconfig/console/console | 137 ++ usr.sbin/bsdconfig/console/font | 205 +++ usr.sbin/bsdconfig/console/include/Makefile | 11 + usr.sbin/bsdconfig/console/include/messages.subr | 264 ++++ usr.sbin/bsdconfig/console/keymap | 365 +++++ usr.sbin/bsdconfig/console/repeat | 134 ++ usr.sbin/bsdconfig/console/saver | 198 +++ usr.sbin/bsdconfig/console/screenmap | 142 ++ usr.sbin/bsdconfig/console/ttys | 197 +++ usr.sbin/bsdconfig/diskmgmt/INDEX | 53 + usr.sbin/bsdconfig/diskmgmt/Makefile | 16 + usr.sbin/bsdconfig/diskmgmt/USAGE | 33 + usr.sbin/bsdconfig/diskmgmt/diskmgmt | 85 ++ usr.sbin/bsdconfig/diskmgmt/include/Makefile | 11 + usr.sbin/bsdconfig/diskmgmt/include/messages.subr | 27 + usr.sbin/bsdconfig/docsinstall/INDEX | 53 + usr.sbin/bsdconfig/docsinstall/Makefile | 16 + usr.sbin/bsdconfig/docsinstall/USAGE | 33 + usr.sbin/bsdconfig/docsinstall/docsinstall | 97 ++ usr.sbin/bsdconfig/docsinstall/include/Makefile | 11 + .../bsdconfig/docsinstall/include/messages.subr | 28 + usr.sbin/bsdconfig/dot/INDEX | 53 + usr.sbin/bsdconfig/dot/Makefile | 16 + usr.sbin/bsdconfig/dot/USAGE | 143 ++ usr.sbin/bsdconfig/dot/dot | 646 +++++++++ usr.sbin/bsdconfig/dot/include/Makefile | 11 + usr.sbin/bsdconfig/dot/include/messages.subr | 30 + usr.sbin/bsdconfig/examples/Makefile | 11 + usr.sbin/bsdconfig/examples/bsdconfigrc | 35 + usr.sbin/bsdconfig/include/Makefile | 12 + usr.sbin/bsdconfig/include/common.subr | 299 ++++ usr.sbin/bsdconfig/include/dialog.subr | 1441 ++++++++++++++++++++ usr.sbin/bsdconfig/include/messages.subr | 56 + usr.sbin/bsdconfig/include/mustberoot.subr | 360 +++++ usr.sbin/bsdconfig/include/strings.subr | 104 ++ usr.sbin/bsdconfig/include/sysrc.subr | 616 +++++++++ usr.sbin/bsdconfig/mouse/INDEX | 58 + usr.sbin/bsdconfig/mouse/Makefile | 16 + usr.sbin/bsdconfig/mouse/USAGE | 33 + usr.sbin/bsdconfig/mouse/disable | 104 ++ usr.sbin/bsdconfig/mouse/enable | 118 ++ usr.sbin/bsdconfig/mouse/flags | 116 ++ usr.sbin/bsdconfig/mouse/include/Makefile | 11 + usr.sbin/bsdconfig/mouse/include/messages.subr | 90 ++ usr.sbin/bsdconfig/mouse/mouse | 134 ++ usr.sbin/bsdconfig/mouse/port | 142 ++ usr.sbin/bsdconfig/mouse/type | 158 +++ usr.sbin/bsdconfig/networking/INDEX | 57 + usr.sbin/bsdconfig/networking/Makefile | 16 + usr.sbin/bsdconfig/networking/USAGE | 33 + usr.sbin/bsdconfig/networking/defaultrouter | 73 + usr.sbin/bsdconfig/networking/devices | 158 +++ usr.sbin/bsdconfig/networking/hostname | 73 + usr.sbin/bsdconfig/networking/include/Makefile | 12 + usr.sbin/bsdconfig/networking/include/common.subr | 65 + usr.sbin/bsdconfig/networking/include/device.subr | 489 +++++++ .../bsdconfig/networking/include/hostname.subr | 211 +++ usr.sbin/bsdconfig/networking/include/ipaddr.subr | 236 ++++ usr.sbin/bsdconfig/networking/include/media.subr | 235 ++++ .../bsdconfig/networking/include/messages.subr | 100 ++ usr.sbin/bsdconfig/networking/include/netmask.subr | 215 +++ usr.sbin/bsdconfig/networking/include/resolv.subr | 509 +++++++ usr.sbin/bsdconfig/networking/include/routing.subr | 168 +++ usr.sbin/bsdconfig/networking/nameservers | 73 + usr.sbin/bsdconfig/networking/networking | 146 ++ usr.sbin/bsdconfig/password/INDEX | 53 + usr.sbin/bsdconfig/password/Makefile | 16 + usr.sbin/bsdconfig/password/USAGE | 33 + usr.sbin/bsdconfig/password/include/Makefile | 11 + usr.sbin/bsdconfig/password/include/messages.subr | 35 + usr.sbin/bsdconfig/password/include/password.subr | 122 ++ usr.sbin/bsdconfig/password/password | 82 ++ usr.sbin/bsdconfig/security/INDEX | 54 + usr.sbin/bsdconfig/security/Makefile | 16 + usr.sbin/bsdconfig/security/USAGE | 33 + usr.sbin/bsdconfig/security/include/Makefile | 11 + usr.sbin/bsdconfig/security/include/messages.subr | 48 + usr.sbin/bsdconfig/security/kern_securelevel | 135 ++ usr.sbin/bsdconfig/security/security | 149 ++ usr.sbin/bsdconfig/startup/INDEX | 58 + usr.sbin/bsdconfig/startup/Makefile | 16 + usr.sbin/bsdconfig/startup/USAGE | 33 + usr.sbin/bsdconfig/startup/include/Makefile | 11 + usr.sbin/bsdconfig/startup/include/messages.subr | 112 ++ usr.sbin/bsdconfig/startup/include/rcconf.subr | 466 +++++++ usr.sbin/bsdconfig/startup/include/rcedit.subr | 106 ++ usr.sbin/bsdconfig/startup/include/rcvar.subr | 206 +++ usr.sbin/bsdconfig/startup/misc | 406 ++++++ usr.sbin/bsdconfig/startup/rcadd | 153 +++ usr.sbin/bsdconfig/startup/rcconf | 246 ++++ usr.sbin/bsdconfig/startup/rcdelete | 355 +++++ usr.sbin/bsdconfig/startup/rcedit | 69 + usr.sbin/bsdconfig/startup/rcvar | 200 +++ usr.sbin/bsdconfig/startup/startup | 128 ++ usr.sbin/bsdconfig/timezone/INDEX | 53 + usr.sbin/bsdconfig/timezone/Makefile | 16 + usr.sbin/bsdconfig/timezone/USAGE | 42 + usr.sbin/bsdconfig/timezone/include/Makefile | 12 + .../bsdconfig/timezone/include/continents.subr | 138 ++ usr.sbin/bsdconfig/timezone/include/countries.subr | 78 ++ usr.sbin/bsdconfig/timezone/include/iso3166.subr | 197 +++ usr.sbin/bsdconfig/timezone/include/menus.subr | 220 +++ usr.sbin/bsdconfig/timezone/include/messages.subr | 78 ++ usr.sbin/bsdconfig/timezone/include/zones.subr | 670 +++++++++ usr.sbin/bsdconfig/timezone/timezone | 448 ++++++ usr.sbin/bsdconfig/ttys/INDEX | 53 + usr.sbin/bsdconfig/ttys/Makefile | 16 + usr.sbin/bsdconfig/ttys/USAGE | 33 + usr.sbin/bsdconfig/ttys/include/Makefile | 11 + usr.sbin/bsdconfig/ttys/include/messages.subr | 31 + usr.sbin/bsdconfig/ttys/ttys | 128 ++ usr.sbin/bsdconfig/usermgmt/INDEX | 60 + usr.sbin/bsdconfig/usermgmt/Makefile | 17 + usr.sbin/bsdconfig/usermgmt/USAGE | 33 + usr.sbin/bsdconfig/usermgmt/groupadd | 64 + usr.sbin/bsdconfig/usermgmt/groupdel | 93 ++ usr.sbin/bsdconfig/usermgmt/groupedit | 93 ++ usr.sbin/bsdconfig/usermgmt/groupinput | 295 ++++ usr.sbin/bsdconfig/usermgmt/include/Makefile | 11 + .../bsdconfig/usermgmt/include/group_input.subr | 437 ++++++ usr.sbin/bsdconfig/usermgmt/include/messages.subr | 111 ++ .../bsdconfig/usermgmt/include/user_input.subr | 1180 ++++++++++++++++ usr.sbin/bsdconfig/usermgmt/useradd | 64 + usr.sbin/bsdconfig/usermgmt/userdel | 93 ++ usr.sbin/bsdconfig/usermgmt/useredit | 93 ++ usr.sbin/bsdconfig/usermgmt/userinput | 515 +++++++ usr.sbin/bsdconfig/usermgmt/usermgmt | 150 ++ 137 files changed, 19434 insertions(+) create mode 100644 tools/build/options/WITH_BSDCONFIG create mode 100644 usr.sbin/bsdconfig/Makefile create mode 100644 usr.sbin/bsdconfig/USAGE create mode 100755 usr.sbin/bsdconfig/bsdconfig create mode 100644 usr.sbin/bsdconfig/bsdconfig.8 create mode 100644 usr.sbin/bsdconfig/console/INDEX create mode 100644 usr.sbin/bsdconfig/console/Makefile create mode 100644 usr.sbin/bsdconfig/console/USAGE create mode 100755 usr.sbin/bsdconfig/console/console create mode 100755 usr.sbin/bsdconfig/console/font create mode 100644 usr.sbin/bsdconfig/console/include/Makefile create mode 100644 usr.sbin/bsdconfig/console/include/messages.subr create mode 100755 usr.sbin/bsdconfig/console/keymap create mode 100755 usr.sbin/bsdconfig/console/repeat create mode 100755 usr.sbin/bsdconfig/console/saver create mode 100755 usr.sbin/bsdconfig/console/screenmap create mode 100755 usr.sbin/bsdconfig/console/ttys create mode 100644 usr.sbin/bsdconfig/diskmgmt/INDEX create mode 100644 usr.sbin/bsdconfig/diskmgmt/Makefile create mode 100644 usr.sbin/bsdconfig/diskmgmt/USAGE create mode 100755 usr.sbin/bsdconfig/diskmgmt/diskmgmt create mode 100644 usr.sbin/bsdconfig/diskmgmt/include/Makefile create mode 100644 usr.sbin/bsdconfig/diskmgmt/include/messages.subr create mode 100644 usr.sbin/bsdconfig/docsinstall/INDEX create mode 100644 usr.sbin/bsdconfig/docsinstall/Makefile create mode 100644 usr.sbin/bsdconfig/docsinstall/USAGE create mode 100755 usr.sbin/bsdconfig/docsinstall/docsinstall create mode 100644 usr.sbin/bsdconfig/docsinstall/include/Makefile create mode 100644 usr.sbin/bsdconfig/docsinstall/include/messages.subr create mode 100644 usr.sbin/bsdconfig/dot/INDEX create mode 100644 usr.sbin/bsdconfig/dot/Makefile create mode 100644 usr.sbin/bsdconfig/dot/USAGE create mode 100755 usr.sbin/bsdconfig/dot/dot create mode 100644 usr.sbin/bsdconfig/dot/include/Makefile create mode 100644 usr.sbin/bsdconfig/dot/include/messages.subr create mode 100644 usr.sbin/bsdconfig/examples/Makefile create mode 100644 usr.sbin/bsdconfig/examples/bsdconfigrc create mode 100644 usr.sbin/bsdconfig/include/Makefile create mode 100644 usr.sbin/bsdconfig/include/common.subr create mode 100644 usr.sbin/bsdconfig/include/dialog.subr create mode 100644 usr.sbin/bsdconfig/include/messages.subr create mode 100644 usr.sbin/bsdconfig/include/mustberoot.subr create mode 100644 usr.sbin/bsdconfig/include/strings.subr create mode 100644 usr.sbin/bsdconfig/include/sysrc.subr create mode 100644 usr.sbin/bsdconfig/mouse/INDEX create mode 100644 usr.sbin/bsdconfig/mouse/Makefile create mode 100644 usr.sbin/bsdconfig/mouse/USAGE create mode 100755 usr.sbin/bsdconfig/mouse/disable create mode 100755 usr.sbin/bsdconfig/mouse/enable create mode 100755 usr.sbin/bsdconfig/mouse/flags create mode 100644 usr.sbin/bsdconfig/mouse/include/Makefile create mode 100644 usr.sbin/bsdconfig/mouse/include/messages.subr create mode 100755 usr.sbin/bsdconfig/mouse/mouse create mode 100755 usr.sbin/bsdconfig/mouse/port create mode 100755 usr.sbin/bsdconfig/mouse/type create mode 100644 usr.sbin/bsdconfig/networking/INDEX create mode 100644 usr.sbin/bsdconfig/networking/Makefile create mode 100644 usr.sbin/bsdconfig/networking/USAGE create mode 100755 usr.sbin/bsdconfig/networking/defaultrouter create mode 100755 usr.sbin/bsdconfig/networking/devices create mode 100755 usr.sbin/bsdconfig/networking/hostname create mode 100644 usr.sbin/bsdconfig/networking/include/Makefile create mode 100644 usr.sbin/bsdconfig/networking/include/common.subr create mode 100644 usr.sbin/bsdconfig/networking/include/device.subr create mode 100644 usr.sbin/bsdconfig/networking/include/hostname.subr create mode 100644 usr.sbin/bsdconfig/networking/include/ipaddr.subr create mode 100644 usr.sbin/bsdconfig/networking/include/media.subr create mode 100644 usr.sbin/bsdconfig/networking/include/messages.subr create mode 100644 usr.sbin/bsdconfig/networking/include/netmask.subr create mode 100644 usr.sbin/bsdconfig/networking/include/resolv.subr create mode 100644 usr.sbin/bsdconfig/networking/include/routing.subr create mode 100755 usr.sbin/bsdconfig/networking/nameservers create mode 100755 usr.sbin/bsdconfig/networking/networking create mode 100644 usr.sbin/bsdconfig/password/INDEX create mode 100644 usr.sbin/bsdconfig/password/Makefile create mode 100644 usr.sbin/bsdconfig/password/USAGE create mode 100644 usr.sbin/bsdconfig/password/include/Makefile create mode 100644 usr.sbin/bsdconfig/password/include/messages.subr create mode 100644 usr.sbin/bsdconfig/password/include/password.subr create mode 100755 usr.sbin/bsdconfig/password/password create mode 100644 usr.sbin/bsdconfig/security/INDEX create mode 100644 usr.sbin/bsdconfig/security/Makefile create mode 100644 usr.sbin/bsdconfig/security/USAGE create mode 100644 usr.sbin/bsdconfig/security/include/Makefile create mode 100644 usr.sbin/bsdconfig/security/include/messages.subr create mode 100755 usr.sbin/bsdconfig/security/kern_securelevel create mode 100755 usr.sbin/bsdconfig/security/security create mode 100644 usr.sbin/bsdconfig/startup/INDEX create mode 100644 usr.sbin/bsdconfig/startup/Makefile create mode 100644 usr.sbin/bsdconfig/startup/USAGE create mode 100644 usr.sbin/bsdconfig/startup/include/Makefile create mode 100644 usr.sbin/bsdconfig/startup/include/messages.subr create mode 100644 usr.sbin/bsdconfig/startup/include/rcconf.subr create mode 100644 usr.sbin/bsdconfig/startup/include/rcedit.subr create mode 100644 usr.sbin/bsdconfig/startup/include/rcvar.subr create mode 100755 usr.sbin/bsdconfig/startup/misc create mode 100755 usr.sbin/bsdconfig/startup/rcadd create mode 100755 usr.sbin/bsdconfig/startup/rcconf create mode 100755 usr.sbin/bsdconfig/startup/rcdelete create mode 100755 usr.sbin/bsdconfig/startup/rcedit create mode 100755 usr.sbin/bsdconfig/startup/rcvar create mode 100755 usr.sbin/bsdconfig/startup/startup create mode 100644 usr.sbin/bsdconfig/timezone/INDEX create mode 100644 usr.sbin/bsdconfig/timezone/Makefile create mode 100644 usr.sbin/bsdconfig/timezone/USAGE create mode 100644 usr.sbin/bsdconfig/timezone/include/Makefile create mode 100644 usr.sbin/bsdconfig/timezone/include/continents.subr create mode 100644 usr.sbin/bsdconfig/timezone/include/countries.subr create mode 100644 usr.sbin/bsdconfig/timezone/include/iso3166.subr create mode 100644 usr.sbin/bsdconfig/timezone/include/menus.subr create mode 100644 usr.sbin/bsdconfig/timezone/include/messages.subr create mode 100644 usr.sbin/bsdconfig/timezone/include/zones.subr create mode 100755 usr.sbin/bsdconfig/timezone/timezone create mode 100644 usr.sbin/bsdconfig/ttys/INDEX create mode 100644 usr.sbin/bsdconfig/ttys/Makefile create mode 100644 usr.sbin/bsdconfig/ttys/USAGE create mode 100644 usr.sbin/bsdconfig/ttys/include/Makefile create mode 100644 usr.sbin/bsdconfig/ttys/include/messages.subr create mode 100755 usr.sbin/bsdconfig/ttys/ttys create mode 100644 usr.sbin/bsdconfig/usermgmt/INDEX create mode 100644 usr.sbin/bsdconfig/usermgmt/Makefile create mode 100644 usr.sbin/bsdconfig/usermgmt/USAGE create mode 100755 usr.sbin/bsdconfig/usermgmt/groupadd create mode 100755 usr.sbin/bsdconfig/usermgmt/groupdel create mode 100755 usr.sbin/bsdconfig/usermgmt/groupedit create mode 100755 usr.sbin/bsdconfig/usermgmt/groupinput create mode 100644 usr.sbin/bsdconfig/usermgmt/include/Makefile create mode 100644 usr.sbin/bsdconfig/usermgmt/include/group_input.subr create mode 100644 usr.sbin/bsdconfig/usermgmt/include/messages.subr create mode 100644 usr.sbin/bsdconfig/usermgmt/include/user_input.subr create mode 100755 usr.sbin/bsdconfig/usermgmt/useradd create mode 100755 usr.sbin/bsdconfig/usermgmt/userdel create mode 100755 usr.sbin/bsdconfig/usermgmt/useredit create mode 100755 usr.sbin/bsdconfig/usermgmt/userinput create mode 100755 usr.sbin/bsdconfig/usermgmt/usermgmt (limited to 'usr.sbin') diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index ba4a896728419..e8e4b7117d2f7 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -421,6 +421,7 @@ __DEFAULT_NO_OPTIONS = \ BIND_LIBS \ BIND_SIGCHASE \ BIND_XML \ + BSDCONFIG \ CLANG_EXTRAS \ CLANG_IS_CC \ CTF \ diff --git a/tools/build/options/WITH_BSDCONFIG b/tools/build/options/WITH_BSDCONFIG new file mode 100644 index 0000000000000..3ac38b9bcc6a5 --- /dev/null +++ b/tools/build/options/WITH_BSDCONFIG @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Install bsdconfig(8), a BSD-licensed configuration/management utility. diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 7df7e7d3a9dcb..95eb7b8f0d736 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -144,6 +144,10 @@ SUBDIR+= rndc-confgen SUBDIR+= bluetooth .endif +.if ${MK_BSDCONFIG} != "no" +SUBDIR+= bsdconfig +.endif + .if ${MK_BSNMP} != "no" SUBDIR+= bsnmpd .endif diff --git a/usr.sbin/bsdconfig/Makefile b/usr.sbin/bsdconfig/Makefile new file mode 100644 index 0000000000000..1ce8d9cb21e02 --- /dev/null +++ b/usr.sbin/bsdconfig/Makefile @@ -0,0 +1,32 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= console \ + diskmgmt \ + docsinstall \ + dot \ + examples \ + include \ + mouse \ + networking \ + password \ + security \ + startup \ + timezone \ + ttys \ + usermgmt + +FILESDIR= ${LIBEXECDIR}/bsdconfig +FILES= USAGE + +SCRIPTS= bsdconfig + +MAN= bsdconfig.8 + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + mkdir -p ${DESTDIR}${SCRIPTSDIR} + mkdir -p ${DESTDIR}${MANDIR}8 + +.include diff --git a/usr.sbin/bsdconfig/USAGE b/usr.sbin/bsdconfig/USAGE new file mode 100644 index 0000000000000..86355b033bd3f --- /dev/null +++ b/usr.sbin/bsdconfig/USAGE @@ -0,0 +1,40 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: + @PROGRAM_NAME@ [-h] + @PROGRAM_NAME@ command [-h] + @PROGRAM_NAME@ [OPTIONS] [command [OPTIONS]] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). + +COMMANDS: +@COMMAND_LIST@ diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig new file mode 100755 index 0000000000000..2e8eaaed14dbc --- /dev/null +++ b/usr.sbin/bsdconfig/bsdconfig @@ -0,0 +1,313 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/strings.subr +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ FUNCTIONS + +# usage +# +# display usage and exit +# +usage() +{ + local index="INDEX" cmd_list="" + + cd $BSDCFG_LIBE + + # Test for language-specific indices + f_quietly ls */"$index.${LANG:-$LC_ALL}" && + index="$index.${LANG:-$LC_ALL}" + + cmd_list=$( + awk '/^menu_selection="/ { + sub(/\|.*/, "") + sub(/^menu_selection="/, "") + print + }' */$index | sort + ) + + # + # Determine the longest command-length (in characters) + # + local longest_cmd + longest_cmd=$( echo "$cmd_list" | f_longest_line_length ) + f_dprintf "longest_cmd=[$longest_cmd]" + + # + # Determine the maximum width of terminal/console + # + local max_size max_width + max_size=$( stty size 2> /dev/null ) + : ${max_size:="24 80"} + max_width="${max_size#*[$IFS]}" + f_dprintf "max_width=[$max_width]" + + # + # Using the longest command-length as the width of a single column, + # determine if we can use more than one column to display commands. + # + local x=$longest_cmd ncols=1 + x=$(( $x + 8 )) # Accomodate leading tab character + x=$(( $x + 3 + $longest_cmd )) # Preload end of next column + while [ $x -lt $max_width ]; do + ncols=$(( $ncols + 1 )) + x=$(( $x + 3 + $longest_cmd )) + done + f_dprintf "ncols=[$ncols] x=[$x]" + + # + # Re-format the command-list into multiple columns + # + cmd_list=$( eval "$( echo "$cmd_list" | + awk -v ncols=$ncols -v size=$longest_cmd ' + BEGIN { + n = 0 + row_item[1] = "" + } + function print_row() + { + fmt = "printf \"\\t%-" size "s" + for (i = 1; i < cur_col; i++) + fmt = fmt " %-" size "s" + fmt = fmt "\\n\"" + printf "%s", fmt + for (i = 1; i <= cur_col; i++) + printf " \"%s\"", row_item[i] + print "" + } + { + n++ + cur_col = (( n - 1 ) % ncols ) + 1 + printf "f_dprintf \"row_item[%u]=[%s]\"\n", cur_col, $0 + row_item[cur_col] = $0 + if ( cur_col == ncols ) print_row() + } + END { + if ( cur_col < ncols ) print_row() + }' )" + ) + + f_usage $BSDCFG_LIBE/USAGE \ + "PROGRAM_NAME" "$pgm" \ + "COMMAND_LIST" "$cmd_list" +} + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local title="$DIALOG_TITLE" + local btitle="$DIALOG_BACKTITLE" + local prompt="$msg_menu_text" + local menu_list size + + menu_list=" + 'X' '$msg_exit' '$msg_exit_bsdconfig' + " # END-QUOTE + + local sanitize_awk="{ gsub(/'/, \"'\\\\''\"); print }" + + local menuitem menu_title menu_help menu_selection index=1 + for menuitem in $( ls -d [0-9][0-9][0-9].* ); do + [ $index -lt ${#DIALOG_MENU_TAGS} ] || break + tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) + + menu_program= + menu_title= + menu_help= + f_include_lang $menuitem/INDEX + [ "$menu_program" ] || continue + + case "$menu_program" in + /*) : already fully qualified ;; + *) + menu_program="$menuitem/$menu_program" + esac + + menu_title=$( echo "$menu_title" | awk "$sanitize_awk" ) + menu_help=$( echo "$menu_help" | awk "$sanitize_awk" ) + setvar "menu_program$tag" "$menu_program" + menu_list="$menu_list '$tag' '$menu_title' '$menu_help'" + + index=$(( $index + 1 )) + done + + size=$( eval f_dialog_menu_with_help_size \ + \"\$title\" \ + \"\$btitle\" \ + \"\$prompt\" \ + \"\" \ + $menu_list ) + + eval $DIALOG \ + --clear \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --item-help \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_exit_bsdconfig\" \ + --menu \"\$prompt\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) usage;; + esac +done +shift $(( $OPTIND -1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_main_menu" + +[ "$SECURE" ] && f_mustberoot_init + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Log our operating environment for debugging purposes +# +export UNAME_S="$(uname -s)" # Operating System (i.e. FreeBSD) +export UNAME_P="$(uname -p)" # Processor Architecture (i.e. i386) +export UNAME_R="$(uname -r)" # Release Level (i.e. X.Y-RELEASE) +f_dprintf "UNAME_S=[$UNAME_S] UNAME_P=[$UNAME_P] UNAME_R=[$UNAME_R]" + +cd $BSDCFG_LIBE || f_die 1 "$msg_directory_not_found" "$BSDCFG_LIBE" + +# +# If a non-option argument was passed, process it as a menuitem selection... +# +if [ "$1" ]; then + # + # ...unless it's a long-option for usage. + # + case "$1" in + -help|--help|-\?) usage;; + esac + + # + # Find the INDEX (possibly i18n) claiming this keyword + # + lang="${LANG:-$LC_ALL}" + if [ "$lang" ]; then + sel=$( grep "^menu_selection=\"$1|" */INDEX.$lang \ + 2> /dev/null | tail -1 ) + + # Fall-back to non-i18n sources if nothing was found + [ "$sel" ] || + sel=$( grep "^menu_selection=\"$1|" */INDEX | tail -1 ) + else + sel=$( grep "^menu_selection=\"$1|" */INDEX | tail -1 ) + fi + + # + # If no matches, display usage (which shows valid keywords) + # + if [ ! "$sel" ]; then + f_err "%s: %s: $msg_not_found\n" "$pgm" "$1" + usage + fi + + # + # The command to execute is after the pipe-character (|) in the + # menu_selection property of the INDEX file for the menuitem. + # + cmd="${sel#*|}" + cmd="${cmd%\"}" + if [ ! "$cmd" ]; then + echo "$pgm: $1: $msg_not_found" + usage + fi + shift + + # + # If the command pathname is not fully qualified fix-up/force to be + # relative to the menuitem directory. + # + case "$cmd" in + /*) : already fully qualified ;; + *) + dir="${sel%%/*}" + cmd="$dir/$cmd" + esac + + exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1 +fi + +[ -f "$HOME/.bsdconfigrc" ] || f_dialog_msgbox "$msg_help_text" + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + X) # Exit + break + ;; + + *) # Dynamically loaded menuitem + cmd=$( eval echo \"\$menu_program$mtag\" ) + f_dprintf "cmd=[$cmd]" + $cmd ${USE_XDIALOG:+-X} + ;; + + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/bsdconfig.8 b/usr.sbin/bsdconfig/bsdconfig.8 new file mode 100644 index 0000000000000..58cfe002a7055 --- /dev/null +++ b/usr.sbin/bsdconfig/bsdconfig.8 @@ -0,0 +1,240 @@ +.\" Copyright (c) 2012 Ron McDowell +.\" Copyright (c) 2012 Devin Teske +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" docsinstall +.\" password +.\" diskmgmt +.\" usermgmt +.\" useradd +.\" useredit +.\" userdel +.\" groupmgmt +.\" groupadd +.\" groupedit +.\" groupdel +.\" console +.\" syscons_font +.\" syscons_keymap +.\" syscons_repeat +.\" syscons_saver +.\" syscons_screenmap +.\" syscons_ttys +.\" timezone +.\" mouse +.\" mouse_enable +.\" mouse_type +.\" mouse_port +.\" mouse_flags +.\" mouse_disable +.\" networking +.\" defaultrouter +.\" hostname +.\" nameservers +.\" netdev +.\" security +.\" ttys +.\" [dot] +.\" +.\" $FreeBSD$ +.\" +.Dd Mar 20, 2012 +.Dt BSDCONFIG 8 +.Os +.Sh NAME +.Nm bsdconfig +.Nd system configuration utility +.Sh SYNOPSIS +.Nm +.Op Fl h +.Nm +.Ar command +.Op Fl h +.Nm +.Op OPTIONS +.Op command Op OPTIONS +.Sh DESCRIPTION +.Nm +is used to set up many system configuration settings, both for new systems, as +well as changing configuration settings of existing systems. +.Pp +.Nm +takes a command as an argument. If invoked with no arguments, it will bring up +a master menu listing the available commands. +.Pp +The following options are available: +.Bl -tag -width indent+ +.It Fl h +Print usage statement and exit. +.It Fl S +Secure X11 mode +.Pq implies Fl X . +As root, always prompt-for and validate +.Xr sudo 8 +username/password before starting. +.It Fl X +Use +.Xr Xdialog 1 +in place of +.Xr dialog 1 . + +.Sh COMMANDS +The following commands +.Pq sorted alphabetically +are currently included in the base +.Nm +program, with more to be added soon. Other commands can be added, as detailed +below in the +.Cm ADDING COMMANDS +section, and once added, will appear in the master menu as well as in the +.Cm -h +listing. +.Bl -tag -width ".Cm syscons_screenmap" +.It Cm console +Utilities to customize the behavior of the system console. +.It Cm defaultrouter +Shortcut to the Default Router/Gateway menu under networking. +.It Cm diskmgmt +Manage disk partitions and/or labels. Executes +.Xr sade 8 . +.It Cm docsinstall +Executes the +.Cm bsdinstall docsinstall +sub-utility to allow installation/re-installation of the FreeBSD Documentation +set(s). +.It Cm dot +Generate a graphviz +.Xr dot 1 +language file +.Pq printed on stdout +visualizing the +.Xr bsdconfig 8 +menu, include, and shortcut structure and relationships. See +.Dq bsdconfig dot -h +for more details. +.It Cm groupadd +Shortcut to the Add Groups menu under groupmgmt. +.It Cm groupdel +Shortcut to the Delete Groups menu under groupmgmt. +.It Cm groupedit +Shortcut to the Edit/View Groups menu under groupmgmt. +.It Cm groupmgmt +Utilities to Add/Change/View/Delete Group Accounts. +.It Cm hostname +Shortcut to the Hostname/Domain menu under networking. +.It Cm mouse +Utilities for configuring, exploring, and enabling console mouse support. +.It Cm mouse_disable +Shortcut to the Disable menu under mouse. +.It Cm mouse_enable +Shortcut to the Enable menu under mouse. +.It Cm mouse_flags +Shortcut to the Flags menu under mouse. +.It Cm mouse_port +Shortcut to the Port menu under mouse. +.It Cm mouse_type +Shortcut to the Type menu under mouse. +.It Cm nameservers +Shortcut to the DNS Nameservers menu under networking. +.It Cm netdev +Shortcut to the Network Interfaces menu under networking. +.It Cm networking +Utilities to set/change Hostname/Domain, Network Interfaces, Default +Router/Gateway, and DNS Nameservers. +.It Cm password +Set the system administrator +.Pq root +password. +.It Cm security +Configure various system security settings. +.It Cm syscons_font +Shortcut to the Font menu under console. +.It Cm syscons_keymap +Shortcut to the Keymap menu under console. +.It Cm syscons_repeat +Shortcut to the Repeat menu under console. +.It Cm syscons_saver +Shortcut to the Saver menu under console. +.It Cm syscons_screenmap +Shortcut to the Screenmap menu under console. +.It Cm syscons_ttys +Shortcut to the Ttys menu under console. +.It Cm timezone +Set the regional timezone of the local machine. +.It Cm useradd +Shortcut to the Add Users menu under usermgmt. +.It Cm userdel +Shortcut to the Delete Users menu under usermgmt. +.It Cm useredit +Shortcut to the Edit/View Users menu under usermgmt. +.It Cm usermgmt +Utilities to Add/Edit/View/Delete User Accounts. + +.Sh INTERNATIONALIZATION +i18n features are built into +.Nm +and language-specific translation files will be added as they become available. +In the absence of language-specific translation files, the default +.Pq en_US.ISO8859-1 +files will be used. + +.Sh ADDING COMMANDS +To be documented later. Document menu_selection="command|*" syntax of INDEX +files. + +.Sh ENVIRONMENT VARIABLES +The following environment variables affect the execution of +.Nm : +.Bl -tag -width ".Ev LC_ALL" +.It Ev LANG +If LANG is set, messages and index information will be read from files named +messages.$LANG and INDEX.$LANG and fall back to files named messages and INDEX if +messages.$LANG and INDEX.$LANG do not exist. LANG takes precedence over LC_ALL. +.It Ev LC_ALL +If LC_ALL is set, messages and index information will be read from files named +messages.$LC_ALL and INDEX.$LC_ALL and fall back to files named messages and INDEX if +messages.$LC_ALL and INDEX.$LC_ALL do not exist. + +.Sh FILES +/usr/share/examples/bsdconfig/bsdconfigrc can be copied to $HOME/.bsdconfigrc and +customized as needed. + +.Sh EXIT STATUS +.Ex -std + +.Sh SEE ALSO +.Xr host-setup 8 + +.Sh HISTORY +.Nm +first appeared in +.Fx 10.0 . + +.Sh AUTHORS +.\" An -nosplit +.An Ron McDowell Aq rcm@FuzzWad.ORG +.An Devin Teske Aq devinteske@hotmail.com + +.Sh BUGS +Undoubtedly. diff --git a/usr.sbin/bsdconfig/console/INDEX b/usr.sbin/bsdconfig/console/INDEX new file mode 100644 index 0000000000000..30ebcf89aeb2a --- /dev/null +++ b/usr.sbin/bsdconfig/console/INDEX @@ -0,0 +1,59 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Console" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Customize system console behavior" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="console|console" +menu_selection="syscons_font|font" +menu_selection="syscons_keymap|keymap" +menu_selection="syscons_repeat|repeat" +menu_selection="syscons_saver|saver" +menu_selection="syscons_screenmap|screenmap" +menu_selection="syscons_ttys|ttys" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="console" diff --git a/usr.sbin/bsdconfig/console/Makefile b/usr.sbin/bsdconfig/console/Makefile new file mode 100644 index 0000000000000..aa6c6f181511e --- /dev/null +++ b/usr.sbin/bsdconfig/console/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/080.console +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= console font keymap repeat saver screenmap ttys + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/console/USAGE b/usr.sbin/bsdconfig/console/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/console/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/console/console b/usr.sbin/bsdconfig/console/console new file mode 100755 index 0000000000000..d4eeec8b337ac --- /dev/null +++ b/usr.sbin/bsdconfig/console/console @@ -0,0 +1,137 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_configure_system_console_settings" + local prompt="$msg_console_menu_text" + + menu_list=" + 'X $msg_exit' '$msg_exit_this_menu' + '2 $msg_font' '$msg_choose_alternate_screen_font' + '3 $msg_keymap' '$msg_choose_alternate_keyboard_map' + '4 $msg_repeat' '$msg_set_repeat_rate' + '5 $msg_saver' '$msg_configure_screen_saver' + '6 $msg_screenmap' '$msg_choose_alternate_screenmap' + '7 $msg_ttys' '$msg_choose_console_terminal_type' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_configuration" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + "2 $msg_font") # Choose an alternate screen font + $BSDCFG_LIBE/$APP_DIR/font ${USE_XDIALOG:+-X} ;; + "3 $msg_keymap") # Choose an alternate keyboard map + $BSDCFG_LIBE/$APP_DIR/keymap ${USE_XDIALOG:+-X} ;; + "4 $msg_repeat") # Set the rate at which keys repeat + $BSDCFG_LIBE/$APP_DIR/repeat ${USE_XDIALOG:+-X} ;; + "5 $msg_saver") # Configure the screen saver + $BSDCFG_LIBE/$APP_DIR/saver ${USE_XDIALOG:+-X} ;; + "6 $msg_screenmap") # Choose an alternate screenmap + $BSDCFG_LIBE/$APP_DIR/screenmap ${USE_XDIALOG:+-X} ;; + "7 $msg_ttys") # Choose console terminal type + $BSDCFG_LIBE/$APP_DIR/ttys ${USE_XDIALOG:+-X} ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/console/font b/usr.sbin/bsdconfig/console/font new file mode 100755 index 0000000000000..5ea3e2d225289 --- /dev/null +++ b/usr.sbin/bsdconfig/console/font @@ -0,0 +1,205 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_choose_a_font" + local prompt="$msg_font_menu_text" + + menu_list=" + '1 $msg_none' '$msg_use_hardware_default_font' + '2 $msg_ibm_437' '$msg_ibm_437_desc' + '3 $msg_ibm_850' '$msg_ibm_850_desc' + '4 $msg_ibm_865' '$msg_ibm_865_desc' + '5 $msg_ibm_866' '$msg_ibm_866_desc' + '6 $msg_ibm_866u' '$msg_ibm_866u_desc' + '7 $msg_ibm_1251' '$msg_ibm_1251_desc' + '8 $msg_iso_8859_1' '$msg_iso_8859_1_desc' + '9 $msg_iso_8859_2' '$msg_iso_8859_2_desc' + 'a $msg_iso_8859_4' '$msg_iso_8859_4_desc' + 'b $msg_iso_8859_7' '$msg_iso_8859_7_desc' + 'c $msg_iso_8859_8' '$msg_iso_8859_8_desc' + 'd $msg_iso_8859_15' '$msg_iso_8859_15_desc' + 'e $msg_swiss' '$msg_swiss_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_font" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "1 $msg_none") # Use hardware default font + f_sysrc_set font8x8 "NO" || f_die + f_sysrc_set font8x14 "NO" || f_die + f_sysrc_set font8x16 "NO" || f_die + break ;; + "2 $msg_ibm_437") # English and others, VGA default + f_sysrc_set font8x8 "cp437-8x8" || f_die + f_sysrc_set font8x14 "cp437-8x14" || f_die + f_sysrc_set font8x16 "cp437-8x16" || f_die + break ;; + "3 $msg_ibm_850") # Western Europe, IBM encoding + f_sysrc_set font8x8 "cp850-8x8" || f_die + f_sysrc_set font8x14 "cp850-8x14" || f_die + f_sysrc_set font8x16 "cp850-8x16" || f_die + break ;; + "4 $msg_ibm_865") # Norwegian, IBM encoding + f_sysrc_set font8x8 "cp865-8x8" || f_die + f_sysrc_set font8x14 "cp865-8x14" || f_die + f_sysrc_set font8x16 "cp865-8x16" || f_die + break ;; + "5 $msg_ibm_866") # Russian, IBM encoding (use with KOI8-R screenmap) + f_sysrc_set font8x8 "cp866-8x8" || f_die + f_sysrc_set font8x14 "cp866-8x14" || f_die + f_sysrc_set font8x16 "cp866b-8x16" || f_die + f_sysrc_set mousechar_start 3 || f_die + break ;; + "6 $msg_ibm_866u") # Ukrainian, IBM encoding (use w/ KOI8-U screenmap) + f_sysrc_set font8x8 "cp866u-8x8" || f_die + f_sysrc_set font8x14 "cp866u-8x14" || f_die + f_sysrc_set font8x16 "cp866u-8x16" || f_die + f_sysrc_set mousechar_start 3 || f_die + break ;; + "7 $msg_ibm_1251") # Cyrillic, MS Windows encoding + f_sysrc_set font8x8 "cp1251-8x8" || f_die + f_sysrc_set font8x14 "cp1251-8x14" || f_die + f_sysrc_set font8x16 "cp1251-8x16" || f_die + f_sysrc_set mousechar_start 3 || f_die + break ;; + "8 $msg_iso_8859_1") # Western Europe, ISO encoding + f_sysrc_set font8x8 "iso-8x8" || f_die + f_sysrc_set font8x14 "iso-8x14" || f_die + f_sysrc_set font8x16 "iso-8x16" || f_die + break ;; + "9 $msg_iso_8859_2") # Eastern Europe, ISO encoding + f_sysrc_set font8x8 "iso02-8x8" || f_die + f_sysrc_set font8x14 "iso02-8x14" || f_die + f_sysrc_set font8x16 "iso02-8x16" || f_die + break ;; + "a $msg_iso_8859_4") # Baltic, ISO encoding + f_sysrc_set font8x8 "iso04-8x8" || f_die + f_sysrc_set font8x14 "iso04-8x14" || f_die + f_sysrc_set font8x16 "iso04-8x16" || f_die + break ;; + "b $msg_iso_8859_7") # Greek, ISO encoding + f_sysrc_set font8x8 "iso07-8x8" || f_die + f_sysrc_set font8x14 "iso07-8x14" || f_die + f_sysrc_set font8x16 "iso07-8x16" || f_die + break ;; + "c $msg_iso_8859_8") # Hebrew, ISO encoding + f_sysrc_set font8x8 "iso08-8x8" || f_die + f_sysrc_set font8x14 "iso08-8x14" || f_die + f_sysrc_set font8x16 "iso08-8x16" || f_die + break ;; + "d $msg_iso_8859_15") # Europe, ISO encoding + f_sysrc_set font8x8 "iso15-8x8" || f_die + f_sysrc_set font8x14 "iso15-8x14" || f_die + f_sysrc_set font8x16 "iso15-8x16" || f_die + break ;; + "e $msg_swiss") # English, better resolution + f_sysrc_set font8x8 "swiss-8x8" || f_die + f_sysrc_set font8x14 "NO" || f_die + f_sysrc_set font8x16 "swiss-8x16" || f_die + break ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/console/include/Makefile b/usr.sbin/bsdconfig/console/include/Makefile new file mode 100644 index 0000000000000..e9157515ce327 --- /dev/null +++ b/usr.sbin/bsdconfig/console/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/080.console/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/console/include/messages.subr b/usr.sbin/bsdconfig/console/include/messages.subr new file mode 100644 index 0000000000000..ffc90849d515b --- /dev/null +++ b/usr.sbin/bsdconfig/console/include/messages.subr @@ -0,0 +1,264 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_choose_a_font="Choose a font" +hline_choose_a_keyboard_map="Choose a keyboard map" +hline_choose_a_keyboard_repeat_rate="Choose a keyboard repeat rate" +hline_choose_a_screen_map="Choose a screen map" +hline_choose_a_screen_saver="Choose a nifty-looking screen saver" +hline_choose_a_terminal_type="Choose a terminal type" +hline_configure_system_console_settings="Configure your system console settings" +msg_beastie="Beastie" +msg_beastie_desc="\"BSD Daemon\" animated screen saver (graphics)" +msg_belgian="Belgian" +msg_belgian_desc="Belgian ISO keymap" +msg_blank="Blank" +msg_blank_desc="Simply blank the screen" +msg_brazil_cp850="Brazil CP850" +msg_brazil_cp850_desc="Brazil CP850 keymap" +msg_brazil_iso="Brazil ISO" +msg_brazil_iso_accent="Brazil ISO (accent)" +msg_brazil_iso_accent_desc="Brazil ISO keymap (accent keys)" +msg_brazil_iso_desc="Brazil ISO keymap" +msg_bulgarian_bds="Bulgarian BDS" +msg_bulgarian_bds_desc="Bulgarian BDS keymap" +msg_bulgarian_phonetic="Bulgarian Phonetic" +msg_bulgarian_phonetic_desc="Bulgarian Phonetic keymap" +msg_cancel="Cancel" +msg_central_european_iso="Central European ISO" +msg_central_european_iso_desc="Central European ISO keymap" +msg_choose_alternate_keyboard_map="Choose an alternate keyboard map" +msg_choose_alternate_screenmap="Choose an alternate screenmap" +msg_choose_alternate_screen_font="Choose an alternate screen font" +msg_choose_console_terminal_type="Choose console terminal type" +msg_configure_screen_saver="Configure the screen saver" +msg_console_menu_text="The system console driver for FreeBSD has a number of configuration\noptions which may be set according to your preference.\n\nWhen you are done setting configuration options, select Cancel." +msg_croatian_iso="Croation ISO" +msg_croatian_iso_desc="Croation ISO keymap" +msg_czech_iso_accent="Czech ISO (accent)" +msg_czech_iso_accent_desc="Czech ISO keymap (accent keys)" +msg_daemon="Daemon" +msg_daemon_desc="\"BSD Daemon\" animated screen saver (text)" +msg_danish_cp865="Danish CP865" +msg_danish_cp865_desc="Danish Code Page 865 keymap" +msg_danish_iso="Danish ISO" +msg_danish_iso_desc="Danish ISO keymap" +msg_default="Default" +msg_default_desc="Use default keyboard repeat rate" +msg_dragon="Dragon" +msg_dragon_desc="Dragon screensaver (graphics)" +msg_enter_timeout_period="Enter time-out period in seconds for screen saver" +msg_estonian_cp850="Estonian CP850" +msg_estonian_cp850_desc="Estonian Code Page 850 keymap" +msg_estonian_iso="Estonian ISO" +msg_estonian_iso_desc="Estonian ISO keymap" +msg_estonian_iso_15="Estonian ISO 15" +msg_estonian_iso_15_desc="Estonian ISO 8859-15 keymap" +msg_exit="Exit" +msg_exit_this_menu="Exit this menu (returning to previous)" +msg_fade="Fade" +msg_fade_desc="Fade out effect screen saver" +msg_fast="Fast" +msg_fast_desc="Fast keyboard repeat rate" +msg_finnish_cp850="Finnish CP850" +msg_finnish_cp850_desc="Finnish Code Page 850 keymap" +msg_finnish_iso="Finnish ISO" +msg_finnish_iso_desc="Finnish ISO keymap" +msg_fire="Fire" +msg_fire_desc="Flames effect screen saver" +msg_font="Font" +msg_font_menu_text="Most PC hardware defaults to displaying characters in the\nIBM 437 character set. However, in the Unix world, this\ncharacter set is very rarely used. Most Western European\ncountries, for example, prefer ISO 8859-1.\nAmerican users won't notice the difference since the bottom half\nof all these charactersets is ANSI anyway. However, they might\nwant to load a font anyway to use the 30- or 50-line displays.\nIf your hardware is capable of downloading a new display font,\nyou can select the appropriate font below." +msg_french_iso="French ISO" +msg_french_iso_accent="French ISO (accent)" +msg_french_iso_accent_desc="French ISO keymap (accent keys)" +msg_french_iso_desc="French ISO keymap" +msg_french_iso_macbook="French ISO/Macbook" +msg_french_iso_macbook_desc="French ISO keymap on macbook" +msg_green="Green" +msg_green_desc="\"Green\" power saving mode (if supported by monitor)" +msg_german_cp850="German CP850" +msg_german_cp850_desc="German Code Page 850 keymap" +msg_german_iso="German ISO" +msg_german_iso_desc="German ISO keymap" +msg_greek_101="Greek 101" +msg_greek_101_desc="Greek ISO keymap (101 keys)" +msg_greek_104="Greek 104" +msg_greek_104_desc="Greek ISO keymap (104 keys)" +msg_greek_elot="Greek ELOT" +msg_greek_elot_desc="Greek ISO keymap (ELOT 1000)" +msg_hungarian_101="Hungarian 101" +msg_hungarian_101_desc="Hungarian ISO keymap (101 key)" +msg_hungarian_102="Hungarian 102" +msg_hungarian_102_desc="Hungarian ISO keymap (102 key)" +msg_ibm_437="IBM 437" +msg_ibm_437_desc="English and others, VGA default" +msg_ibm_437_vga_default="IBM437 (VGA default)" +msg_ibm_850="IBM 850" +msg_ibm_850_desc="Western Europe, IBM encoding" +msg_ibm_865="IBM 865" +msg_ibm_865_desc="Norwegian, IBM encoding" +msg_ibm_866="IBM 866" +msg_ibm_866_desc="Russian, IBM encoding (use with KOI8-R screenmap)" +msg_ibm_866u="IBM 866u" +msg_ibm_866u_desc="Ukrainian, IBM encoding (use with KOI8-U screenmap)" +msg_ibm_1251="IBM 1251" +msg_ibm_1251_desc="Cyrillic, MS Windows encoding" +msg_icelandic="Icelandic" +msg_icelandic_accent="Icelandic (accent)" +msg_icelandic_accent_desc="Icelandic ISO keymap (accent keys)" +msg_icelandic_desc="Icelandic ISO keymap" +msg_iso_8859_1="ISO 8859-1" +msg_iso_8859_1_desc="Western Europe, ISO encoding" +msg_iso_8859_1_to_ibm437="ISO 8859-1 to IBM437" +msg_iso_8859_1_to_ibm437_desc="W-Europe ISO 8859-1 to IBM 437 screenmap" +msg_iso_8859_2="ISO 8859-2" +msg_iso_8859_2_desc="Eastern Europe, ISO encoding" +msg_iso_8859_4="ISO 8859-4" +msg_iso_8859_4_desc="Baltic, ISO encoding" +msg_iso_8859_7="ISO 8859-7" +msg_iso_8859_7_desc="Greek, ISO encoding" +msg_iso_8859_7_to_ibm437="ISO 8859-7 to IBM437" +msg_iso_8859_7_to_ibm437_desc="Greek ISO 8859-1 to IBM 437 screenmap" +msg_iso_8859_8="ISO 8859-8" +msg_iso_8859_8_desc="Hebrew, ISO encoding" +msg_iso_8859_15="ISO 8859-15" +msg_iso_8859_15_desc="Europe, ISO encoding" +msg_italian="Italian" +msg_italian_desc="Italian ISO keymap" +msg_japanese_106="Japanese 106" +msg_japanese_106_desc="Japanese 106 keymap" +msg_keymap="Keymap" +msg_keymap_menu_text="The system console driver for FreeBSD defaults to a standard\n\"US\" keyboard map. Users may wish to choose one of the\nother keymaps below." +msg_koi8_r="KOI8-R" +msg_koi8_u="KOI8-U" +msg_koi8_r_to_ibm866="KOI8-R to IBM866" +msg_koi8_r_to_ibm866_desc="Russian KOI8-R to IBM 866 screenmap" +msg_koi8_u_to_ibm866u="KOI8-U to IBM866u" +msg_koi8_u_to_ibm866u_desc="Ukrainian KOI8-U to IBM 866u screenmap" +msg_latin_american="Latin American" +msg_latin_american_accent="Latin American (accent)" +msg_latin_american_accent_desc="Latin American ISO keymap (accent keys)" +msg_latin_american_desc="Latin American ISO keymap" +msg_logo="Logo" +msg_logo_desc="FreeBSD \"logo\" animated screen saver (graphics)" +msg_none="None" +msg_none_saver_desc="Disable the screensaver" +msg_none_screenmap_desc="No screenmap, don'\''t touch font" +msg_none_ttys_desc="Don'\''t touch anything" +msg_normal="Normal" +msg_normal_desc="\"Normal\" keyboard repeat rate" +msg_norway_iso="Norway ISO" +msg_norway_iso_desc="Norwegian ISO keymap" +msg_ok="OK" +msg_polish_iso="Polish ISO" +msg_polish_iso_desc="Polish ISO keymap" +msg_portuguese="Portuguese" +msg_portuguese_accent="Portuguese (accent)" +msg_portuguese_accent_desc="Portuguese ISO keymap (accent keys)" +msg_portuguese_desc="Portuguese ISO keymap" +msg_rain="Rain" +msg_rain_desc="Rain drops screen saver" +msg_repeat="Repeat" +msg_repeat_menu_text="This menu allows you to set the speed at which keys repeat\nwhen held down." +msg_russia_koi8_r="Russia KOI8-R" +msg_russia_koi8_r_desc="Russia KOI8-R keymap" +msg_saver="Saver" +msg_saver_menu_text="By default, the console driver will not attempt to do anything\nspecial with your screen when it's idle. If you expect to leave your\nmonitor switched on and idle for long periods of time then you should\nprobably enable one of these screen savers to prevent burn-in." +msg_screenmap="Screenmap" +msg_screenmap_menu_text="Unless you load a specific font, most PC hardware defaults to\ndisplaying characters in the IBM 437 character set. However,\nin the Unix world, this character set is very rarely used. Most\nWestern European countries, for example, prefer ISO 8859-1.\nAmerican users won't notice the difference since the bottom half\nof all these character sets is ANSI anyway.\nIf your hardware is capable of downloading a new display font,\nyou should probably choose that option. However, for hardware\nwhere this is not possible (e.g. monochrome adapters), a screen\nmap will give you the best approximation that your hardware can\ndisplay at all." +msg_set_repeat_rate="Set the rate at which keys repeat" +msg_slovak="Slovak" +msg_slovak_desc="Slovak ISO keymap" +msg_slovenian="Slovenian" +msg_slovenian_desc="Slovenian ISO keymap" +msg_slow="Slow" +msg_slow_desc="Slow keyboard repeat rate" +msg_snake="Snake" +msg_snake_desc="Draw a FreeBSD \"snake\" on your screen" +msg_spanish="Spanish" +msg_spanish_accent="Spanish (accent)" +msg_spanish_accent_desc="Spanish ISO keymap (accent keys)" +msg_spanish_desc="Spanish ISO keymap" +msg_star="Star" +msg_star_desc="A \"twinkling stars\" effect" +msg_swedish_cp850="Swedish CP850" +msg_swedish_cp850_desc="Swedish Code Page 850 keymap" +msg_swedish_iso="Swedish ISO" +msg_swedish_iso_desc="Swedish ISO keymap" +msg_swiss="SWISS" +msg_swiss_desc="English, better resolution" +msg_swiss_french_cp850="Swiss French CP850" +msg_swiss_french_cp850_desc="Swiss French Code Page 850 keymap" +msg_swiss_french_iso="Swiss French ISO" +msg_swiss_french_iso_accent="Swiss French ISO (accent)" +msg_swiss_french_iso_accent_desc="Swiss French ISO keymap (accent keys)" +msg_swiss_french_iso_desc="Swiss French ISO keymap" +msg_swiss_german_cp850="Swiss German CP850" +msg_swiss_german_cp850_desc="Swiss German Code Page 850 keymap" +msg_swiss_german_iso="Swiss German ISO" +msg_swiss_german_iso_accent="Swiss German ISO (accent)" +msg_swiss_german_iso_accent_desc="Swiss German ISO keymap (accent keys)" +msg_swiss_german_iso_desc="Swiss German ISO keymap" +msg_system_console_configuration="System Console Configuration" +msg_system_console_font="System Console Font" +msg_system_console_keymap="System Console Keymap" +msg_system_console_keyboard_repeat_rate="System Console Keyboard Repeat Rate" +msg_system_console_screen_saver="System Console Screen Saver" +msg_system_console_screenmap="System Console Screenmap" +msg_system_console_terminal_type="System Console Terminal Type" +msg_timeout="Timeout" +msg_timeout_desc="Set the screen saver timeout interval" +msg_ttys="Ttys" +msg_ttys_menu_text="For various console encodings, a corresponding terminal type\nmust be chosen in /etc/ttys.\n\nWARNING: For compatibility reasons, only entries starting with\nttyv and terminal types starting with cons[0-9] can be changed\nvia this menu." +msg_uk_cp850="UK CP850" +msg_uk_cp850_desc="UK Code Page 850 keymap" +msg_uk_iso="UK ISO" +msg_uk_iso_desc="UK ISO keymap" +msg_ukrainian_koi8_u="Ukranian KOI8-U" +msg_ukrainian_koi8_u_desc="Ukranian KOI8-U keymap" +msg_ukrainian_koi8_u_koi8_r="Ukranian KOI8-U+KOI8-R" +msg_ukrainian_koi8_u_koi8_r_desc="Ukranian KOI8-U+KOI8-R keymap (alter)" +msg_us_ascii="US-ASCII" +msg_us_ascii_to_ibm327="US-ASCII to IBM437" +msg_us_ascii_to_ibm327_desc="US-ASCII to IBM 437 screenmap" +msg_usa_capslock_ctrl="USA CapsLock->Ctrl" +msg_usa_capslock_ctrl_desc="US standard (Caps as L-Control)" +msg_usa_dvorak="USA Dvorak" +msg_usa_dvorak_desc="US Dvorak keymap" +msg_usa_dvorak_left="USA Dvorak (left)" +msg_usa_dvorak_left_desc="US left handed Dvorak keymap" +msg_usa_dvorak_right="USA Dvorak (right)" +msg_usa_dvorak_right_desc="US right handed Dvorak keymap" +msg_usa_emacs="USA Emacs" +msg_usa_emacs_desc="US standard optimized for EMACS" +msg_usa_iso="USA ISO" +msg_usa_iso_desc="US ISO keymap" +msg_usa_unix="USA UNIX" +msg_usa_unix_desc="US traditional UNIX-workstation" +msg_use_hardware_default_font="Use hardware default font" +msg_value_required="Value Required" +msg_warp="Warp" +msg_warp_desc="A \"stars warping\" effect" diff --git a/usr.sbin/bsdconfig/console/keymap b/usr.sbin/bsdconfig/console/keymap new file mode 100755 index 0000000000000..63592e7c3a5d7 --- /dev/null +++ b/usr.sbin/bsdconfig/console/keymap @@ -0,0 +1,365 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# List of keymap names +# +KEYMAP_NAMES=" + belgian brazil_cp850 brazil_iso + brazil_iso_accent bulgarian_bds bulgarian_phonetic + central_european_iso croatian_iso czech_iso_accent + danish_cp865 danish_iso estonian_cp850 + estonian_iso estonian_iso_15 finnish_cp850 + finnish_iso french_iso french_iso_accent + french_iso_macbook german_cp850 german_iso + greek_101 greek_104 greek_elot + hungarian_101 hungarian_102 icelandic + icelandic_accent italian japanese_106 + latin_american latin_american_accent norway_iso + polish_iso portuguese portuguese_accent + russia_koi8_r slovak slovenian + spanish spanish_accent swedish_cp850 + swedish_iso swiss_french_cp850 swiss_french_iso + swiss_french_iso_accent swiss_german_cp850 swiss_german_iso + swiss_german_iso_accent uk_cp850 uk_iso + ukrainian_koi8_u ukrainian_koi8_u_koi8_r usa_capslock_ctrl + usa_dvorak usa_dvorak_left usa_dvorak_right + usa_emacs usa_iso usa_unix +" # END-QUOTE + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_choose_a_keyboard_map" + local prompt="$msg_keymap_menu_text" + + # + # Export variables for awk(1) ENVIRON visibility + # + local name + for name in $KEYMAP_NAMES; do + export msg_$name msg_${name}_desc + done + + # + # Generate a sorted list of keymaps. If the first letter of the keymap + # name is unique (case-insensitive) then it is used as the tag to allow + # the user to jump to that entry. + # + menu_list=$( + for name in $KEYMAP_NAMES; do + eval echo \"\$msg_$name\" msg_$name + done | sort | awk 'BEGIN { prefix = "" } + { + cur_prefix = tolower(substr(ENVIRON[$NF], 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + printf "%s'\'' '\''%s'\''\n", + ENVIRON[$NF], ENVIRON[$NF"_desc"] + }' + ) + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_keymap" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + # + # Reverse the users choice into the variable name + # + keymap_name= + mtag="${mtag# }" # remove single leading-space if-present + for name in $KEYMAP_NAMES; do + if [ "$( eval echo \"\$msg_$name\" )" = "$mtag" ]; then + keymap_name="$name" + break + fi + done + + [ "$keymap_name" ] || continue + + case "$keymap_name" in + belgian) + f_sysrc_set keymap "be.iso" || f_die + break ;; + brazil_cp850) + f_sysrc_set keymap "br275.cp850" || f_die + break ;; + brazil_iso) + f_sysrc_set keymap "br275.iso" || f_die + break ;; + brazil_iso_accent) + f_sysrc_set keymap "br275.iso.acc" || f_die + break ;; + bulgarian_bds) + f_sysrc_set keymap "bg.bds.ctrlcaps" || f_die + break ;; + bulgarian_phonetic) + f_sysrc_set keymap "bg.phonetic.ctrlcaps" || f_die + break ;; + central_european_iso) + f_sysrc_set keymap "ce.iso2" || f_die + break ;; + croatian_iso) + f_sysrc_set keymap "hr.iso" || f_die + break ;; + czech_iso_accent) + f_sysrc_set keymap "cs.latin2.qwertz" || f_die + break ;; + danish_cp865) + f_sysrc_set keymap "danish.cp865" || f_die + break ;; + danish_iso) + f_sysrc_set keymap "danish.iso" || f_die + break ;; + estonian_cp850) + f_sysrc_set keymap "estonian.cp850" || f_die + break ;; + estonian_iso) + f_sysrc_set keymap "estonian.iso" || f_die + break ;; + estonian_iso_15) + f_sysrc_set keymap "estonian.iso15" || f_die + break ;; + finnish_cp850) + f_sysrc_set keymap "finnish.cp850" || f_die + break ;; + finnish_iso) + f_sysrc_set keymap "finnish.iso" || f_die + break ;; + french_iso) + f_sysrc_set keymap "fr.iso" || f_die + break ;; + french_iso_accent) + f_sysrc_set keymap "fr.iso.acc" || f_die + break ;; + french_iso_macbook) + f_sysrc_set keymap "fr.macbook.acc" || f_die + break ;; + german_cp850) + f_sysrc_set keymap "german.cp850" || f_die + break ;; + german_iso) + f_sysrc_set keymap "german.iso" || f_die + break ;; + greek_101) + f_sysrc_set keymap "gr.us101.acc" || f_die + break ;; + greek_104) + f_sysrc_set keymap "el.iso07" || f_die + break ;; + greek_elot) + f_sysrc_set keymap "gr.elot.acc" || f_die + break ;; + hungarian_101) + f_sysrc_set keymap "hu.iso2.101keys" || f_die + break ;; + hungarian_102) + f_sysrc_set keymap "hu.iso2.102keys" || f_die + break ;; + icelandic) + f_sysrc_set keymap "icelandic.iso" || f_die + break ;; + icelandic_accent) + f_sysrc_set keymap "icelandic.iso.acc" || f_die + break ;; + italian) + f_sysrc_set keymap "it.iso" || f_die + break ;; + japanese_106) + f_sysrc_set keymap "jp.106" || f_die + break ;; + latin_american) + f_sysrc_set keymap "latinamerican" || f_die + break ;; + latin_american_accent) + f_sysrc_set keymap "latinamerican.iso.acc" || f_die + break ;; + norway_iso) + f_sysrc_set keymap "norwegian.iso" || f_die + break ;; + polish_iso) + f_sysrc_set keymap "pl_PL.ISO8859-2" || f_die + break ;; + portuguese) + f_sysrc_set keymap "pt.iso" || f_die + break ;; + portuguese_accent) + f_sysrc_set keymap "pt.iso.acc" || f_die + break ;; + russia_koi8_r) + f_sysrc_set keymap "ru.koi8-r" || f_die + break ;; + slovak) + f_sysrc_set keymap "sk.iso2" || f_die + break ;; + slovenian) + f_sysrc_set keymap "si.iso" || f_die + break ;; + spanish) + f_sysrc_set keymap "spanish.iso" || f_die + break ;; + spanish_accent) + f_sysrc_set keymap "spanish.iso.acc" || f_die + break ;; + swedish_cp850) + f_sysrc_set keymap "swedish.cp850" || f_die + break ;; + swedish_iso) + f_sysrc_set keymap "swedish.iso" || f_die + break ;; + swiss_french_cp850) + f_sysrc_set keymap "swissfrench.cp850" || f_die + break ;; + swiss_french_iso) + f_sysrc_set keymap "swissfrench.iso" || f_die + break ;; + swiss_french_iso_accent) + f_sysrc_set keymap "swissfrench.iso.acc" || f_die + break ;; + swiss_german_cp850) + f_sysrc_set keymap "swissgerman.cp850" || f_die + break ;; + swiss_german_iso) + f_sysrc_set keymap "swissgerman.iso" || f_die + break ;; + swiss_german_iso_accent) + f_sysrc_set keymap "swissgerman.iso.acc" || f_die + break ;; + uk_cp850) + f_sysrc_set keymap "uk.cp850" || f_die + break ;; + uk_iso) + f_sysrc_set keymap "uk.iso" || f_die + break ;; + ukrainian_koi8_u) + f_sysrc_set keymap "ua.koi8-u" || f_die + break ;; + ukrainian_koi8_u_koi8_r) + f_sysrc_set keymap "ua.koi8-u.shift.alt" || f_die + break ;; + usa_capslock_ctrl) + f_sysrc_set keymap "us.pc-ctrl" || f_die + break ;; + usa_dvorak) + f_sysrc_set keymap "us.dvorak" || f_die + break ;; + usa_dvorak_left) + f_sysrc_set keymap "us.dvorakl" || f_die + break ;; + usa_dvorak_right) + f_sysrc_set keymap "us.dvorakr" || f_die + break ;; + usa_emacs) + f_sysrc_set keymap "us.emacs" || f_die + break ;; + usa_iso) + f_sysrc_set keymap "us.iso" || f_die + break ;; + usa_unix) + f_sysrc_set keymap "us.unix" || f_die + break ;; + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/console/repeat b/usr.sbin/bsdconfig/console/repeat new file mode 100755 index 0000000000000..8f968fd87cdf3 --- /dev/null +++ b/usr.sbin/bsdconfig/console/repeat @@ -0,0 +1,134 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_choose_a_keyboard_repeat_rate" + local prompt="$msg_repeat_menu_text" + + menu_list=" + '$msg_slow' '$msg_slow_desc' + '$msg_normal' '$msg_normal_desc' + '$msg_fast' '$msg_fast_desc' + '$msg_default' '$msg_default_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_keyboard_repeat_rate" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "$msg_slow") # Slow keyboard repeat rate + f_sysrc_set keyrate "slow" || f_die + break ;; + "$msg_normal") # "Normal" keyboard repeat rate + f_sysrc_set keyrate "normal" || f_die + break ;; + "$msg_fast") # Fast keyboard repeat rate + f_sysrc_set keyrate "fast" || f_die + break ;; + "$msg_default") # Use default keyboard repeat rate + f_sysrc_set keyrate "NO" || f_die + break ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/console/saver b/usr.sbin/bsdconfig/console/saver new file mode 100755 index 0000000000000..8e803d2ab6895 --- /dev/null +++ b/usr.sbin/bsdconfig/console/saver @@ -0,0 +1,198 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_choose_a_screen_saver" + local prompt="$msg_saver_menu_text" + + menu_list=" + '1 $msg_blank' '$msg_blank_desc' + '2 $msg_beastie' '$msg_beastie_desc' + '3 $msg_daemon' '$msg_daemon_desc' + '4 $msg_dragon' '$msg_dragon_desc' + '5 $msg_fade' '$msg_fade_desc' + '6 $msg_fire' '$msg_fire_desc' + '7 $msg_green' '$msg_green_desc' + '8 $msg_logo' '$msg_logo_desc' + '9 $msg_rain' '$msg_rain_desc' + 'a $msg_snake' '$msg_snake_desc' + 'b $msg_star' '$msg_star_desc' + 'c $msg_warp' '$msg_warp_desc' + 'd $msg_none' '$msg_none_saver_desc' + '$msg_timeout' '$msg_timeout_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_screen_saver" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "1 $msg_blank") # Simply blank the screen + f_sysrc_set saver "blank" || f_die + break ;; + "2 $msg_beastie") # "BSD Daemon" animated screen saver (graphics) + f_sysrc_set saver "beastie" || f_die + break ;; + "3 $msg_daemon") # "BSD Daemon" animated screen saver (text) + f_sysrc_set saver "daemon" || f_die + break ;; + "4 $msg_dragon") # Dragon screensaver (graphics) + f_sysrc_set saver "dragon" || f_die + break ;; + "5 $msg_fade") # Fade out effect screen saver + f_sysrc_set saver "fade" || f_die + break ;; + "6 $msg_fire") # Flames effect screen saver + f_sysrc_set saver "fire" || f_die + break ;; + "7 $msg_green") # "Green" power saving mode (if supported by monitor) + f_sysrc_set saver "green" || f_die + break ;; + "8 $msg_logo") # FreeBSD "logo" animated screen saver (graphics) + f_sysrc_set saver "logo" || f_die + break ;; + "9 $msg_rain") # Rain drops screen saver + f_sysrc_set saver "rain" || f_die + break ;; + "a $msg_snake") # Draw a FreeBSD "snake" on your screen + f_sysrc_set saver "snake" || f_die + break ;; + "b $msg_star") # A "twinkling stars" effect + f_sysrc_set saver "star" || f_die + break ;; + "c $msg_warp") # A "stars warping" effect + f_sysrc_set saver "warp" || f_die + break ;; + "d $msg_none") # Disable the screensaver + f_sysrc_set saver "NO" || f_die + break ;; + "$msg_timeout") # Set the screen saver timeout interval + f_dialog_title "$msg_value_required" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + prompt="$msg_enter_timeout_period" + blanktime=$( f_sysrc_get blanktime ) + hline="" + size=$( f_dialog_inputbox_size \ + "$title" \ + "$btitle" \ + "$prompt" \ + "$blanktime" \ + "$hline" ) + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --inputbox "$prompt" $size \ + "$blanktime" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + retval=$? + blanktime=$( f_dialog_inputstr ) + [ $retval -eq $SUCCESS ] && + f_sysrc_set blanktime "$blanktime" + ;; + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/console/screenmap b/usr.sbin/bsdconfig/console/screenmap new file mode 100755 index 0000000000000..9e10c9fe433a0 --- /dev/null +++ b/usr.sbin/bsdconfig/console/screenmap @@ -0,0 +1,142 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_choose_a_screen_map" + local prompt="$msg_screenmap_menu_text" + + menu_list=" + '1 $msg_none' '$msg_none_screenmap_desc' + '2 $msg_iso_8859_1_to_ibm437' '$msg_iso_8859_1_to_ibm437_desc' + '3 $msg_iso_8859_7_to_ibm437' '$msg_iso_8859_7_to_ibm437_desc' + '4 $msg_us_ascii_to_ibm327' '$msg_us_ascii_to_ibm327_desc' + '5 $msg_koi8_r_to_ibm866' '$msg_koi8_r_to_ibm866_desc' + '6 $msg_koi8_u_to_ibm866u' '$msg_koi8_u_to_ibm866u_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_screenmap" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "1 $msg_none") # No screenmap, don't touch font + f_sysrc_set scrnmap "NO" || f_die + break ;; + "2 $msg_iso_8859_1_to_ibm437") # W-Europe ISO 8859-1 to IBM 437 scrnmap + f_sysrc_set scrnmap "iso-8859-1_to_cp437" || f_die + break ;; + "3 $msg_iso_8859_7_to_ibm437") # Greek ISO 8859-7 to IBM 437 screenmap + f_sysrc_set scrnmap "iso-8859-7_to_cp437" || f_die + break ;; + "4 $msg_us_ascii_to_ibm327") # US-ASCII to IBM 437 screenmap + f_sysrc_set scrnmap "us-ascii_to_cp437" || f_die + break ;; + "5 $msg_koi8_r_to_ibm866") # Russian KOI8-R to IBM 866 screenmap + f_sysrc_set scrnmap "koi8-r2cp866" || f_die + break ;; + "6 $msg_koi8_u_to_ibm866u") # Ukrainian KOI8-U to IBM 866u screenmap + f_sysrc_set scrnmap "koi8-u2cp866u" || f_die + break ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys new file mode 100755 index 0000000000000..7502bece7e3c8 --- /dev/null +++ b/usr.sbin/bsdconfig/console/ttys @@ -0,0 +1,197 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="080.console" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Location of ttys(5) +# +ETC_TTYS=/etc/ttys + +############################################################ GLOBALS + +# +# Terminal-type map/menu-item list +# +TTY_MENU_LIST=" + '1 $msg_none' '$msg_none_ttys_desc' + '2 $msg_ibm_437_vga_default' 'cons25' + '3 $msg_iso_8859_1' 'cons25l1' + '4 $msg_iso_8859_2' 'cons25l2' + '5 $msg_iso_8859_7' 'cons25l7' + '6 $msg_koi8_r' 'cons25r' + '7 $msg_koi8_u' 'cons25u' + '8 $msg_us_ascii' 'cons25w' +" # END-QUOTE + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local size + local hline="$hline_choose_a_terminal_type" + local prompt="$msg_ttys_menu_text" + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $TTY_MENU_LIST ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $TTY_MENU_LIST \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# ttys_set_type $consterm +# +# Set terminal type of `ttyv*' and `cons[0-9]' entries in ttys(5) to $consterm. +# +ttys_set_type() +{ + local consterm="$1" + + # + # Create new temporary file to write our ttys(5) update with new types. + # + local tmpfile="$( mktemp -t "pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions and ownership (mktemp(1) creates the temporary file + # with 0600 permissions -- change the permissions and ownership to + # match ttys(5) before we write it out and mv(1) it into place). + # + local mode="$( stat -f '%#Lp' "$ETC_TTYS" 2> /dev/null )" + local owner="$( stat -f '%u:%g' "$ETC_TTYS" 2> /dev/null )" + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on ttys(5), replacing only the types of `ttyv*' and + # `cons[0-9]' terminals with the new type. + # + awk -v consterm="$consterm" ' + BEGIN { + } + { + # "Skip" blank-lines, lines containing only whitespace, and + # lines containing only a comment or whitespace-then-comment. + # + if ( $0 ~ /^[[:space:]]*(#|$)/ ) { print; next } + + # "Skip" terminal types other than those supported + # + if ( $1 !~ /^(ttyv.*|cons[0-9])$/ ) { print; next } + + # Change the terminal type to the new value + # + match($0, /[[:alnum:]\.\+-_]+[[:space:]]+(on|off).*$/) + if ( ! RSTART ) { print; next } + left = substr($0, 0, RSTART - 1) + match($0, /[[:space:]]+(on|off).*$/) + right = substr($0, RSTART) + printf "%s%s%s\n", left, consterm, right + } + ' "$ETC_TTYS" > "$tmpfile" || return $FAILURE + f_quietly mv "$tmpfile" "$ETC_TTYS" || return $FAILURE + + return $SUCCESS +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_console_terminal_type" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + [ "$mtag" = "1 $msg_none" ] && break + + consterm=$( eval f_dialog_menutag2item \"\$mtag\" $TTY_MENU_LIST ) + err=$( ttys_set_type "$consterm" 2>&1 ) + [ "$err" ] || break + + f_show_msg "%s" "$err" +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/diskmgmt/INDEX b/usr.sbin/bsdconfig/diskmgmt/INDEX new file mode 100644 index 0000000000000..2d03af83b8419 --- /dev/null +++ b/usr.sbin/bsdconfig/diskmgmt/INDEX @@ -0,0 +1,53 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Disk Management" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Manage disk partitions and/or labels" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="diskmgmt|diskmgmt" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="diskmgmt" diff --git a/usr.sbin/bsdconfig/diskmgmt/Makefile b/usr.sbin/bsdconfig/diskmgmt/Makefile new file mode 100644 index 0000000000000..894c62c17a084 --- /dev/null +++ b/usr.sbin/bsdconfig/diskmgmt/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/050.diskmgmt +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= diskmgmt + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/diskmgmt/USAGE b/usr.sbin/bsdconfig/diskmgmt/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/diskmgmt/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/diskmgmt/diskmgmt b/usr.sbin/bsdconfig/diskmgmt/diskmgmt new file mode 100755 index 0000000000000..6d3fbf0ff80d8 --- /dev/null +++ b/usr.sbin/bsdconfig/diskmgmt/diskmgmt @@ -0,0 +1,85 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="050.diskmgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# If X11 is requested, which terminal and what options should we use? +# +X11TERM=xterm +X11TERM_OPTS= + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_mustberoot_init + +# +# If Xdialog(1) is requested, we'll need to wrap bsdinstall(8) into xterm(1) +# +if [ "$USE_XDIALOG" ]; then + f_have "$X11TERM" || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "$X11TERM" + + exec $X11TERM $X11TERM_OPTS -e /usr/sbin/sade +else + exec /usr/sbin/sade +fi + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/diskmgmt/include/Makefile b/usr.sbin/bsdconfig/diskmgmt/include/Makefile new file mode 100644 index 0000000000000..9c94c0e15e884 --- /dev/null +++ b/usr.sbin/bsdconfig/diskmgmt/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/050.diskmgmt/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/diskmgmt/include/messages.subr b/usr.sbin/bsdconfig/diskmgmt/include/messages.subr new file mode 100644 index 0000000000000..f0b563f970a89 --- /dev/null +++ b/usr.sbin/bsdconfig/diskmgmt/include/messages.subr @@ -0,0 +1,27 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +msg_no_such_file_or_directory="%s: %s: No such file or directory" diff --git a/usr.sbin/bsdconfig/docsinstall/INDEX b/usr.sbin/bsdconfig/docsinstall/INDEX new file mode 100644 index 0000000000000..c61e2217aa858 --- /dev/null +++ b/usr.sbin/bsdconfig/docsinstall/INDEX @@ -0,0 +1,53 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Documentation installation" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Install FreeBSD Documentation set" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="docsinstall|docsinstall" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="docsinstall" diff --git a/usr.sbin/bsdconfig/docsinstall/Makefile b/usr.sbin/bsdconfig/docsinstall/Makefile new file mode 100644 index 0000000000000..de03d7d974d95 --- /dev/null +++ b/usr.sbin/bsdconfig/docsinstall/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/020.docsinstall +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= docsinstall + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/docsinstall/USAGE b/usr.sbin/bsdconfig/docsinstall/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/docsinstall/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/docsinstall/docsinstall b/usr.sbin/bsdconfig/docsinstall/docsinstall new file mode 100755 index 0000000000000..c0e11b6bdaf28 --- /dev/null +++ b/usr.sbin/bsdconfig/docsinstall/docsinstall @@ -0,0 +1,97 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="020.docsinstall" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# If X11 is requested, which terminal and what options should we use? +# +X11TERM=xterm +X11TERM_OPTS= + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_mustberoot_init + +# +# If Xdialog(1) is requested, we'll need to wrap bsdinstall(8) into xterm(1) +# +if [ "$USE_XDIALOG" ]; then + # + # Make sure $X11TERM exists and is executable + # + case "$X11TERM" in + */*) + [ -e "$X11TERM" ] || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "$X11TERM" + [ -x "$X11TERM" ] || f_die 1 \ + "$msg_permission_denied" "$pgm" "$X11TERM" + ;; + *) + f_have "$X11TERM" || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "$X11TERM" + esac + + exec $X11TERM $X11TERM_OPTS -e /usr/sbin/bsdinstall docsinstall +else + exec /usr/sbin/bsdinstall docsinstall +fi + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/docsinstall/include/Makefile b/usr.sbin/bsdconfig/docsinstall/include/Makefile new file mode 100644 index 0000000000000..9f75f7d213226 --- /dev/null +++ b/usr.sbin/bsdconfig/docsinstall/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/020.docsinstall/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/docsinstall/include/messages.subr b/usr.sbin/bsdconfig/docsinstall/include/messages.subr new file mode 100644 index 0000000000000..4c55f0fc078ae --- /dev/null +++ b/usr.sbin/bsdconfig/docsinstall/include/messages.subr @@ -0,0 +1,28 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +msg_no_such_file_or_directory="%s: %s: No such file or directory" +msg_permission_denied="%s: %s: permission denied" diff --git a/usr.sbin/bsdconfig/dot/INDEX b/usr.sbin/bsdconfig/dot/INDEX new file mode 100644 index 0000000000000..96b0c8738f2bf --- /dev/null +++ b/usr.sbin/bsdconfig/dot/INDEX @@ -0,0 +1,53 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="dot|dot" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="" diff --git a/usr.sbin/bsdconfig/dot/Makefile b/usr.sbin/bsdconfig/dot/Makefile new file mode 100644 index 0000000000000..5a87282c704f3 --- /dev/null +++ b/usr.sbin/bsdconfig/dot/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/dot +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= dot + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/dot/USAGE b/usr.sbin/bsdconfig/dot/USAGE new file mode 100644 index 0000000000000..c1c0dc3e3bf1d --- /dev/null +++ b/usr.sbin/bsdconfig/dot/USAGE @@ -0,0 +1,143 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -c Don't show command-line shortcut relationships. + -d Don't show the date in the graph label. + -i Don't show include relationships. + +EXAMPLES: + View dot(1) language output describing bsdconfig(8) layout/make-up: + + bsdconfig @PROGRAM_NAME@ | less + + Render dot(1) output in SVG format (displays in most modern browsers): + + bsdconfig @PROGRAM_NAME@ | dot -Tsvg -o bsdconfig.svg + + NOTE: Requires `graphics/graphviz' from ports/packages. + + View the above-rendered SVG file using your favorite X11-based viewer: + + gimmage bsdconfig.svg + + NOTE: Requires `graphics/gimmage' from ports/packages. + + or + + gthumb bsdconfig.svg + + NOTE: Image is scaled to fit window on launch. + NOTE: Requires `graphics/gthumb' from ports/packages. + + or + + gqview bsdconfig.svg + + NOTE: Requires `graphics/gqview' from ports/packages. + + or + + gx bsdconfig.svg + + NOTE: Image is scaled to fit window on launch. + NOTE: Requires `graphics/gx' from ports/packages. + + or + + eog bsdconfig.svg + + NOTE: Requires `graphics/eog' from ports/packages. + + Render dot(1) output as PostScript print output consisting of multiple + US-Letter sized pages that can be assembled into a large poster (using + traditional tools such as scissors and tape): + + bsdconfig @PROGRAM_NAME@ | dot -Teps -o bsdconfig.eps + poster -v -mLet -s1 -o bsdconfig.ps bsdconfig.eps + + NOTE: Change "-s1" above to "-s0.5" to halve the size of the + poster or "-s2", for example, to double the poster size. + + NOTE: Requires both `graphics/graphviz' and `print/poster' from + ports/packages. + + Render dot(1) output as PostScript scaled to fit on a poster consisting + of 2x-wide and 4x-tall US-Letter sized pages: + + bsdconfig @PROGRAM_NAME@ | dot -Teps -o bsdconfig.eps + poster -v -mLet -p2x4Letter -o bsdconfig.ps bsdconfig.eps + + NOTE: Requires both `graphics/graphviz' and `print/poster' from + ports/packages. + + View the above-rendered PostScript poster using X11: + + gsview bsdconfig.ps + + NOTE: Requires `print/gsview' from ports/packages. + + or + + convert bsdconfig.ps bsdconfig.pdf + xpdf bsdconfig.pdf + + NOTE: Requires both `graphics/ImageMagick' and + `graphics/xdpf' from ports/packages. + + NOTE: The converted PDF file is not suitable for + printing due to loss of quality during the + conversion process. + + Print the above-rendered PostScript poster: + + lpr -h bsdconfig.ps + + NOTE: Requires configuration of a printer in `/etc/printcap'. + + Extract each page of the poster into a separate PNG file: + + gs -q -dNOPAUSE -dBATCH -sPAPERSIZE=letter \ + -dTextAlphaBits=4 -dGraphicsAlphaBits=4 \ + -sDEVICE=png16m -sOutputFile=bsdconfig%03d.png \ + bsdconfig.ps + + NOTE: Requires `print/ghostscript9' from ports/packages. + + NOTE: The converted PNG files are not suitable for printing + due to loss of quality during the conversion process. + + Extract a single page of the poster into a separate PostScript file for + printing individual pages from the command-line: + + psselect 1 bsdconfig.ps bsdconfig-page1.ps + lpr -h bsdconfig-page1.ps + + NOTE: Change "1" to "2" for the second page, ad-infinitum. + NOTE: Requires `print/psutils-letter' from ports/packages. diff --git a/usr.sbin/bsdconfig/dot/dot b/usr.sbin/bsdconfig/dot/dot new file mode 100755 index 0000000000000..e1f0e1b6eb111 --- /dev/null +++ b/usr.sbin/bsdconfig/dot/dot @@ -0,0 +1,646 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include_lang $BSDCFG_LIBE/include/messages.subr + +APP_DIR="dot" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Locataion of bsdconfig(8) +# +BSDCONFIG=/usr/sbin/bsdconfig + +############################################################ GLOBALS + +# +# Options +# +SHOW_GRAPH_LABEL_DATE=1 +SHOW_INCLUDES=1 +SHOW_CMDLINE=1 + +############################################################ FUNCTIONS + +# begin_nodelist $shape $color $fillcolor $style +# +# Create a new multi-node list rendering nodes in a specific style described by +# the arguments passed. +# +begin_nodelist() +{ + local shape="$1" color="$2" fillcolor="$3" style="$4" + + printf "\tnode [\n" + [ "$shape" ] && + printf '\t\tshape = "%s",\n' "$shape" + [ "$color" ] && + printf '\t\tcolor = "%s",\n' "$color" + [ "$fillcolor" ] && + printf '\t\tfillcolor = "%s",\n' "$fillcolor" + [ "$style" ] && + printf '\t\tstyle = "%s",\n' "$style" + printf "\t] {\n" +} + +# print_node $node [$attributes ...] +# +# Print a node within a multi-node list. +# +print_node() +{ + local node="$1" + + shift 1 # node + + case "$node" in + edge) printf '\t\t%s' "$node";; + *) printf '\t\t"%s"' "$node";; + esac + + if [ $# -gt 0 ]; then + echo -n ' [' + while [ $# -gt 0 ]; do + printf " %s" "$1" + shift 1 + [ $# -gt 0 ] && echo -n "," + done + echo -n " ]" + fi + + echo ";" +} + +# print_node2 $node $node [$attributes ...] +# +# Print a directed node-node connection within a multi-node list. +# +print_node2() +{ + local node1="$1" node2="$2" + + shift 2 # node1 node2 + + printf '\t\t"%s" -> "%s"' "$node1" "$node2" + + if [ $# -gt 0 ]; then + echo -n ' [' + while [ $# -gt 0 ]; do + printf " %s" "$1" + shift 1 + [ $# -gt 0 ] && echo -n "," + done + echo -n " ]" + fi + + echo ";" +} + +# end_nodelist +# +# Close a multi-node list. +# +end_nodelist() +{ + printf "\t};\n" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts cdhi flag; do + case "$flag" in + i) SHOW_INCLUDES=;; + d) SHOW_GRAPH_LABEL_DATE=;; + c) SHOW_CMDLINE=;; + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +cd $BSDCFG_LIBE || f_die 1 "$msg_directory_not_found" "$BSDCFG_LIB" + +# +# Get a list of menu programs +# +menu_program_list= +for file in [0-9][0-9][0-9].*/INDEX; do + menu_program_list="$menu_program_list $( + tail -r "$file" | awk -v item="${file%%/*}" ' + /^[[:space:]]*menu_program="/ { + sub(/^.*="/, "") + sub(/"$/, "") + if ( ! $0 ) next + if ( $0 !~ "^/" ) sub(/^/, item "/") + print; exit + }' + )" +done + +# +# Get a list of submenu programs +# +submenu_program_list= +for menu_program in $menu_program_list; do + case "$menu_program" in + [0-9][0-9][0-9].*/*) : fall-through ;; + *) continue # No sub-menus we can process + esac + + submenu_program_list="$submenu_program_list $( + awk -v menu_program="$menu_program" \ + -v item="${menu_program%%/*}" \ + ' + /^menu_selection="/ { + sub(/.*\|/, "") + sub(/"$/, "") + if ( ! $0 ) next + if ( $0 !~ "^/" ) + sub(/^/, item "/") + if ( $0 == menu_program ) next + print + } + ' "${menu_program%%/*}/INDEX" + )" +done + +# +# Get a list of command-line programs +# +cmd_program_list= +for file in */INDEX; do + cmd_program_list="$cmd_program_list $( + awk -v item="${file%%/*}" ' + /^menu_selection="/ { + sub(/.*\|/, "") + sub(/"$/, "") + + if ( ! $0 ) next + + if ( $0 !~ "^/" ) + sub(/^/, item "/") + + print + } + ' $file + )" +done + +# +# [Optionally] Calculate list of include files +# +if [ "$SHOW_INCLUDES" ]; then + # + # Build list of files in which to search for includes + # + file_list=$( + for file in \ + $BSDCONFIG \ + $menu_program_list \ + $submenu_program_list \ + $cmd_program_list \ + ; do + [ -e "$file" ] && echo $file + done | sort -u + ) + + # + # Build list of includes used by the above files + # + include_file_list= + for file in $file_list; do + include_file_list="$include_file_list $( + awk -v file="$file" -v item="${file%%/*}" ' + BEGIN { regex = "^f_include \\$BSDCFG_LIBE/" } + ( $0 ~ regex "include/" ) { + sub(regex, "") + print + } + ( $0 ~ regex "\\$APP_DIR/include/" ) { + sub(regex "\\$APP_DIR", item) + print + } + ' $file + )" + done + + # + # Sort the list of includes and remove duplicate entries + # + include_file_list=$( + for include_file in $include_file_list; do + echo "$include_file" + done | sort -u + ) + + # + # Search previously-discovered include files for further includes + # + for file in $include_file_list; do + include_file_list="$include_file_list $( + awk -v file="$file" -v item="${file%%/*}" ' + BEGIN { regex = "^f_include \\$BSDCFG_LIBE/" } + ( $0 ~ regex "include/" ) { + sub(regex, "") + print + } + ( $0 ~ regex "\\$APP_DIR/include/" ) { + sub(regex "\\$APP_DIR", item) + print + } + ' $file + )" + done + + # + # Sort the list of includes and remove duplicate entries [again] + # + include_file_list=$( + for include_file in $include_file_list; do + echo "$include_file" + done | sort -u + ) +fi + +# +# Start the directional-graph (digraph) output +# +printf 'strict digraph "" { // Empty name to prevent SVG Auto-Tooltip\n' +label_format="$msg_graph_label_with_command" +[ "$SHOW_GRAPH_LABEL_DATE" ] && + label_format="$msg_graph_label_with_command_and_date" +lang="${LANG:-$LC_ALL}" +printf "\n\tlabel = \"$label_format\"\n" \ + "${lang:+LANG=${lang%%[$IFS]*} }bsdconfig $pgm${ARGV:+ $ARGV}" \ + "$( date +"%c %Z" )" + +# +# Print graph-specific properties +# +printf '\n\t/*\n\t * Graph setup and orientation\n\t */\n' +printf '\tlabelloc = top;\t\t// display above label at top of graph\n' +printf '\trankdir = LR;\t\t// create ranks left-to-right\n' +printf '\torientation = portrait;\t// default\n' +printf '\tratio = fill;\t\t// approximate aspect ratio\n' +printf '\tcenter = 1;\t\t// center drawing on page\n' + +# +# Perform edge-concentration when displaying a lot of information +# +# NOTE: This is disabled because dot version 2.28.0 (current) and older have a +# bug that causes a crash when rankdir = LR and concentrate = true +# +# NOTE: Do not re-enable until said bug is fixed in some future revision. +# +#[ "$SHOW_INCLUDES" -a "$SHOW_CMDLINE" ] && +# printf '\tconcentrate = true;\t// enable edge concentrators\n' + +# +# Print font details for graph/cluster label(s) +# +printf '\n\t/*\n\t * Font details for graph/cluster label(s)\n\t */\n' +printf '\tfontname = "Times-Italic";\n' +printf '\tfontsize = 14;\n' + +# +# Print default node attributes +# +printf '\n\t/*\n\t * Default node attributes\n\t */\n' +printf '\tnode [\n' +printf '\t\tfontname = "Times-Roman",\n' +printf '\t\tfontsize = 12,\n' +printf '\t\twidth = 2.5, // arbitrary minimum width for all nodes\n' +printf '\t\tfixedsize, // turn minimum width into exact width\n' +printf '\t];\n' + +# +# Print top-level item(s) +# +printf '\n\t/*\n\t * bsdconfig(8)\n\t */\n' +shape=circle color=black fillcolor=yellow style=filled +begin_nodelist "$shape" "$color" "$fillcolor" "$style" +print_node "bsdconfig" "fontname = \"Times-Bold\"" "fontsize = 16" +end_nodelist + +# +# Print menus +# +printf '\n\t/*\n\t * Menu items\n\t */\n' +shape=box color=black fillcolor=lightblue style=filled +begin_nodelist "$shape" "$color" "$fillcolor" "$style" +for menu_program in $menu_program_list; do + print_node "$menu_program" "label = \"${menu_program#*/}\"" +done +end_nodelist + +# +# Print sub-menus +# +printf '\n\t/*\n\t * Sub-menu items\n\t */\n' +shape=box color=black fillcolor=lightblue style=filled +begin_nodelist "$shape" "$color" "$fillcolor" "$style" +for submenu_program in $submenu_program_list; do + print_node "$submenu_program" "label = \"${submenu_program#*/}\"" +done +end_nodelist + +# +# Print menu relationships +# +printf '\n\t/*\n\t * Menu item relationships\n\t */\n' +shape=box color=black fillcolor=lightblue style=filled edge_color=blue +begin_nodelist "$shape" "$color" "$fillcolor" "$style" +print_node edge "penwidth = 5.0" "style = bold" "color = $edge_color" +for menu_program in $menu_program_list; do + print_node2 "bsdconfig" "$menu_program" +done +end_nodelist + +# +# Print sub-menu relationships +# +printf '\n\t/*\n\t * Sub-menu item relationships\n\t */\n' +shape=box color=black fillcolor=lightblue style=filled edge_color=blue +begin_nodelist "$shape" "$color" "$fillcolor" "$style" +# Lock sub-menu headport to the West (unless `-c' was passed) +[ "$SHOW_CMDLINE" -o ! "$SHOW_INCLUDES" ] && print_node edge "headport = w" +print_node edge "style = bold" "color = $edge_color" +for submenu_program in $submenu_program_list; do + for menu_program in $menu_program_list; do + case "$menu_program" in + [0-9][0-9][0-9].*/*) : fall-through ;; + *) continue # Not a menu item + esac + + # Continue if program directories do not match + [ "${menu_program%%/*}" = "${submenu_program%%/*}" ] || + continue + + print_node2 "$menu_program" "$submenu_program" + break + done +done +end_nodelist + +# +# [Optionally] Print include files +# +if [ "$SHOW_INCLUDES" ]; then + printf '\n\t/*\n\t * Include files\n\t */\n' + shape=oval color=black fillcolor=white style=filled + begin_nodelist "$shape" "$color" "$fillcolor" "$style" + printf '\t\tconstraint = false;\n' + for include_file in $include_file_list; do + print_node "$include_file" \ + "label = \"${include_file#*include/}\"" + done + end_nodelist +fi + +# +# [Optionally] Print f_include() usage/relationships +# +if [ "$SHOW_INCLUDES" ]; then + printf '\n\t/*\n\t * Include usage\n\t */\n' + shape=oval color=black fillcolor=white style=filled edge_color=grey + begin_nodelist "$shape" "$color" "$fillcolor" "$style" + print_node edge "style = dashed" "color = $edge_color" + file_list=$( + for file in \ + $BSDCONFIG \ + $menu_program_list \ + $submenu_program_list \ + $cmd_program_list \ + $include_file_list \ + ; do + [ -e "$file" ] && echo $file + done | sort -u + ) + for file in $file_list; do + # Skip binary files and text files that don't use f_include() + grep -qlI f_include $file || continue + + awk \ + -v file="$file" \ + -v item="${file%%/*}" \ + -v bsdconfig="$BSDCONFIG" \ + ' + BEGIN { regex = "^f_include \\$BSDCFG_LIBE/" } + ( $0 ~ regex "include/" ) { + sub(regex, "") + if ( file == bsdconfig ) sub(".*/", "", file) + printf "\t\t\"%s\" -> \"%s\";\n", $0, file + } + ( $0 ~ regex "\\$APP_DIR/include/" ) { + sub(regex "\\$APP_DIR", item) + if ( file == bsdconfig ) sub(".*/", "", file) + printf "\t\t\"%s\" -> \"%s\";\n", $0, file + } + ' $file + done | sort | awk ' + BEGIN { found = 0 } + { + # If already found or no-match... just spew + if ( found ) { print; next } + if ( $0 !~ /^[[:space:]]*"include\// ) { print; next } + printf "\t\tedge [ label = \"\\T\", fontsize = 9 ];\n" + print; found = 1 + }' + end_nodelist +fi + +# +# Print command-line shortcuts +# +if [ "$SHOW_CMDLINE" ]; then + printf '\n\t/*\n\t * Command-line shortcuts\n\t */\n' + shape=parallelogram color=black fillcolor=lightseagreen style=filled + begin_nodelist "$shape" "$color" "$fillcolor" "$style" + for file in */INDEX; do + awk -v item="${file%%/*}" ' + /^menu_selection="/ { + sub(/^.*="/, "") + sub(/\|.*/, "") + printf "\t\t\"bsdconfig %s\"", $0 + printf " [ label = \"%s\" ];\n", $0 + } + ' $file + done + end_nodelist +fi + +# +# Print command-line shortcut relationships +# +if [ "$SHOW_CMDLINE" ]; then + printf '\n\t/*\n\t * Command-line shortcut relationships\n\t */\n' + shape=box color=black fillcolor=lightseagreen style=filled + begin_nodelist "$shape" "$color" "$fillcolor" "$style" + print_node edge "headport = w" "weight = 100.0" + print_node edge "style = bold" "color = $fillcolor" + for file in */INDEX; do + awk -v item="${file%%/*}" \ + -v node_fillcolor="$node_fillcolor" \ + -v edge_color="$edge_color" \ + ' + /^menu_selection="/ { + sub(/^.*="/, "") + sub(/"$/, "") + + if ( ! $0 ) next + + split($0, menusel, "|") + if ( menusel[2] !~ "^/" ) + sub(/^/, item "/", menusel[2]) + + printf "\t\t\"bsdconfig %s\" -> \"%s\";\n", + menusel[1], menusel[2] + } + ' $file + done + end_nodelist +fi + +# +# Print clusters +# +bgcolor_bsdconfig="lightyellow" +bgcolor_includes="gray98" +bgcolor_menuitem="aliceblue" +bgcolor_shortcuts="honeydew" +printf '\n\t/*\n\t * Clusters\n\t */\n' +printf '\tsubgraph "cluster_bsdconfig" {\n' +printf '\t\tbgcolor = "%s";\n' "$bgcolor_bsdconfig" +printf '\t\tlabel = "bsdconfig(8)";\n' +printf '\t\ttooltip = "bsdconfig(8)";\n' +print_node "bsdconfig" +if [ "$SHOW_INCLUDES" ]; then + printf '\t\tsubgraph "cluster_bsdconfig_includes" {\n' + printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_includes" + printf '\t\t\tlabel = "%s";\n' "$msg_includes" + for include_file in $include_file_list; do + case "$include_file" in + include/*) printf '\t\t\t"%s";\n' "$include_file";; + esac + done + printf '\t\t};\n' +fi +end_nodelist +for INDEX in */INDEX; do + menu_title= + menu_help= + f_include_lang "$INDEX" + + item="${INDEX%%/*}" + printf '\tsubgraph "cluster_%s" {\n' "$item" + + case "$item" in + [0-9][0-9][0-9].*) bgcolor="$bgcolor_menuitem";; + *) bgcolor="$bgcolor_shortcuts" + esac + printf '\t\tbgcolor = "%s";\n' "$bgcolor" + if [ "$menu_title" ]; then + printf '\t\tlabel = "%s\\n\\"%s\\"";\n' "$item" "$menu_title" + else + printf '\t\tlabel = "%s";\n' "$item" + fi + printf '\t\ttooltip = "%s";\n' "${menu_help:-$item}" + + program_list=$( + for program in \ + $menu_program_list \ + $submenu_program_list \ + $cmd_program_list \ + ; do + echo "$program" + done | sort -u + ) + for program in $program_list; do + case "$program" in "$item"/*) + print_node "$program" "label = \"${program#*/}\"" + esac + done + + if [ "$SHOW_INCLUDES" ]; then + item_include_list= + [ -d "$item/include" ] && + item_include_list=$( find "$item/include" -type f ) + item_include_list=$( + for item_include in $item_include_list; do + for include_file in $include_file_list; do + [ "$item_include" = "$include_file" ] || + continue + echo "$item_include"; break + done + done + ) + if [ "$item_include_list" ]; then + printf '\t\tsubgraph "cluster_%s_includes" {\n' "$item" + printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_includes" + printf '\t\t\tlabel = "%s";\n' "$msg_includes" + fi + for item_include in $item_include_list; do + printf '\t\t\t"%s";\n' "$item_include" + done + [ "$item_include_list" ] && printf '\t\t};\n' + fi + + if [ "$SHOW_CMDLINE" ]; then + printf '\t\tsubgraph "cluster_%s_shortcuts" {\n' "$item" + printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_shortcuts" + printf '\t\t\tlabel = "%s";\n' "$msg_shortcuts" + awk '/^menu_selection="/ { + sub(/^.*="/, "") + sub(/\|.*/, "") + printf "\t\t\t\"bsdconfig %s\";\n", $0 + }' "$INDEX" + printf '\t\t};\n' + fi + + end_nodelist +done + +printf '\n};\n' + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/dot/include/Makefile b/usr.sbin/bsdconfig/dot/include/Makefile new file mode 100644 index 0000000000000..9bbdea0742b6f --- /dev/null +++ b/usr.sbin/bsdconfig/dot/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/dot/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/dot/include/messages.subr b/usr.sbin/bsdconfig/dot/include/messages.subr new file mode 100644 index 0000000000000..527f30885ab01 --- /dev/null +++ b/usr.sbin/bsdconfig/dot/include/messages.subr @@ -0,0 +1,30 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +msg_graph_label_with_command="bsdconfig(8)\\\ndot(1) output generated by \`\`%s''" +msg_graph_label_with_command_and_date="bsdconfig(8)\\\ndot(1) output generated by \`\`%s'' on\\\n%s" +msg_includes="Includes" +msg_shortcuts="Shortcuts" diff --git a/usr.sbin/bsdconfig/examples/Makefile b/usr.sbin/bsdconfig/examples/Makefile new file mode 100644 index 0000000000000..f072350d1c1c3 --- /dev/null +++ b/usr.sbin/bsdconfig/examples/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/examples/bsdconfig +FILES= bsdconfigrc + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/examples/bsdconfigrc b/usr.sbin/bsdconfig/examples/bsdconfigrc new file mode 100644 index 0000000000000..a863a94b1722b --- /dev/null +++ b/usr.sbin/bsdconfig/examples/bsdconfigrc @@ -0,0 +1,35 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# This file allows you to customize the behavior of bsdconfig. +# Copy it to your $HOME/.bsdconfigrc and edit to suit. +# defaults are shown. + +# debugging aid for development +# f_dprintf() { +# echo "$(date):$pgm: $1" >> $HOME/out +# } diff --git a/usr.sbin/bsdconfig/include/Makefile b/usr.sbin/bsdconfig/include/Makefile new file mode 100644 index 0000000000000..898359a97e8b1 --- /dev/null +++ b/usr.sbin/bsdconfig/include/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/include +FILES= common.subr dialog.subr messages.subr mustberoot.subr \ + strings.subr sysrc.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/include/common.subr b/usr.sbin/bsdconfig/include/common.subr new file mode 100644 index 0000000000000..c724c62f67008 --- /dev/null +++ b/usr.sbin/bsdconfig/include/common.subr @@ -0,0 +1,299 @@ +if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ GLOBALS + +# +# Program name +# +pgm="${0##*/}" + +# +# Program arguments +# +ARGC="$#" +ARGV="$@" + +# +# Global exit status variables +# +SUCCESS=0 +FAILURE=1 + +############################################################ FUNCTIONS + +# +# This is an empty function by default, to use it, copy +# /usr/share/examples/bsdconfig/bsdconfigrc to $HOME/.bsdconfigrc +# +f_dprintf() +{ + : this page intentionally left blank +} + +# f_err $fmt [ $opts ... ] +# +# Print a message to stderr (fd=2). +# +f_err() +{ + printf "$@" >&2 +} + +# f_quietly $command [ $arguments ... ] +# +# run a command quietly (quell any output to stdout or stderr) +# +f_quietly() +{ + "$@" > /dev/null 2>&1 +} + +# f_have $anything ... +# +# A wrapper to the `type' built-in. Returns true if argument is a valid shell +# built-in, keyword, or externally-tracked binary, otherwise false. +# +f_have() +{ + f_quietly type "$@" +} + +# f_die [ $status [ $fmt [ $opts ... ]]] +# +# Abruptly terminate due to an error optionally displaying a message in a +# dialog box using printf(1) syntax. +# +f_die() +{ + local status=$FAILURE + + # If there is at least one argument, take it as the status + if [ $# -gt 0 ]; then + status=$1 + shift 1 # status + fi + + # If there are still arguments left, pass them to f_show_msg + [ $# -gt 0 ] && f_show_msg "$@" + + # Optionally call f_clean_up() function if it exists + f_have f_clean_up && f_clean_up + + exit $status +} + +# f_interrupt +# +# Interrupt handler. +# +f_interrupt() +{ + exec 2>&1 # fix sh(1) bug where stderr gets lost within async-trap + f_die +} + +# f_show_msg $fmt [ $opts ... ] +# +# Display a message in a dialog box using printf(1) syntax. +# +f_show_msg() +{ + local msg + msg=$( printf "$@" ) + + # + # Use f_dialog_msgbox from dialog.subr if possible, otherwise fall + # back to dialog(1) (without options, making it obvious when using + # un-aided system dialog). + # + if f_have f_dialog_msgbox; then + f_dialog_msgbox "$msg" + else + dialog --msgbox "$msg" 0 0 + fi +} + +# f_include $file +# +# Include a shell subroutine file. +# +# If the subroutine file exists but returns error status during loading, exit +# is called and execution is prematurely terminated with the same error status. +# +f_include() +{ + local file="$1" + . "$file" || exit $? +} + +# f_include_lang $file +# +# Include a language file. Automatically takes $LANG and $LC_ALL into +# considerationg when including $file (suffix ".$LC_ALL" or ".$LANG" will +# automatically by added prior to loading the language file). +# +# No error is produced if (a) a language has been requested (by setting either +# $LANG or $LC_ALL in the environment) and (b) the language file does not +# exist -- in which case we will fall back to loading $file without-suffix. +# +# If the language file exists but returns error status during loading, exit +# is called and execution is prematurely terminated with the same error status. +# +f_include_lang() +{ + local file="$1" + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + if [ -f "$file.$lang" ]; then + . "$file.$lang" || exit $? + else + . "$file" || exit $? + fi +} + +# f_usage $file [ $key1 $value1 ... ] +# +# Display USAGE file with optional pre-processor macro definitions. The first +# argument is the template file containing the usage text to be displayed. If +# $LANG or $LC_ALL (in order of preference, respectively) is set, ".encoding" +# will automatically be appended as a suffix to the provided $file pathname. +# +# When processing $file, output begins at the first line containing that is +# (a) not a comment, (b) not empty, and (c) is not pure-whitespace. All lines +# appearing after this first-line are output, including (a) comments (b) empty +# lines, and (c) lines that are purely whitespace-only. +# +# If additional arguments appear after $file, substitutions are made while +# printing the contents of the USAGE file. The pre-processor macro syntax is in +# the style of autoconf(1), for example: +# +# f_usage $file "FOO" "BAR" +# +# Will cause instances of "@FOO@" appearing in $file to be replaced with the +# text "BAR" before bering printed to the screen. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_usage_awk=' +BEGIN { found = 0 } +{ + if ( !found && $0 ~ /^[[:space:]]*($|#)/ ) next + found = 1 + print +} +' +f_usage() +{ + local file="$1" + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + + shift 1 # file + + local usage + if [ -f "$file.$lang" ]; then + usage=$( awk "$f_usage_awk" "$file.$lang" ) || exit $FAILURE + else + usage=$( awk "$f_usage_awk" "$file" ) || exit $FAILURE + fi + + while [ $# -gt 0 ]; do + local key="$1" + export value="$2" + usage=$( echo "$usage" | awk \ + "{ gsub(/@$key@/, ENVIRON[\"value\"]); print }" ) + shift 2 + done + + f_err "%s\n" "$usage" + + exit $FAILURE +} + +# f_index_menu_selection $file $pgm +# +# Process $file looking for $menu_selection values that correspond to $pgm. +# This function is for internationalization (i18n) mapping of the on-disk +# scriptname ($pgm) into the localized language (given language-specific +# $file). If $LANG or $LC_ALL (in orderder of preference, respectively) is set, +# ".encoding" will automatically be appended as a suffix to the provided $file +# pathname. +# +# If, within $file, multiple $menu_selection values map to $pgm, only the first +# one will be returned. If no mapping can be made, the NULL string is returned. +# +# If $file does not exist, error status is returned along with the NULL string. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_index_menusel_awk=' +# Variables that should be defined on the invocation line: +# -v pgm="program_name" +# +( $0 ~ "^menu_selection=.*\\|" pgm "\"" ) { + sub(/\|.*/, "") + sub(/^menu_selection="/, "") + print + exit +} +' +f_index_menu_selection() +{ + local file="$1" pgm="$2" + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + + if [ -f "$file.$lang" ]; then + awk -v pgm="$pgm" "$f_index_menusel_awk" "$file.$lang" || + exit $FAILURE + elif [ -f "$file" ]; then + awk -v pgm="$pgm" "$f_index_menusel_awk" "$file" || + exit $FAILURE + else + return $FAILURE + fi +} + +############################################################ MAIN + +# +# Trap signals so we can recover gracefully +# +trap 'f_interrupt' SIGINT +trap 'f_die' SIGTERM SIGPIPE SIGXCPU SIGXFSZ \ + SIGFPE SIGTRAP SIGABRT SIGSEGV +trap '' SIGALRM SIGPROF SIGUSR1 SIGUSR2 SIGHUP SIGVTALRM + +fi # ! $_COMMON_SUBR diff --git a/usr.sbin/bsdconfig/include/dialog.subr b/usr.sbin/bsdconfig/include/dialog.subr new file mode 100644 index 0000000000000..35c71cbd88110 --- /dev/null +++ b/usr.sbin/bsdconfig/include/dialog.subr @@ -0,0 +1,1441 @@ +if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/strings.subr +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION + +# +# Default directory to store dialog(1) temporary files +# +: ${DIALOG_TMPDIR:="/tmp"} + +############################################################ GLOBALS + +# +# Default name of dialog(1) utility +# NOTE: This is changed to "Xdialog" by the optional `-X' argument +# +DIALOG="dialog" + +# +# Default dialog(1) title and backtitle text +# +DIALOG_TITLE="$pgm" +DIALOG_BACKTITLE="bsdconfig" + +# +# Settings used while interacting with dialog(1) +# +DIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz" + +# +# Declare that we are fully-compliant with Xdialog(1) by unset'ing all +# compatibility settings. +# +unset XDIALOG_HIGH_DIALOG_COMPAT +unset XDIALOG_FORCE_AUTOSIZE +unset XDIALOG_INFOBOX_TIMEOUT + +# +# Default behavior is to call f_dialog_init() automatically if not already +# called manually by the time the first f_dialog_*() function is used. +# +: ${DIALOG_SELF_INITIALIZE=1} + +############################################################ GENERIC FUNCTIONS + +# f_dialog_title [$new_title] +# +# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1) +# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first +# argument is NULL, the current title is returned. +# +# Each time this function is called, a backup of the current values is made +# allowing a one-time (single-level) restoration of the previous title using the +# f_dialog_title_restore() function (below). +# +f_dialog_title() +{ + local new_title="$1" + + if [ "$new_title" ]; then + if [ "$USE_XDIALOG" ]; then + _DIALOG_BACKTITLE="$DIALOG_BACKTITLE" + DIALOG_BACKTITLE="$new_title" + else + _DIALOG_TITLE="$DIALOG_TITLE" + DIALOG_TITLE="$new_title" + fi + else + if [ "$USE_XDIALOG" ]; then + echo "$DIALOG_BACKTITLE" + else + echo "$DIALOG_TITLE" + fi + fi +} + +# f_dialog_title_restore +# +# Restore the previous title set by the last call to f_dialog_title(). +# Restoration is non-recursive and only works to restore the most-recent title. +# +f_dialog_title_restore() +{ + if [ "$USE_XDIALOG" ]; then + DIALOG_BACKTITLE="$_DIALOG_BACKTITLE" + else + DIALOG_TITLE="$_DIALOG_TITLE" + fi +} + +# f_dialog_backtitle [$new_backtitle] +# +# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of +# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the +# first argument is NULL, the current backtitle is returned. +# +f_dialog_backtitle() +{ + local new_backtitle="$1" + + if [ "$new_backtitle" ]; then + if [ "$USE_XDIALOG" ]; then + _DIALOG_TITLE="$DIALOG_TITLE" + DIALOG_TITLE="$new_backtitle" + else + _DIALOG_BACKTITLE="$DIALOG_BACKTITLE" + DIALOG_BACKTITLE="$new_backtitle" + fi + else + if [ "$USE_XDIALOG" ]; then + echo "$DIALOG_TITLE" + else + echo "$DIALOG_BACKTITLE" + fi + fi +} + +# f_dialog_backtitle_restore +# +# Restore the previous backtitle set by the last call to f_dialog_backtitle(). +# Restoration is non-recursive and only works to restore the most-recent +# backtitle. +# +f_dialog_backtitle_restore() +{ + if [ "$USE_XDIALOG" ]; then + DIALOG_TITLE="$_DIALOG_TITLE" + else + DIALOG_BACKTITLE="$_DIALOG_BACKTITLE" + fi +} + +############################################################ SIZE FUNCTIONS + +# f_dialog_infobox_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--infobox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width or height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_infobox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + max_size=$( stty size ) # usually "24 80" + fi + + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + local height width=$min_width + + # + # Bump width for long titles (but don't exceed terminal width). + # + n=$(( ${#title} + 4 )) + if [ $n -gt $width -a $n -gt $min_width ]; then + # Add 16.6% width for Xdialog(1) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) + + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # + # For Xdialog(1), bump width for long backtitles (which appear within + # the window; don't exceed maximum width). + # + if [ "$USE_XDIALOG" ]; then + n=$(( ${#btitle} + 4 )) + n=$(( $n + $n / 6 )) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # + # Bump width for long prompts (if not already at maximum width). + # + if [ $width -lt $max_width ]; then + n=$( echo "$prompt" | f_longest_line_length ) + n=$(( $n + 4 )) + + # Add 16.6% width for Xdialog(1) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # + # Bump width for long hlines (if not already at maximum width). + # NOTE: Though Xdialog(1) supports `--hline', it's not currently used. + # + if [ ! "$USE_XDIALOG" ]; then + if [ $width -lt $max_width ]; then + n=$(( ${#hline} + 10 )) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + fi + + # + # Set height based on number of rows in prompt + # + height=$( echo "$prompt" | f_number_of_lines ) + height=$(( $height + 2 )) + + # + # For Xdialog(1) bump height if backtitle is enabled (displayed in the + # X11 window with a separator line between the backtitle and msg text) + # + if [ "$USE_XDIALOG" -a "$btitle" ]; then + n=$( echo "$btitle" | f_number_of_lines ) + height=$(( $height + $n + 2 )) + fi + + # Make sure height is less than maximum screen size + [ $height -le $max_height ] || height=$max_height + + # Return both + echo "$height $width" +} + +# f_dialog_buttonbox_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--msgbox' and `--yesno' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width or height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_buttonbox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" + local size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + # Add height to accomodate the buttons + height=$(( $height + 2 )) + + # Adjust for clipping with Xdialog(1) on Linux/GTK2 + [ "$USE_XDIALOG" ] && height=$(( $height + 3 )) + + # + # Enforce maximum height regardless + # + local max_size + if [ "$USE_XDIALOG" ]; then + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + [ $height -le $max_height ] || height=$max_height + + # Return both + echo "$height $width" +} + +# f_dialog_inputbox_size $title $backtitle $prompt $init [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--inputbox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, initial text, and [optionally] +# hline returning the optimal width and height for the box (not exceeding the +# actual terminal width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_inputbox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" init="$4" hline="$5" n + local size="$( f_dialog_buttonbox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width max_size + if [ "$USE_XDIALOG" ]; then + min_width=35 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # + # Add height to accomodate the input box + # + [ ! "$USE_XDIALOG" ] && height=$(( $height + 3 )) + [ $height -le $max_height ] || height=$max_height + + # + # Bump width for initial text (if not already at maximum width). + # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it! + # + if [ $width -lt $max_width ]; then + n=$(( ${#init} + 7 )) + + # Add 16.6% width for Xdialog(1) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Return both + echo "$height $width" +} + +# f_xdialog_2inputsbox_size $title $backtitle $prompt \ +# $label1 $init1 $label2 $init2 +# +# Xdialog(1) does not perform auto-sizing of the width and height of +# `--2inputsbox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, label for the first field, initial +# text for said field, label for the second field, and initial text for said +# field returning the optimal width and height for the box (not exceeding the +# actual terminal width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# Xdialog(1). +# +# Output is in the format of "height width". +# +f_xdialog_2inputsbox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" + local label1="$4" init1="$5" label2="$6" init2="$7" n + local size="$( f_dialog_inputbox_size \ + "$title" "$btitle" "$prompt" "$init1" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width=35 + local max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # Add height for first label + height=$(( $height + 2 )) + + # + # Bump width for first label text (if not already at maximum width). + # + if [ $width -lt $max_width ]; then + n=$(( ${#label1} + 7 )) + + # Add 16.6% width for Xdialog(1) + n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Add height for second label + height=$(( $height + 2 )) + + # + # Bump width for second label text (if not already at maximum width). + # + if [ $width -lt $max_width ]; then + n=$(( ${#label2} + 7 )) + + # Add 16.6% width for Xdialog(1) + n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Add height for a second inputbox + height=$(( $height + 2 )) + + # + # Bump width for second initial text (if not already at maximum width). + # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it! + # + if [ $width -lt $max_width ]; then + n=$(( ${#init2} + 7 )) + + # Add 16.6% width for Xdialog(1) + n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Return both + echo "$height $width" +} + +# f_dialog_menu_size $title $backtitle $prompt $hline \ +# $tag1 $item1 $tag2 $item2 ... +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--menu' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, hline and list of tag/item pairs, +# returning the optimal width and height for the menu (not exceeding the actual +# terminal width or height). +# +# Output is in the format of "height width rows". +# +f_dialog_menu_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width min_rows max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + min_rows=1 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + min_rows=0 + max_size=$( stty size ) # usually "24 80" + fi + + local max_width="${max_size##*[$IFS]}" + local max_height="${max_size%%[$IFS]*}" + local box_size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local box_height="${box_size%%[$IFS]*}" + local box_width="${box_size##*[$IFS]}" + local max_rows=$(( $max_height - 8 )) + local height width=$box_width rows=$min_rows + + shift 4 # title/btitle/prompt/hline + + # If there's no prompt, bump the max-rows by 1 + [ "$prompt" ] || max_rows=$(( $max_rows + 1 )) + + # + # The sum total between the longest tag-length and longest item-length + # should be used for the menu width (not to exceed terminal width). + # + # Also, calculate the number of rows (not to exceed terminal height). + # + local longest_tag=0 longest_item=0 + while [ $# -ge 2 ]; do + local tag="$1" item="$2" + shift 2 # tag/item + + [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} + [ ${#item} -gt $longest_item ] && longest_item=${#item} + [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) + done + + # Update width + n=$(( $longest_tag + $longest_item + 10 )) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # Fix rows and set height + [ $rows -gt 0 ] || rows=1 + if [ "$USE_XDIALOG" ]; then + height=$(( $rows + $box_height + 7 )) + else + height=$(( $rows + $box_height + 4 )) + fi + [ $height -le $max_height ] || height=$max_height + + # Return all three + echo "$height $width $rows" +} + +# f_dialog_menu_with_help_size $title $backtitle $prompt $hline \ +# $tag1 $item1 $help1 $tag2 $item2 $help2 ... +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--menu' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, hline and list of tag/item/help +# triplets, returning the optimal width and height for the menu (not exceeding +# the actual terminal width or height). +# +# Output is in the format of "height width rows". +# +f_dialog_menu_with_help_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width min_rows max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + min_rows=1 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + min_rows=0 + max_size=$( stty size ) # usually "24 80" + fi + + local max_width="${max_size##*[$IFS]}" + local max_height="${max_size%%[$IFS]*}" + local box_size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local box_height="${box_size%%[$IFS]*}" + local box_width="${box_size##*[$IFS]}" + local max_rows=$(( $max_height - 8 )) + local height width=$box_width rows=$min_rows + + shift 4 # title/btitle/prompt/hline + + # If there's no prompt, bump the max-rows by 1 + [ "$prompt" ] || max_rows=$(( $max_rows + 1 )) + + # + # The sum total between the longest tag-length and longest item-length + # should be used for the menu width (not to exceed terminal width). + # + # Also, calculate the number of rows (not to exceed terminal height). + # + # Also, calculate the longest help while we're here. This will be used + # to influence the width of the menu if (and only-if) using Xdialog(1). + # + local longest_tag=0 longest_item=0 longest_help=0 + while [ $# -ge 3 ]; do + local tag="$1" item="$2" help="$3" + shift 3 # tag/item/help + + [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} + [ ${#item} -gt $longest_item ] && longest_item=${#item} + [ ${#help} -gt $longest_help ] && longest_help=${#help} + [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) + done + + # Update width + n=$(( $longest_tag + $longest_item + 10 )) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # Update width for help text if using Xdialog(1) + if [ "$USE_XDIALOG" ]; then + n=$(( $longest_help + 10 )) + n=$(( $n + $n / 6 )) # +16.6% + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Fix rows and set height + [ $rows -gt 0 ] || rows=1 + if [ "$USE_XDIALOG" ]; then + height=$(( $rows + $box_height + 8 )) + else + height=$(( $rows + $box_height + 4 )) + fi + [ $height -le $max_height ] || height=$max_height + + # Return all three + echo "$height $width $rows" +} + +# f_dialog_radiolist_size $title $backtitle $prompt $hline \ +# $tag1 $item1 $status1 $tag2 $item2 $status2 ... +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--radiolist' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, hline and list of tag/item/status +# triplets, returning the optimal width and height for the radiolist (not +# exceeding the actual terminal width or height). +# +# Output is in the format of "height width rows". +# +f_dialog_radiolist_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width min_rows max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + min_rows=1 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + min_rows=0 + max_size=$( stty size ) # usually "24 80" + fi + + local max_width="${max_size##*[$IFS]}" + local max_height="${max_size%%[$IFS]*}" + local box_size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local box_height="${box_size%%[$IFS]*}" + local box_width="${box_size##*[$IFS]}" + local max_rows=$(( $max_height - 8 )) + local height width=$box_width rows=$min_rows + + shift 4 # title/btitle/prompt/hline + + # + # The sum total between the longest tag-length, longest item-length, + # and radio-button width should be used for the menu width (not to + # exceed terminal width). + # + # Also, calculate the number of rows (not to exceed terminal height). + # + local longest_tag=0 longest_item=0 + while [ $# -ge 2 ]; do + local tag="$1" item="$2" help="$3" + shift 3 # tag/item/status + + [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} + [ ${#item} -gt $longest_item ] && longest_item=${#item} + [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) + done + + # Update width + n=$(( $longest_tag + $longest_item + 13 )) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # Fix rows and set height + [ $rows -gt 0 ] || rows=1 + if [ "$USE_XDIALOG" ]; then + height=$(( $rows + $box_height + 7 )) + else + height=$(( $rows + $box_height + 4 )) + fi + [ $height -le $max_height ] || height=$max_height + + # Return all three + echo "$height $width $rows" +} + +# f_dialog_calendar_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--calendar' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_calendar_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n + local size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width min_height max_size + if [ "$USE_XDIALOG" ]; then + min_height=15 + min_width=55 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_height=0 + min_width=40 + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # + # Enforce the minimum width for displaying the calendar + # + [ $width -ge $min_width ] || width=$min_width + + # + # When using dialog(1), the calendar box is unique from other dialog(1) + # boxes in-that the height passed should not accomodate the 15-lines + # required to display the calendar. This does not apply to Xdialog(1). + # + # When using Xdialog(1), the height must accomodate the 15-lines + # required to display the calendar. + # + # NOTE: Also under dialog(1), because we can't predict whether the user + # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract + # 16 rather than 15. This does not apply to Xdialog(1). + # + max_height=$(( $max_height - 16 )) + height=$( echo "$prompt" | f_number_of_lines ) + if [ "$USE_XDIALOG" ]; then + # Add height to accomodate for the embedded calendar widget + height=$(( $height + $min_height - 1 )) + + # Also, bump height if backtitle is enabled + if [ "$btitle" ]; then + local n="$( echo "$btitle" | f_number_of_lines )" + height=$(( $height + $n + 2 )) + fi + else + [ "$prompt" ] && height=$(( $height + 1 )) + fi + [ $height -le $max_height ] || height=$max_height + + # + # The calendar box refuses to display if too large. + # + max_width=$(( $max_width - 2 )) + [ $width -le $max_width ] || width=$max_width + + # Return both + echo "$height $width" +} + +# f_dialog_timebox_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--timebox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_timebox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n + local size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width min_height max_size + if [ "$USE_XDIALOG" ]; then + min_width=40 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_height=0 + min_width=20 + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # + # Enforce the minimum width for displaying the timebox + # + [ $width -ge $min_width ] || width=$min_width + + # + # When using dialog(1), the timebox box is unique from other dialog(1) + # boxes in-that the height passed should not accomodate the 6-lines + # required to display the timebox. This does not apply to Xdialog(1). + # + # When using Xdialog(1), the height seems to have no effect. All values + # provide the same results. + # + # NOTE: Also under dialog(1), because we can't predict whether the user + # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract + # 7 rather than 6. This does not apply to Xdialog(1). + # + if [ "$USE_XDIALOG" ]; then + height=0 # Autosize; all values produce same results + else + max_height=$(( $max_height - 7 )) + height=$( echo "$prompt" | f_number_of_lines ) + height=$(( $height + 1 )) + [ $height -le $max_height ] || height=$max_height + [ "$prompt" ] && height=$(( $height + 1 )) + fi + + # + # The timebox box refuses to display if too large. + # + max_width=$(( $max_width - 2 )) + [ $width -le $max_width ] || width=$max_width + + # Return both + echo "$height $width" +} + +############################################################ CLEAR FUNCTIONS + +# f_dialog_clear +# +# Clears any/all previous dialog(1) displays. +# +f_dialog_clear() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + $DIALOG --clear +} + +############################################################ INFO FUNCTIONS + +# f_dialog_info $info_text ... +# +# Throw up a dialog(1) infobox. The infobox remains until another dialog is +# displayed or `dialog --clear' (or dialog_clear) is called. +# +f_dialog_info() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local info_text="$*" + local size="$( f_dialog_infobox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$info_text" )" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + ${USE_XDIALOG:+--ignore-eof} \ + ${USE_XDIALOG:+--no-buttons} \ + --infobox \"\$info_text\" $size +} + +# f_xdialog_info $info_text ... +# +# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces +# EOF. This implies that you must execute this either as an rvalue to a pipe, +# lvalue to indirection or in a sub-shell that provides data on stdin. +# +f_xdialog_info() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local info_text="$*" + local size="$( f_dialog_infobox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$info_text" )" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --no-close --no-buttons \ + --infobox \"\$info_text\" $size \ + -1 # timeout of -1 means abort when EOF on stdin +} + +############################################################ MSGBOX FUNCTIONS + +# f_dialog_msgbox $msg_text ... +# +# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER +# or ESC, acknowledging the modal dialog. +# +# If the user presses ENTER, the exit status is zero (success), otherwise if +# the user presses ESC the exit status is 255. +# +f_dialog_msgbox() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local size="$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg_text" )" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg_text\" $size +} + +############################################################ YESNO FUNCTIONS + +# f_dialog_yesno $msg_text ... +# +# Display a dialog(1) Yes/No prompt to allow the user to make some decision. +# The yesno prompt remains until the user presses ENTER or ESC, acknowledging +# the modal dialog. +# +# If the user chooses YES the exit status is zero, or chooses NO the exit +# status is one, or presses ESC the exit status is 255. +# +f_dialog_yesno() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local hline="$hline_arrows_tab_enter" + local size="$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg_text" \ + "$hline" )" + + if [ "$USE_XDIALOG" ]; then + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_yes\" \ + --cancel-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + else + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --yes-label \"\$msg_yes\" \ + --no-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + fi +} + +# f_dialog_noyes $msg_text ... +# +# Display a dialog(1) No/Yes prompt to allow the user to make some decision. +# The noyes prompt remains until the user presses ENTER or ESC, acknowledging +# the modal dialog. +# +# If the user chooses YES the exit status is zero, or chooses NO the exit +# status is one, or presses ESC the exit status is 255. +# +# NOTE: This is just like the f_dialog_yesno function except "No" is default. +# +f_dialog_noyes() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local hline="$hline_arrows_tab_enter" + local size="$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg_text" \ + "$hline" )" + + if [ "$USE_XDIALOG" ]; then + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --default-no \ + --ok-label \"\$msg_yes\" \ + --cancel-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + else + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --defaultno \ + --yes-label \"\$msg_yes\" \ + --no-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + fi +} + +############################################################ INPUT FUNCTIONS + +# f_dialog_inputstr +# +# Obtain the inputstr entered by the user from the most recently displayed +# dialog(1) inputbox and clean up any temporary files. +# +f_dialog_inputstr() +{ + local tmpfile="$DIALOG_TMPDIR/dialog.inputbox.$$" + + [ -f "$tmpfile" ] || return $FAILURE + + # Skip warnings and trim leading/trailing whitespace from user input + awk ' + BEGIN { found = 0 } + { + if ( ! found ) + { + if ( $0 ~ /^$/ ) next + if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next + found = 1 + } + sub(/^[[:space:]]*/, "") + sub(/[[:space:]]*$/, "") + print + } + ' "$tmpfile" 2> /dev/null + f_quietly rm -f "$tmpfile" + + return $SUCCESS +} + +############################################################ MENU FUNCTIONS + +# f_dialog_menutag +# +# Obtain the menutag chosen by the user from the most recently displayed +# dialog(1) menu and clean up any temporary files. +# +f_dialog_menutag() +{ + local tmpfile="$DIALOG_TMPDIR/dialog.menu.$$" + + [ -f "$tmpfile" ] || return $FAILURE + + awk ' + BEGIN { found = 0 } + { + if ( found ) # ... just spew + { + print + next + } + if ( $0 ~ /^$/ ) next + if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next + found = 1 + print + } + ' "$tmpfile" 2> /dev/null + f_quietly rm -f "$tmpfile" + + return $SUCCESS +} + +# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ... +# +# To use the `--menu' option of dialog(1) you must pass an ordered list of +# tag/item pairs on the command-line. When the user selects a menu option the +# tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the item associated with said tag. +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item pairs (HINT: use the same tag/item list as was +# passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2item() +{ + local tag="$1" tagn item + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + item="$2" + shift 2 # tagn/item + + if [ "$tag" = "$tagn" ]; then + echo "$item" + return $SUCCESS + fi + done + return $FAILURE +} + +# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \ +# $tag2 $item2 $help2 ... +# +# To use the `--menu' option of dialog(1) with the `--item-help' option, you +# must pass an ordered list of tag/item/help triplets on the command-line. When +# the user selects a menu option the tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the item associated with said tag (help is discarded/ignored). +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list +# as was passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2item_with_help() +{ + local tag="$1" tagn item + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + item="$2" + shift 3 # tagn/item/help + + if [ "$tag" = "$tagn" ]; then + echo "$item" + return $SUCCESS + fi + done + return $FAILURE +} + +# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ... +# +# To use the `--menu' option of dialog(1) you must pass an ordered list of +# tag/item pairs on the command-line. When the user selects a menu option the +# tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the index associated with said tag. The index is the one-based tag/item pair +# array position within the ordered list of tag/item pairs passed to dialog(1). +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item pairs (HINT: use the same tag/item list as was +# passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2index() +{ + local tag="$1" tagn n=1 + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + shift 2 # tagn/item + + if [ "$tag" = "$tagn" ]; then + echo $n + return $SUCCESS + fi + n=$(( $n + 1 )) + done + return $FAILURE +} + +# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \ +# $tag2 $item2 $help2 ... +# +# To use the `--menu' option of dialog(1) with the `--item-help' option, you +# must pass an ordered list of tag/item/help triplets on the command-line. When +# the user selects a menu option the tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the index associated with said tag. The index is the one-based tag/item/help +# triplet array position within the ordered list of tag/item/help triplets +# passed to dialog(1). +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list +# as was passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2index_with_help() +{ + local tag="$1" tagn n=1 + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + shift 3 # tagn/item/help + + if [ "$tag" = "$tagn" ]; then + echo $n + return $SUCCESS + fi + n=$(( $n + 1 )) + done + return $FAILURE +} + +############################################################ INIT FUNCTIONS + +# f_dialog_init +# +# Initialize (or re-initialize) the dialog module after setting/changing any +# of the following environment variables: +# +# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate +# that Xdialog(1) should be used instead of dialog(1). +# +# SECURE Either NULL or Non-NULL. If given a value will indicate +# that (while running as root) sudo(8) authentication is +# required to proceed. +# +f_dialog_init() +{ + DIALOG_SELF_INITIALIZE= + + # + # Process stored command-line arguments + # + SECURE=$( set -- "$ARGV" + while getopts S flag > /dev/null; do + case "$flag" in + S) echo 1;; + \?) continue;; + esac + done + ) + USE_XDIALOG=$( set -- "$ARGV" + while getopts SX flag > /dev/null; do + case "$flag" in + S|X) echo 1;; + \?) continue;; + esac + done + ) + + # + # Process `-X' command-line option + # + [ "$USE_XDIALOG" ] && DIALOG=Xdialog + + # + # Sanity check, or die gracefully + # + if ! f_have $DIALOG; then + unset USE_XDIALOG + failed_dialog="$DIALOG" + DIALOG=dialog + f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog" + fi + + # + # If we're already running as root but we got there by way of sudo(8) + # and we have X11, we should merge the xauth(1) credentials from our + # original user. + # + if [ "$USE_XDIALOG" ] && + [ "$( id -u )" = "0" ] && + [ "$SUDO_USER" -a "$DISPLAY" ] + then + if ! f_have xauth; then + # Die gracefully, as we [likely] can't use Xdialog(1) + unset USE_XDIALOG + DIALOG=dialog + f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth" + fi + HOSTNAME=$(hostname) + displaynum="${DISPLAY#*:}" + eval xauth -if \~$SUDO_USER/.Xauthority extract - \ + \"\$HOSTNAME/unix:\$displaynum\" \ + \"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \ + ~root/.Xauthority merge - > /dev/null 2>&1' + fi + + # + # Probe Xdialog(1) for maximum height/width constraints, or die + # gracefully + # + if [ "$USE_XDIALOG" ]; then + if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 ) + then + # Xdialog(1) failed, fall back to dialog(1) + unset USE_XDIALOG + size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$maxsize" "" ) + eval dialog \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$maxsize\" $size + exit $FAILURE + fi + + XDIALOG_MAXSIZE=$( + set -- ${maxsize##*:} + + height=${1%,} + width=$2 + + echo $height $width + ) + unset maxsize + fi + + # + # If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE. + # The reason for this is because many dialog(1) applications use + # --backtitle for the program name (which is better suited as + # --title with Xdialog(1)). + # + if [ "$USE_XDIALOG" ]; then + _DIALOG_TITLE="$DIALOG_TITLE" + DIALOG_TITLE="$DIALOG_BACKTITLE" + DIALOG_BACKTITLE="$_DIALOG_TITLE" + unset _DIALOG_TITLE + fi +} + +############################################################ CLEAN-UP FUNCTIONS + +# f_clean_up +# +# Clean-up routines (run when script exits or is killed). +# +f_clean_up() +{ + f_quietly rm -f "$DIALOG_TMPDIR"/dialog.*.$$ +} + +############################################################ MAIN + +# +# Trap signals so we can recover gracefully +# +trap 'f_clean_up' EXIT + +fi # ! $_DIALOG_SUBR diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr new file mode 100644 index 0000000000000..282d9369c4bf5 --- /dev/null +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -0,0 +1,56 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +field_username="Username:" +field_password="Password:" +hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER" +hline_arrows_tab_enter="Press arrows, TAB or ENTER" +msg_cancel="Cancel" +msg_cannot_create_permission_denied="%s: cannot create %s: Permission denied" +msg_directory_not_found="%s: Directory not found." +msg_exit="Exit" +msg_exit_bsdconfig="Exit bsdconfig" +msg_help_text="This menu allows you to configure your system after the installation\nprocess is complete. At the minimum, you should probably set the root\npassword and the system time zone.\n\nFor extra goodies like bash, emacs, firefox, etc., you should look at the\n'Packages' item in this menu.\n\nFor setting the timezone after the system is installed, use the 'Time\nZone' item in this menu.\n\nFor more information on the overall general system configuration, see the\n/etc/rc.conf and /etc/defaults/rc.conf files. (Note: to inhibit displaying\nof this message, create an empty file: \$HOME/.bsdconfigrc or copy it from\n/usr/share/examples/bsdconfig/bsdconfigrc)" +msg_main_menu="Main Menu" +msg_menu_text="If you've already installed FreeBSD, you may use\nthis menu to customize it somewhat to suit your\nparticular configuration. Most importantly, you\ncan use the Packages utility to load extra '3rd\nparty' software not provided in the base\ndistributions." +msg_must_be_root_to_execute="%s: must be root to execute" +msg_nfailed_attempts="%u incorrect password attempts" +msg_no="No" +msg_no_such_file_or_directory="%s: %s: No such file or directory" +msg_no_username="No username provided!" +msg_not_found="not found" +msg_ok="OK" +msg_permission_denied="%s: %s: permission denied" +msg_please_enter_password="Please enter your password for sudo(8):" +msg_please_enter_username_password="Please enter a username and password for sudo(8):" +msg_previous_syntax_errors="%s: Not overwriting \`%s' due to previous syntax errors" +msg_secure_mode_requires_x11="Secure-mode requires X11 (use \`-X')!" +msg_secure_mode_requires_root="Secure-mode requires root-access!" +msg_sorry_try_again="Sorry, try again." +msg_unknown_user="Unknown user: %s" +msg_user_disallowed="User disallowed: %s" +msg_yes="Yes" diff --git a/usr.sbin/bsdconfig/include/mustberoot.subr b/usr.sbin/bsdconfig/include/mustberoot.subr new file mode 100644 index 0000000000000..c64653056fe04 --- /dev/null +++ b/usr.sbin/bsdconfig/include/mustberoot.subr @@ -0,0 +1,360 @@ +if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION +# NOTE: These are not able to be overridden/inherited for security purposes. + +# +# Number of tries a user gets to enter his/her password before we log the +# sudo(8) failure and exit. +# +PASSWD_TRIES=3 + +# +# While in SECURE mode, should authentication as `root' be allowed? Set to +# non-NULL to enable authentication as `root', otherwise disabled. +# +# WARNING: +# Unless using a custom sudo(8) configuration, user `root' should not be +# allowed because no password is required to become `root' when already `root' +# and therefore, any value entered as password will work. +# +SECURE_ALLOW_ROOT= + +# +# While in SECURE mode, should we divulge (through error message) when the +# requested authentication user does not exist? Set to non-NULL to enable, +# otherwise a non-existent user is treated like an invalid password. +# +SECURE_DIVULGE_UNKNOWN_USER= + +############################################################ FUNCTIONS + +# f_become_root_via_sudo +# +# If not running as root, prompt for sudo(8) credentials to become root. +# Re-execution of the current program via sudo is automatically handled. +# +# The following environment variables effect functionality: +# +# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate +# that Xdialog(1) should be used instead of dialog(1). +# +f_become_root_via_sudo() +{ + local msg hline size + + [ "$( id -u )" = "0" ] && return $SUCCESS + + f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" + + # + # Check sudo(8) access before prompting for password. + # + :| sudo -S -v 2> /dev/null + if [ $? -ne $SUCCESS ]; then + # + # sudo(8) access denied. Prompt for their password. + # + msg="$msg_please_enter_password" + hline="$hline_alnum_punc_tab_enter" + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + + # + # Continue prompting until they either Cancel, succeed + # or exceed the number of allowed failures. + # + local password nfailures=0 retval + while [ $nfailures -lt $PASSWD_TRIES ]; do + if [ "$USE_XDIALOG" ]; then + password=$( $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --password --inputbox "$msg" $size \ + 2>&1 > /dev/null ) + retval=$? + + # Catch X11-related errors + [ $retval -eq 255 ] && + f_die $retval "$password" + else + $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --insecure \ + --passwordbox "$msg" $size \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + retval=$? + password=$( f_dialog_inputstr ) + fi + + # Exit if the user cancelled. + [ $retval -eq $SUCCESS ] || exit $retval + + # + # Validate sudo(8) credentials + # + sudo -S -v 2> /dev/null <<-EOF + $password + EOF + retval=$? + unset password # scrub memory + if [ $retval -eq $SUCCESS ]; then + # Access granted... + break + else + # Access denied... + nfailures=$(( $nfailures + 1 )) + + # introduce a short delay + if [ $nfailures -lt $PASSWD_TRIES ]; then + f_dialog_info "$msg_sorry_try_again" + sleep 1 + fi + fi + done + + # + # If user exhausted number of allowed password tries, log + # the security event and exit immediately. + # + if [ $nfailures -ge $PASSWD_TRIES ]; then + msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) + logger -p auth.notice -t sudo " " \ + "$USER : $msg" \ + "; TTY=$(tty)" \ + "; PWD=$PWD" \ + "; USER=root" \ + "; COMMAND=$0" + f_die 1 "sudo: $msg" + fi + fi + + # Use xauth(1) to grant root the ability to use this X11/SSH session + if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then + f_have xauth || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "xauth" + local HOSTNAME displaynum + HOSTNAME=$(hostname) + displaynum="${DISPLAY#*:}" + xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ + $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ + ~root/.Xauthority merge - > /dev/null 2>&1' + fi + + # Re-execute ourselves with sudo(8) + if [ $ARGC -gt 0 ]; then + exec sudo "$0" $ARGV + else + exec sudo "$0" + fi + exit $? # Never reached unless error +} + +# f_authenticate_some_user +# +# Only used if running as root and requires X11 (see USE_XDIALOG below). +# Prompts the user to enter a username and password to be authenticated via +# sudo(8) to proceed. +# +# The following environment variables effect functionality: +# +# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate +# that Xdialog(1) should be used instead of dialog(1). +# +f_authenticate_some_user() +{ + local msg hline size width height + + f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" + + # + # Secure-mode has been requested. + # + + [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" + [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" + + # + # Prompt for sudo(8) credentials. + # + + msg="$msg_please_enter_username_password" + hline="$hline_alnum_punc_tab_enter" + size=$( f_xdialog_2inputsbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$field_username" "" \ + "$field_password" "" ) + width="${size##*[$IFS]}" + height="${size%%[$IFS]*}" + height=$(( $height + 2 )) # Add height for --password + + # + # Continue prompting until they either Cancel, succeed or exceed the + # number of allowed failures. + # + local user_pass nfailures=0 retval + while [ $nfailures -lt $PASSWD_TRIES ]; do + user_pass=$( $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --password --2inputsbox "$msg" \ + $height $width \ + "$field_username" "" \ + "$field_password" "" \ + 2>&1 > /dev/null ) + retval=$? + + # Catch X11-related errors + [ $retval -eq 255 ] && f_die $retval "$user_pass" + + # Exit if the user cancelled. + [ $retval -eq $SUCCESS ] || exit $retval + + # + # Make sure the user exists and is non-root + # + local user password + user="${user_pass%%/*}" + password="${user_pass#*/}" + unset user_pass # scrub memory + if [ ! "$user" ]; then + nfailures=$(( $nfailures + 1 )) + f_dialog_msgbox "$msg_no_username" + continue + fi + if [ ! "$SECURE_ALLOW_ROOT" ]; then + case "$user" in + root|toor) + nfailures=$(( $nfailures + 1 )) + f_dialog_msgbox "$( printf \ + "$msg_user_disallowed" "$user" )" + continue + esac + fi + if ! f_quietly id "$user"; then + nfailures=$(( $nfailures + 1 )) + if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then + f_dialog_msgbox "$( printf \ + "$msg_unknown_user" "$user" )" + elif [ $nfailures -lt $PASSWD_TRIES ]; then + f_dialog_info "$msg_sorry_try_again" + sleep 1 + fi + continue + fi + + # + # Validate sudo(8) credentials for given user + # + su -m "$user" <<-EOF + sh < /dev/null < /dev/null 2>&1 +} + +fi # ! $_STRINGS_SUBR diff --git a/usr.sbin/bsdconfig/include/sysrc.subr b/usr.sbin/bsdconfig/include/sysrc.subr new file mode 100644 index 0000000000000..52e57bcde2aba --- /dev/null +++ b/usr.sbin/bsdconfig/include/sysrc.subr @@ -0,0 +1,616 @@ +if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION + +# +# Standard pathnames (inherit values from shell if available) +# +: ${RC_DEFAULTS:="/etc/defaults/rc.conf"} + +############################################################ GLOBALS + +# +# Global exit status variables +# +SUCCESS=0 +FAILURE=1 + +############################################################ FUNCTIONS + +# f_clean_env [ --except $varname ... ] +# +# Unset all environment variables in the current scope. An optional list of +# arguments can be passed, indicating which variables to avoid unsetting; the +# `--except' is required to enable the exclusion-list as the remainder of +# positional arguments. +# +# Be careful not to call this in a shell that you still expect to perform +# $PATH expansion in, because this will blow $PATH away. This is best used +# within a sub-shell block "(...)" or "$(...)" or "`...`". +# +f_clean_env() +{ + local var arg except= + + # + # Should we process an exclusion-list? + # + if [ "$1" = "--except" ]; then + except=1 + shift 1 + fi + + # + # Loop over a list of variable names from set(1) built-in. + # + for var in $( set | awk -F= \ + '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \ + | grep -v '^except$' + ); do + # + # In POSIX bourne-shell, attempting to unset(1) OPTIND results + # in "unset: Illegal number:" and causes abrupt termination. + # + [ "$var" = OPTIND ] && continue + + # + # Process the exclusion-list? + # + if [ "$except" ]; then + for arg in "$@" ""; do + [ "$var" = "$arg" ] && break + done + [ "$arg" ] && continue + fi + + unset "$var" + done +} + +# f_sysrc_get $varname +# +# Get a system configuration setting from the collection of system- +# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf +# and /etc/rc.conf). +# +# NOTE: Additional shell parameter-expansion formats are supported. For +# example, passing an argument of "hostname%%.*" (properly quoted) will +# return the hostname up to (but not including) the first `.' (see sh(1), +# "Parameter Expansion" for more information on additional formats). +# +f_sysrc_get() +{ + # Sanity check + [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE + + # Taint-check variable name + case "$1" in + [0-9]*) + # Don't expand possible positional parameters + return $FAILURE;; + *) + [ "$1" ] || return $FAILURE + esac + + ( # Execute within sub-shell to protect parent environment + + # + # Clear the environment of all variables, preventing the + # expansion of normals such as `PS1', `TERM', etc. + # + f_clean_env --except RC_CONFS RC_DEFAULTS SUCCESS + + . "$RC_DEFAULTS" > /dev/null 2>&1 + + unset RC_DEFAULTS + # no longer needed + + # + # If the query is for `rc_conf_files' then store the value that + # we inherited from sourcing RC_DEFAULTS (above) so that we may + # conditionally restore this value after source_rc_confs in the + # event that RC_CONFS does not customize the value. + # + if [ "$1" = "rc_conf_files" ]; then + _rc_conf_files="$rc_conf_files" + fi + + # + # If RC_CONFS is defined, set $rc_conf_files to an explicit + # value, modifying the default behavior of source_rc_confs(). + # + ( : ${RC_CONFS?} ) > /dev/null 2>&1 + if [ $? -eq ${SUCCESS:-0} ]; then + rc_conf_files="$RC_CONFS" + _rc_confs_set=1 + fi + + unset SUCCESS + # no longer needed + + source_rc_confs > /dev/null 2>&1 + + # + # If the query was for `rc_conf_files' AND after calling + # source_rc_confs the vaue has not changed, then we should + # restore the value to the one inherited from RC_DEFAULTS + # before performing the final query (preventing us from + # returning what was set via RC_CONFS when the intent was + # instead to query the value from the file(s) specified). + # + if [ "$1" = "rc_conf_files" -a \ + "$_rc_confs_set" -a \ + "$rc_conf_files" = "$RC_CONFS" \ + ]; then + rc_conf_files="$_rc_conf_files" + unset _rc_conf_files + unset _rc_confs_set + fi + + unset RC_CONFS + # no longer needed + + # + # This must be the last functional line for both the sub-shell + # and the function to preserve the return status from formats + # such as "${varname?}" and "${varname:?}" (see "Parameter + # Expansion" in sh(1) for more information). + # + eval echo '"${'"$1"'}"' 2> /dev/null + ) +} + +# f_sysrc_get_default $varname +# +# Get a system configuration default setting from the default rc.conf(5) file +# (or whatever RC_DEFAULTS points at). +# +f_sysrc_get_default() +{ + # Sanity check + [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE + + # Taint-check variable name + case "$1" in + [0-9]*) + # Don't expand possible positional parameters + return $FAILURE;; + *) + [ "$1" ] || return $FAILURE + esac + + ( # Execute within sub-shell to protect parent environment + + # + # Clear the environment of all variables, preventing the + # expansion of normals such as `PS1', `TERM', etc. + # + f_clean_env --except RC_DEFAULTS + + . "$RC_DEFAULTS" > /dev/null 2>&1 + + unset RC_DEFAULTS + # no longer needed + + # + # This must be the last functional line for both the sub-shell + # and the function to preserve the return status from formats + # such as "${varname?}" and "${varname:?}" (see "Parameter + # Expansion" in sh(1) for more information). + # + eval echo '"${'"$1"'}"' 2> /dev/null + ) +} + +# f_sysrc_find $varname +# +# Find which file holds the effective last-assignment to a given variable +# within the rc.conf(5) file(s). +# +# If the variable is found in any of the rc.conf(5) files, the function prints +# the filename it was found in and then returns success. Otherwise output is +# NULL and the function returns with error status. +# +f_sysrc_find() +{ + local varname="$1" + local regex="^[[:space:]]*$varname=" + local rc_conf_files="$( f_sysrc_get rc_conf_files )" + local conf_files= + local file + + # Check parameters + [ "$varname" ] || return $FAILURE + + # + # If RC_CONFS is defined, set $rc_conf_files to an explicit + # value, modifying the default behavior of source_rc_confs(). + # + [ "$RC_CONFS" ] && rc_conf_files="$RC_CONFS" + + # + # Reverse the order of files in rc_conf_files (the boot process sources + # these in order, so we will search them in reverse-order to find the + # last-assignment -- the one that ultimately effects the environment). + # + for file in $rc_conf_files; do + conf_files="$file${conf_files:+ }$conf_files" + done + + # + # Append the defaults file (since directives in the defaults file + # indeed affect the boot process, we'll want to know when a directive + # is found there). + # + conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS" + + # + # Find which file matches assignment to the given variable name. + # + for file in $conf_files; do + [ -f "$file" -a -r "$file" ] || continue + if grep -Eq "$regex" $file; then + echo $file + return $SUCCESS + fi + done + + return $FAILURE # Not found +} + +# f_sysrc_desc $varname +# +# Attempts to return the comments associated with varname from the rc.conf(5) +# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to). +# +# Multi-line comments are joined together. Results are NULL if no description +# could be found. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sysrc_desc_awk=' +# Variables that should be defined on the invocation line: +# -v varname="varname" +# +BEGIN { + regex = "^[[:space:]]*"varname"=" + found = 0 + buffer = "" +} +{ + if ( ! found ) + { + if ( ! match($0, regex) ) next + + found = 1 + sub(/^[^#]*(#[[:space:]]*)?/, "") + buffer = $0 + next + } + + if ( !/^[[:space:]]*#/ || + /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ || + /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ || + /^[[:space:]]*$/ ) exit + + sub(/(.*#)*[[:space:]]*/, "") + buffer = buffer" "$0 +} +END { + # Clean up the buffer + sub(/^[[:space:]]*/, "", buffer) + sub(/[[:space:]]*$/, "", buffer) + + print buffer + exit ! found +} +' +f_sysrc_desc() +{ + awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS" +} + +# f_sysrc_set $varname $new_value +# +# Change a setting in the system configuration files (edits the files in-place +# to change the value in the last assignment to the variable). If the variable +# does not appear in the source file, it is appended to the end of the primary +# system configuration file `/etc/rc.conf'. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sysrc_set_awk=' +# Variables that should be defined on the invocation line: +# -v varname="varname" +# -v new_value="new_value" +# +BEGIN { + regex = "^[[:space:]]*"varname"=" + found = retval = 0 +} +{ + # If already found... just spew + if ( found ) { print; next } + + # Does this line match an assignment to our variable? + if ( ! match($0, regex) ) { print; next } + + # Save important match information + found = 1 + matchlen = RSTART + RLENGTH - 1 + + # Store the value text for later munging + value = substr($0, matchlen + 1, length($0) - matchlen) + + # Store the first character of the value + t1 = t2 = substr(value, 0, 1) + + # Assignment w/ back-ticks, expression, or misc. + # We ignore these since we did not generate them + # + if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next } + + # Assignment w/ single-quoted value + else if ( t1 == "'\''" ) { + sub(/^'\''[^'\'']*/, "", value) + if ( length(value) == 0 ) t2 = "" + sub(/^'\''/, "", value) + } + + # Assignment w/ double-quoted value + else if ( t1 == "\"" ) { + sub(/^"(.*\\\\+")*[^"]*/, "", value) + if ( length(value) == 0 ) t2 = "" + sub(/^"/, "", value) + } + + # Assignment w/ non-quoted value + else if ( t1 ~ /[^[:space:];]/ ) { + t1 = t2 = "\"" + sub(/^[^[:space:]]*/, "", value) + } + + # Null-assignment + else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" } + + printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \ + t1, new_value, t2, value +} +END { exit retval } +' +f_sysrc_set() +{ + local varname="$1" new_value="$2" + + # Check arguments + [ "$varname" ] || return $FAILURE + + # + # Find which rc.conf(5) file contains the last-assignment + # + local not_found= + local file="$( f_sysrc_find "$varname" )" + if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then + # + # We either got a null response (not found) or the variable + # was only found in the rc.conf(5) defaults. In either case, + # let's instead modify the first file from $rc_conf_files. + # + + not_found=1 + + # + # If RC_CONFS is defined, use $RC_CONFS + # rather than $rc_conf_files. + # + if [ "$RC_CONFS" ]; then + file="${RC_CONFS%%[$IFS]*}" + else + file=$( f_sysrc_get rc_conf_files ) + file="${file%%[$IFS]*}" + fi + fi + + # + # If not found, append new value to last file and return. + # + if [ "$not_found" ]; then + echo "$varname=\"$new_value\"" >> "$file" + return $? + fi + + # + # Perform sanity checks. + # + if [ ! -w "$file" ]; then + f_err "$msg_cannot_create_permission_denied\n" \ + "$pgm" "$file" + return $FAILURE + fi + + # + # Create a new temporary file to write to. + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions (else we're in for a surprise, as mktemp(1) creates + # the temporary file with 0600 permissions, and if we simply mv(1) the + # temporary file over the destination, the destination will inherit the + # permissions from the temporary file). + # + local mode + mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) + f_quietly chmod "${mode:-0644}" "$tmpfile" + + # + # Fixup ownership. The destination file _is_ writable (we tested + # earlier above). However, this will fail if we don't have sufficient + # permissions (so we throw stderr into the bit-bucket). + # + local owner + owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on the matching file, replacing only the last occurrence. + # + local new_contents retval + new_contents=$( tail -r $file 2> /dev/null ) + new_contents=$( echo "$new_contents" | awk -v varname="$varname" \ + -v new_value="$new_value" "$f_sysrc_set_awk" ) + retval=$? + + # + # Write the temporary file contents. + # + echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE + if [ $retval -ne $SUCCESS ]; then + echo "$varname=\"$new_value\"" >> "$tmpfile" + fi + + # + # Taint-check our results. + # + if ! /bin/sh -n "$tmpfile"; then + f_err "$msg_previous_syntax_errors\n" "$pgm" "$file" + rm -f "$tmpfile" + return $FAILURE + fi + + # + # Finally, move the temporary file into place. + # + mv "$tmpfile" "$file" +} + +# f_sysrc_delete $varname +# +# Remove a setting from the system configuration files (edits files in-place). +# Deletes all assignments to the given variable in all config files. If the +# `-f file' option is passed, the removal is restricted to only those files +# specified, otherwise the system collection of rc_conf_files is used. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sysrc_delete_awk=' +# Variables that should be defined on the invocation line: +# -v varname="varname" +# +BEGIN { + regex = "^[[:space:]]*"varname"=" + found = 0 +} +{ + if ( $0 ~ regex ) + found = 1 + else + print +} +END { exit ! found } +' +f_sysrc_delete() +{ + local varname="$1" + local file + + # Check arguments + [ "$varname" ] || return $FAILURE + + # + # Operate on each of the specified files + # + for file in ${RC_CONFS:-$( f_sysrc_get rc_conf_files )}; do + [ -e "$file" ] || continue + + # + # Create a new temporary file to write to. + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions and ownership (mktemp(1) defaults to 0600 + # permissions) to instead match the destination file. + # + local mode owner + mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on the file, removing all occurrences, saving the + # output in our temporary file. + # + awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \ + > "$tmpfile" + if [ $? -ne $SUCCESS ]; then + # The file didn't contain any assignments + rm -f "$tmpfile" + continue + fi + + # + # Taint-check our results. + # + if ! /bin/sh -n "$tmpfile"; then + f_err "$msg_previous_syntax_errors\n" \ + "$pgm" "$file" + rm -f "$tmpfile" + return $FAILURE + fi + + # + # Perform sanity checks + # + if [ ! -w "$file" ]; then + f_err "$msg_permission_denied\n" "$pgm" "$file" + rm -f "$tmpfile" + return $FAILURE + fi + + # + # Finally, move the temporary file into place. + # + mv "$tmpfile" "$file" + done +} + +fi # ! $_SYSRC_SUBR diff --git a/usr.sbin/bsdconfig/mouse/INDEX b/usr.sbin/bsdconfig/mouse/INDEX new file mode 100644 index 0000000000000..cf7224cbf7141 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/INDEX @@ -0,0 +1,58 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Mouse" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Configure the Mouse" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="mouse|mouse" +menu_selection="mouse_enable|enable" +menu_selection="mouse_type|type" +menu_selection="mouse_port|port" +menu_selection="mouse_flags|flags" +menu_selection="mouse_disable|disable" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="mouse" diff --git a/usr.sbin/bsdconfig/mouse/Makefile b/usr.sbin/bsdconfig/mouse/Makefile new file mode 100644 index 0000000000000..3b68cad57053d --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/110.mouse +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= disable enable flags mouse port type + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/mouse/USAGE b/usr.sbin/bsdconfig/mouse/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/mouse/disable b/usr.sbin/bsdconfig/mouse/disable new file mode 100755 index 0000000000000..a3101a77210e1 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/disable @@ -0,0 +1,104 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="110.mouse" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Location of moused(8) pidfile +# +MOUSED_PIDFILE=/var/run/moused.pid + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_mouse_disable" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Stop the mouse daemon +# +[ -r "$MOUSED_PIDFILE" ] && + f_quietly kill "$( cat "$MOUSED_PIDFILE" )" +f_sysrc_set moused_enable "NO" || f_die +f_sysrc_set moused_type "NO" || f_die +f_sysrc_delete moused_port || f_die +f_sysrc_delete moused_flags || f_die + +# +# Message box +# +f_dialog_title "$msg_message" +hline="$hline_press_enter_or_space" +prompt="$msg_mouse_daemon_is_disabled" +size=$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$prompt" ) +eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$prompt\" $size + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/mouse/enable b/usr.sbin/bsdconfig/mouse/enable new file mode 100755 index 0000000000000..72565e2c083e5 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/enable @@ -0,0 +1,118 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="110.mouse" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Location of moused(8) pidfile +# +MOUSED_PIDFILE=/var/run/moused.pid + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_mouse_enable" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Get the type, port, and flags +# +type=$( f_sysrc_get moused_type ) +case "$type" in +[Nn][Oo]|"") f_die 1 "$msg_please_select_protocol_and_port_first" ;; +esac +port=$( f_sysrc_get moused_port ) +[ "$port" ] || f_die 1 "$msg_please_select_protocol_and_port_first" +flags=$( f_sysrc_get moused_flags ) + +# +# Start the mouse daemon +# +f_dialog_info "$msg_trying_to_start_the_mouse_daemon" +[ -r "$MOUSED_PIDFILE" ] && + f_quietly kill "$( cat "$MOUSED_PIDFILE" 2> /dev/null )" +f_quietly vidcontrol -m on +f_quietly moused -t "$type" -p "$port" $flags + +# +# Confirm with the user that the mouse is working +# +f_dialog_title "$msg_user_confirmation_requested" +f_dialog_yesno "$msg_now_move_the_mouse" +retval=$? +f_dialog_title_restore + +# +# Stop the mouse daemon +# +f_quietly vidcontrol -m off +if [ $retval -eq $SUCCESS ]; then + f_sysrc_set moused_enable "YES" || f_die + ln -fs /dev/sysmouse /dev/mouse || f_die # backwards compat +else + [ -r "$MOUSED_PIDFILE" ] && + f_quietly kill "$( cat "$MOUSED_PIDFILE" )" + f_sysrc_set moused_enable "NO" || f_die +fi + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/mouse/flags b/usr.sbin/bsdconfig/mouse/flags new file mode 100755 index 0000000000000..ab57f214e7ab7 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/flags @@ -0,0 +1,116 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="110.mouse" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Location of moused(8) pidfile +# +MOUSED_PIDFILE=/var/run/moused.pid + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_mouse_flags" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Get the current flags +# +flags=$( f_sysrc_get moused_flags ) + +# +# Prompt the user with the current value +# +f_dialog_title "$msg_value_required" +title="$DIALOG_TITLE" +btitle="$DIALOG_BACKTITLE" +f_dialog_title_restore +hline= +prompt="$msg_please_specify_the_mouse_daemon_flags" +size=$( f_dialog_inputbox_size \ + "$title" \ + "$btitle" \ + "$prompt" \ + "$flags" \ + "$hline" ) +eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$prompt\" $size \ + \"\$flags\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" +retval=$? +flags=$( f_dialog_inputstr ) + +[ $retval -eq $SUCCESS ] || f_die + +# +# Save the new value +# +f_sysrc_set moused_flags "$flags" || f_die + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/mouse/include/Makefile b/usr.sbin/bsdconfig/mouse/include/Makefile new file mode 100644 index 0000000000000..ed0b24dad48e9 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/110.mouse/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/mouse/include/messages.subr b/usr.sbin/bsdconfig/mouse/include/messages.subr new file mode 100644 index 0000000000000..c6f4969845c9d --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/include/messages.subr @@ -0,0 +1,90 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_press_enter_or_space="Press enter or space" +msg_auto="Auto" +msg_auto_desc="Bus mouse, PS/2 style mouse or PnP serial mouse" +msg_busmouse="BusMouse" +msg_busmouse_desc="Logitech, ATI or MS bus mouse (/dev/mse0)" +msg_cancel="Cancel" +msg_com1="COM1" +msg_com1_desc="Serial mouse on COM1 (/dev/cuau0)" +msg_com2="COM2" +msg_com2_desc="Serial mouse on COM2 (/dev/cuau1)" +msg_com3="COM3" +msg_com3_desc="Serial mouse on COM3 (/dev/cuau2)" +msg_com4="COM4" +msg_com4_desc="Serial mouse on COM4 (/dev/cuau3)" +msg_disable="Disable" +msg_disable_the_mouse_daemon="Disable the mouse daemon" +msg_enable="Enable" +msg_exit="Exit" +msg_exit_this_menu="Exit this menu (returning to previous)" +msg_flags="Flags" +msg_glidepoint="GlidePoint" +msg_glidepoint_desc="ALPS GlidePoint pad (serial)" +msg_hitachi="Hitachi" +msg_hitachi_desc="Hitachi tablet (serial)" +msg_intellimouse="IntelliMouse" +msg_intellimouse_desc="Microsoft IntelliMouse (serial)" +msg_logitech="Logitech" +msg_logitech_desc="Logitech protocol (old models) (serial)" +msg_menu_text="You can cut and paste text in the text console by running the mouse\ndaemon. Specify a port and a protocol type of your mouse and enable\nthe mouse daemon. If you don't want this feature, select 6 to disable\nthe daemon.\nOnce you've enabled the mouse daemon, you can specify \"/dev/sysmouse\"\nas your mouse device and \"SysMouse\" or \"MouseSystems\" as mouse\nprotocol when running the X configuration utility (see Configuration\nmenu)." +msg_message="Message" +msg_microsoft="Microsoft" +msg_microsoft_desc="Microsoft protocol (serial)" +msg_mm_series="MM Series" +msg_mm_series_desc="MM Series protocol (serial)" +msg_mouseman="MouseMan" +msg_mouseman_desc="Logitech MouseMan/TrackMan models (serial)" +msg_mousesystems="MouseSystems" +msg_mousesystems_desc="MouseSystems protocol (serial)" +msg_mouse_daemon_is_disabled="The mouse daemon is disabled." +msg_mouse_disable="Mouse Disable" +msg_mouse_enable="Mouse Enable" +msg_mouse_flags="Mouse Flags" +msg_now_move_the_mouse="Now move the mouse and see if it works.\n(Note that buttons don't have any effect for now.)\n\n Is the mouse cursor moving?\n" +msg_ok="OK" +msg_please_configure_your_mouse="Please configure your mouse" +msg_please_select_protocol_and_port_first="Please select a mouse protocol and a port first." +msg_please_specify_the_mouse_daemon_flags="Please specify the mouse daemon flags. If you would like to\nemulate 3 buttons, use -3 here.\n " +msg_port="Port" +msg_port_menu_text="The built-in pointing device of laptop/notebook computers is usually\na PS/2 style device." +msg_protocol_menu_text="If your mouse is attached to the PS/2 mouse port or the bus mouse port,\nyou should always choose \"Auto\", regardless of the model and the brand\nof the mouse. All other protocol types are for serial mice and should\nnot be used with the PS/2 port mouse or the bus mouse. If you have\na serial mouse and are not sure about its protocol, you should also try\n\"Auto\". It may not work for the serial mouse if the mouse does not\nsupport the PnP standard. But, it won't hurt. Many 2-button serial mice\nare compatible with \"Microsoft\" or \"MouseMan\". 3-button serial mice\nmay be compatible with \"MouseSystems\" or \"MouseMan\". If the serial\nmouse has a wheel, it may be compatible with \"IntelliMouse\"." +msg_ps2="PS/2" +msg_ps2_desc="PS/2 style mouse (/dev/psm0)" +msg_select_a_protocol_type_for_your_mouse="Select a protocol type for your mouse" +msg_select_mouse_port="Select mouse port" +msg_select_mouse_protocol_type="Select mouse protocol type" +msg_select_your_mouse_port_from_the_following_menu="Select your mouse port from the following menu" +msg_set_additional_flags="Set additional flags" +msg_test_and_run_the_mouse_daemon="Test and run the mouse daemon" +msg_thinkingmouse="ThinkingMouse" +msg_thinkingmouse_desc="Kensington ThinkingMouse (serial)" +msg_trying_to_start_the_mouse_daemon="Trying to start the mouse daemon..." +msg_type="Type" +msg_user_confirmation_requested="User Confirmation Requested" +msg_value_required="Value Required" diff --git a/usr.sbin/bsdconfig/mouse/mouse b/usr.sbin/bsdconfig/mouse/mouse new file mode 100755 index 0000000000000..f85c4989d0705 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/mouse @@ -0,0 +1,134 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="110.mouse" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="" + local prompt="$msg_menu_text" + + menu_list=" + 'X $msg_exit' '$msg_exit_this_menu' + '2 $msg_enable' '$msg_test_and_run_the_mouse_daemon' + '3 $msg_type' '$msg_select_mouse_protocol_type' + '4 $msg_port' '$msg_select_mouse_port' + '5 $msg_flags' '$msg_set_additional_flags' + '6 $msg_disable' '$msg_disable_the_mouse_daemon' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_please_configure_your_mouse" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + "2 $msg_enable") # Test and run the mouse daemon + $BSDCFG_LIBE/$APP_DIR/enable ${USE_XDIALOG:+-X} ;; + "3 $msg_type") # Select mouse protocol type + $BSDCFG_LIBE/$APP_DIR/type ${USE_XDIALOG:+-X} ;; + "4 $msg_port") # Select mouse port + $BSDCFG_LIBE/$APP_DIR/port ${USE_XDIALOG:+-X} ;; + "5 $msg_flags") # Set additional flags + $BSDCFG_LIBE/$APP_DIR/flags ${USE_XDIALOG:+-X} ;; + "6 $msg_disable") # Disable the mouse daemon + $BSDCFG_LIBE/$APP_DIR/disable ${USE_XDIALOG:+-X} ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/mouse/port b/usr.sbin/bsdconfig/mouse/port new file mode 100755 index 0000000000000..c7e1ae153b0e4 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/port @@ -0,0 +1,142 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="110.mouse" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="" + local prompt="$msg_port_menu_text" + + menu_list=" + '1 $msg_ps2' '$msg_ps2_desc' + '2 $msg_com1' '$msg_com1_desc' + '3 $msg_com2' '$msg_com2_desc' + '4 $msg_com3' '$msg_com3_desc' + '5 $msg_com4' '$msg_com4_desc' + '6 $msg_busmouse' '$msg_busmouse_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_select_your_mouse_port_from_the_following_menu" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "1 $msg_ps2") # PS/2 style mouse (/dev/psm0) + f_sysrc_set moused_port "/dev/psm0" || f_die + break ;; + "2 $msg_com1") # Serial mouse on COM1 (/dev/cuau0) + f_sysrc_set moused_port "/dev/cuau0" || f_die + break ;; + "3 $msg_com2") # Serial mouse on COM2 (/dev/cuau1) + f_sysrc_set moused_port "/dev/cuau1" || f_die + break ;; + "4 $msg_com3") # Serial mouse on COM3 (/dev/cuau2) + f_sysrc_set moused_port "/dev/cuau2" || f_die + break ;; + "5 $msg_com4") # Serial mouse on COM4 (/dev/cuau3) + f_sysrc_set moused_port "/dev/cuau3" || f_die + break ;; + "6 $msg_busmouse") # Logitech, ATI or MS bus mouse (/dev/mse0) + f_sysrc_set moused_port "/dev/mse0" || f_die + break ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/mouse/type b/usr.sbin/bsdconfig/mouse/type new file mode 100755 index 0000000000000..651324451ca36 --- /dev/null +++ b/usr.sbin/bsdconfig/mouse/type @@ -0,0 +1,158 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="110.mouse" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="" + local prompt="$msg_protocol_menu_text" + + menu_list=" + '1 $msg_auto' '$msg_auto_desc' + '2 $msg_glidepoint' '$msg_glidepoint_desc' + '3 $msg_hitachi' '$msg_hitachi_desc' + '4 $msg_intellimouse' '$msg_intellimouse_desc' + '5 $msg_logitech' '$msg_logitech_desc' + '6 $msg_microsoft' '$msg_microsoft_desc' + '7 $msg_mm_series' '$msg_mm_series_desc' + '8 $msg_mouseman' '$msg_mouseman_desc' + '9 $msg_mousesystems' '$msg_mousesystems_desc' + 'A $msg_thinkingmouse' '$msg_thinkingmouse_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_select_a_protocol_type_for_your_mouse" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "1 $msg_auto") # Bus mouse, PS/2 style mouse or PnP serial mouse + f_sysrc_set moused_type "auto" || f_die + break ;; + "2 $msg_glidepoint") # ALPS GlidePoint pad (serial) + f_sysrc_set moused_type "glidepoint" || f_die + break ;; + "3 $msg_hitachi") # Hitachi tablet (serial) + f_sysrc_set moused_type "mmhittab" || f_die + break ;; + "4 $msg_intellimouse") # Microsoft Intellimouse (serial) + f_sysrc_set moused_type "intellimouse" || f_die + break ;; + "5 $msg_logitech") # Logitech protocol (old models) (serial) + f_sysrc_set moused_type "logitech" || f_die + break ;; + "6 $msg_microsoft") # Microsoft protocol (serial) + f_sysrc_set moused_type "microsoft" || f_die + break ;; + "7 $msg_mm_series") # MM Series protocol (serial) + f_sysrc_set moused_type "mmseries" || f_die + break ;; + "8 $msg_mouseman") # Logitech MouseMan/TrackMan models (serial) + f_sysrc_set moused_type "mouseman" || f_die + break ;; + "9 $msg_mousesystems") # MouseSystems protocol (serial) + f_sysrc_set moused_type "mousesystems" || f_die + break ;; + "A $msg_thinkingmouse") # Kensignton ThinkingMouse (serial) + f_sysrc_set moused_type "thinkingmouse" || f_die + break ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/networking/INDEX b/usr.sbin/bsdconfig/networking/INDEX new file mode 100644 index 0000000000000..1d239110fe2e1 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/INDEX @@ -0,0 +1,57 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Networking Management" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Setup Networking interfaces, services, etc." +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="networking|networking" +menu_selection="defaultrouter|defaultrouter" +menu_selection="hostname|hostname" +menu_selection="nameservers|nameservers" +menu_selection="netdev|devices" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="networking" diff --git a/usr.sbin/bsdconfig/networking/Makefile b/usr.sbin/bsdconfig/networking/Makefile new file mode 100644 index 0000000000000..fcd0509437fc7 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/120.networking +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= defaultrouter devices hostname nameservers networking + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/networking/USAGE b/usr.sbin/bsdconfig/networking/USAGE new file mode 100644 index 0000000000000..a4dd74c8048d3 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/networking/defaultrouter b/usr.sbin/bsdconfig/networking/defaultrouter new file mode 100755 index 0000000000000..78d647df96066 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/defaultrouter @@ -0,0 +1,73 @@ +#!/bin/sh +#- +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/routing.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_default_router" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Change the default router/gateway +# +f_dialog_input_defaultrouter + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/networking/devices b/usr.sbin/bsdconfig/networking/devices new file mode 100755 index 0000000000000..a12f782ff30ca --- /dev/null +++ b/usr.sbin/bsdconfig/networking/devices @@ -0,0 +1,158 @@ +#!/bin/sh +#- +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/device.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/media.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/netmask.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line options +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_networking_devices" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + f_dialog_menu_netdev + retval=$? + interface=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || break + + # + # dialog_menu_netdev adds an asterisk (*) to the right of the + # device name if the interface is active. Remove the asterisk + # from the device name if present. + # + case "$interface" in + *\*) interface="${interface%?}";; + esac + + # + # Obtain initial interface settings to be configured. These will be + # passed to the f_dialog_menu_netdev_edit function-call below which + # will block until the user has either cancelled or finished editing + # the values. + # + # First, attempt to read stored configuration from rc.conf(5) and + # fallback to reading the active configuration if not configured in + # the rc.conf(5) file(s). + # + _ipaddr= + _netmask= + _ifconfig=$( f_sysrc_get ifconfig_$interface ) + if [ "$_ifconfig" ]; then + # If DHCP get IP address/netmask later from ifconfig(8) + glob="[Dd][Hh][Cc][Pp]" + case "$_ifconfig" in + $glob) dhcp=1;; + [Ss][Yy][Nn][Cc]$glob) dhcp=1;; + [Nn][Oo][Ss][Yy][Nn][Cc]$glob) dhcp=1;; + *) + # + # Get IP address/netmask from rc.conf(5) + # configuration + # + dhcp= + eval "$( exec 2> /dev/null + set -- $_ifconfig + while [ $# -gt 0 ]; do + case "$1" in + inet) + shift 1 + echo "_ipaddr='$1'" + ;; + netmask) + shift 1 + echo "_netmask='$1'" + ;; + esac + shift 1 + done + )" + ;; + esac + fi + + # + # Fill in IP address/netmask from active settings if no + # configuration could be extrapolated from rc.conf(5) + # + [ "$_ipaddr" ] || _ipaddr=$( f_ifconfig_inet $interface ) + [ "$_netmask" ] || _netmask=$( f_ifconfig_netmask $interface ) + + # Get the extra options (this always comes from rc.conf(5)) + _options=$( f_ifconfig_options $interface ) + + # Block on user-configuration of the probed settings + f_dialog_menu_netdev_edit \ + $interface $_ipaddr $_netmask "$_options" $dhcp + + # Return to root menu if above returns success + [ $? -eq $SUCCESS ] && break + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/networking/hostname b/usr.sbin/bsdconfig/networking/hostname new file mode 100755 index 0000000000000..aec731d202cd7 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/hostname @@ -0,0 +1,73 @@ +#!/bin/sh +#- +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/hostname.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_hostname_domain" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Change the local hostname +# +f_dialog_input_hostname + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/networking/include/Makefile b/usr.sbin/bsdconfig/networking/include/Makefile new file mode 100644 index 0000000000000..858ceba0ea86d --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/120.networking/include +FILES= common.subr device.subr hostname.subr ipaddr.subr media.subr \ + messages.subr netmask.subr resolv.subr routing.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/networking/include/common.subr b/usr.sbin/bsdconfig/networking/include/common.subr new file mode 100644 index 0000000000000..13e9795396636 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/common.subr @@ -0,0 +1,65 @@ +if [ ! "$_NETWORKING_COMMON_SUBR" ]; then _NETWORKING_COMMON_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 + +############################################################ FUNCTIONS + +# f_mounted $local_directory +# +# Return success if a filesystem is mounted on a particular directory. +# +f_mounted() +{ + local dir="$1" + [ -d "$dir" ] || return $FAILURE + mount | grep -Eq " on $dir \([^)]+\)$" +} + +# f_jailed +# +# Returns true if the current process is jail(8)ed. +# +f_jailed() +{ + ! f_quietly ps 1 +} + +# f_nfs_mounted +# +# Returns true if there are any NFS mounts currently active, otherwise false. +# +f_nfs_mounted() +{ + [ "$( df -t nfs )" ] +} + +fi # ! $_NETWORKING_COMMON_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/device.subr b/usr.sbin/bsdconfig/networking/include/device.subr new file mode 100644 index 0000000000000..530885407808e --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/device.subr @@ -0,0 +1,489 @@ +if [ ! "$_NETWORKING_DEVICE_SUBR" ]; then _NETWORKING_DEVICE_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/media.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/netmask.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/resolv.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/routing.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ GLOBALS + +# +# Settings used while interacting with various dialog(1) menus +# +: ${DIALOG_MENU_NETDEV_KICK_INTERFACES=1} +: ${DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK=3} + +############################################################ FUNCTIONS + +# f_device_desc $device_name +# +# Print a description for a device name (eg., `fxp0'). +# +f_device_desc() +{ + local device="$1" d="[1234567890]" desc="" + + # Check variables + [ "$device" ] || return $SUCCESS + + # + # Return sysctl MIB dev.NAME.UNIT.%desc if it exists, + # otherwise fall through to below static list. + # + if f_have sysctl; then + local devname devunit + devname="${device%%$d*}" + devunit="${device#$devname}" + devunit="${devunit%%[a-zA-Z_]*}" + sysctl -n "dev.$devname.$devunit.%desc" 2> /dev/null && + return $SUCCESS + fi + + case "$device" in + # Network devices + ae$d) desc="Attansic/Atheros L2 Fast Ethernet";; + age$d) desc="Attansic/Atheros L1 Gigabit Ethernet";; + alc$d) desc="Atheros AR8131/AR8132 PCIe Ethernet";; + ale$d) desc="Atheros AR8121/AR8113/AR8114 PCIe Ethernet";; + an$d) desc="Aironet 4500/4800 802.11 wireless adapter";; + ath$d) desc="Atheros IEEE 802.11 wireless adapter";; + aue$d) desc="ADMtek USB Ethernet adapter";; + axe$d) desc="ASIX Electronics USB Ethernet adapter";; + bce$d) desc="Broadcom NetXtreme II Gigabit Ethernet card";; + bfe$d) desc="Broadcom BCM440x PCI Ethernet card";; + bge$d) desc="Broadcom BCM570x PCI Gigabit Ethernet card";; + bm$d) desc="Apple BMAC Built-in Ethernet";; + bwn$d) desc="Broadcom BCM43xx IEEE 802.11 wireless adapter";; + cas$d) desc="Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet";; + cc3i$d) desc="SDL HSSI sync serial PCI card";; + cue$d) desc="CATC USB Ethernet adapter";; + cxgb$d) desc="Chelsio T3 10Gb Ethernet card";; + dc$d) desc="DEC/Intel 21143 (and clones) PCI Fast Ethernet card";; + de$d) desc="DEC DE435 PCI NIC or other DC21040-AA based card";; + disc$d) desc="Software discard network interface";; + ed$d) desc="Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA";; + el$d) desc="3Com 3C501 Ethernet card";; + em$d) desc="Intel(R) PRO/1000 Ethernet card";; + en$d) desc="Efficient Networks ATM PCI card";; + ep$d) desc="3Com 3C509 Ethernet card/3C589 PCMCIA";; + et$d) desc="Agere ET1310 based PCI Express Gigabit Ethernet card";; + ex$d) desc="Intel EtherExpress Pro/10 Ethernet card";; + fe$d) desc="Fujitsu MB86960A/MB86965A Ethernet card";; + fpa$d) desc="DEC DEFPA PCI FDDI card";; + fwe$d) desc="FireWire Ethernet emulation";; + fwip$d) desc="IP over FireWire";; + fxp$d) desc="Intel EtherExpress Pro/100B PCI Fast Ethernet card";; + gem$d) desc="Apple GMAC or Sun ERI/GEM Ethernet adapter";; + hme$d) desc="Sun HME (Happy Meal Ethernet) Ethernet adapter";; + ie$d) desc="AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210";; + igb$d) desc="Intel(R) PRO/1000 PCI Express Gigabit Ethernet card";; + ipw$d) desc="Intel PRO/Wireless 2100 IEEE 802.11 adapter";; + iwi$d) desc="Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter";; + iwn$d) desc="Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter";; + ix$d) desc="Intel Etherexpress Ethernet card";; + ixgb$d) desc="Intel(R) PRO/10Gb Ethernet card";; + ixgbe$d) desc="Intel(R) PRO/10Gb Ethernet card";; + jme$d) desc="JMicron JMC250 Gigabit/JMC260 Fast Ethernet";; + kue$d) desc="Kawasaki LSI USB Ethernet adapter";; + le$d) desc="AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter";; + lge$d) desc="Level 1 LXT1001 Gigabit Ethernet card";; + lnc$d) desc="Lance/PCnet (Isolan/Novell NE2100/NE32-VL) Ethernet";; + lp$d) desc="Parallel Port IP (PLIP) peer connection";; + lo$d) desc="Loop-back (local) network interface";; + malo$d) desc="Marvell Libertas 88W8335 802.11 wireless adapter";; + msk$d) desc="Marvell/SysKonnect Yukon II Gigabit Ethernet";; + mxge$d) desc="Myricom Myri10GE 10Gb Ethernet card";; + nfe$d) desc="NVIDIA nForce MCP Ethernet";; + ng${d}_*|ng$d${d}_*|ng$d$d${d}_*|ng$d$d$d${d}_*|ng$d$d$d$d${d}_*) + desc="Vimage netgraph(4) bridged Ethernet device";; + nge$d) desc="NatSemi PCI Gigabit Ethernet card";; + nve$d) desc="NVIDIA nForce MCP Ethernet";; + nxge$d) desc="Neterion Xframe 10GbE Server/Storage adapter";; + pcn$d) desc="AMD Am79c79x PCI Ethernet card";; + plip$d) desc="Parallel Port IP (PLIP) peer connection";; + ral$d) desc="Ralink Technology IEEE 802.11 wireless adapter";; + ray$d) desc="Raytheon Raylink 802.11 wireless adapter";; + re$d) desc="RealTek 8139C+/8169/8169S/8110S PCI Ethernet adapter";; + rl$d) desc="RealTek 8129/8139 PCI Ethernet card";; + rue$d) desc="RealTek USB Ethernet card";; + rum$d) desc="Ralink Technology USB IEEE 802.11 wireless adapter";; + sf$d) desc="Adaptec AIC-6915 PCI Ethernet card";; + sge$d) desc="Silicon Integrated Systems SiS190/191 Ethernet";; + sis$d) desc="SiS 900/SiS 7016 PCI Ethernet card";; + sk$d) desc="SysKonnect PCI Gigabit Ethernet card";; + sn$d) desc="SMC/Megahertz Ethernet card";; + snc$d) desc="SONIC Ethernet card";; + sr$d) desc="SDL T1/E1 sync serial PCI card";; + ste$d) desc="Sundance ST201 PCI Ethernet card";; + stge$d) desc="Sundance/Tamarack TC9021 Gigabit Ethernet";; + ti$d) desc="Alteon Networks PCI Gigabit Ethernet card";; + tl$d) desc="Texas Instruments ThunderLAN PCI Ethernet card";; + tx$d) desc="SMC 9432TX Ethernet card";; + txp$d) desc="3Com 3cR990 Ethernet card";; + uath$d) desc="Atheros AR5005UG and AR5005UX USB wireless adapter";; + upgt$d) desc="Conexant/Intersil PrismGT USB wireless adapter";; + ural$d) desc="Ralink Technology RT2500USB 802.11 wireless adapter";; + urtw$d) desc="Realtek 8187L USB wireless adapter";; + vge$d) desc="VIA VT612x PCI Gigabit Ethernet card";; + vlan$d|vlan$d$d|vlan$d$d$d|vlan$d$d$d$d|vlan$d$d$d$d$d) + desc="IEEE 802.1Q VLAN network interface";; + vr$d) desc="VIA VT3043/VT86C100A Rhine PCI Ethernet card";; + vx$d) desc="3COM 3c590 / 3c595 Ethernet card";; + wb$d) desc="Winbond W89C840F PCI Ethernet card";; + wi$d) desc="Lucent WaveLAN/IEEE 802.11 wireless adapter";; + wpi$d) desc="Intel 3945ABG IEEE 802.11 wireless adapter";; + wx$d) desc="Intel Gigabit Ethernet (82452) card";; + xe$d) desc="Xircom/Intel EtherExpress Pro100/16 Ethernet card";; + xl$d) desc="3COM 3c90x / 3c90xB PCI Ethernet card";; + zyd$d) desc="ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter";; + # Unknown device + *) desc="";; + esac + printf "%s\n" "$desc" +} + +# f_dialog_menu_netdev +# +# Display a list of network devices with descriptions. +# +f_dialog_menu_netdev() +{ + # + # Display a message to let the user know we're working... + # (message will remain until we throw up the next dialog) + # + f_dialog_info "$msg_probing_network_interfaces" + + # + # Get list of usable network interfaces + # + local d='[[:digit:]]+:' + local iflist="`echo "$(ifconfig -l):" | sed -E -e " + # Convert all spaces to colons + y/ /:/ + + # Prune unsavory interfaces + s/lo$d//g + s/ppp$d//g + s/sl$d//g + s/lp$d//g + s/fwe$d//g + s/faith$d//g + s/plip$d//g + + # Convert all colons back into spaces + y/:/ / + "`" + + # + # Optionally kick interfaces in the head to get them to accurately + # track the carrier status in realtime (required on FreeBSD). + # + if [ "$DIALOG_MENU_NETDEV_KICK_INTERFACES" ]; then + DIALOG_MENU_NETDEV_KICK_INTERFACES= + + local ifn + for ifn in $iflist; do + f_quietly ifconfig $ifn up + done + + if [ "$DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK" ]; then + # interfaces need time to update carrier status + sleep $DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK + fi + fi + + # + # Mark any "active" interfaces with an asterisk (*) + # to the right of the device name. + # + interfaces=$( + for ifn in $iflist; do + active=$( ifconfig $ifn | awk \ + ' + ( $1 == "status:" ) \ + { + if ( $2 == "active" ) { print 1; exit } + } + ' ) + printf "'%s%s' '%s'\n" \ + "$ifn" "${active:+*}" "$(f_device_desc "$ifn")" + done + ) + if [ ! "$interfaces" ]; then + f_dialog_msgbox "$msg_no_network_interfaces" + return $FAILURE + fi + + local hline="$hline_arrows_tab_enter" + + # + # Ask user to select an interface + # + local prompt size + prompt="$msg_select_network_interface" + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $interfaces ) + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $interfaces \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_menu_netdev_edit $interface $ipaddr $netmask $options $dhcp +# +# Allow a user to edit network interface settings. Current values are not +# probed but rather taken from the positional arguments. +# +f_dialog_menu_netdev_edit() +{ + local interface="$1" ipaddr="$2" netmask="$3" options="$4" dhcp="$5" + local prompt menu_list size + + # + # Create a duplicate set of variables for change-tracking... + # + local ipaddr_orig="$2" \ + netmask_orig="$3" \ + options_orig="$4" \ + dhcp_orig="$5" + + local hline="$hline_arrows_tab_enter" + prompt=$( printf "$msg_network_configuration" "$interface" ) + + # + # Loop forever until the user has finished configuring the different + # components of the network interface. + # + # To apply the settings, we need to know each of the following: + # - IP Address + # - Network subnet mask + # - Additional ifconfig(8) options + # + # It is only when we have all of the above values that we can make the + # changes effective because all three options must be specified at-once + # to ifconfig(8). + # + while :; do + local dhcp_status="$msg_disabled" + [ "$dhcp" ] && dhcp_status="$msg_enabled" + + # + # Display configuration-edit menu + # + menu_list=" + 'X $msg_save_exit' '$msg_return_to_previous_menu' + '2 $msg_dhcp' '$dhcp_status' + '3 $msg_ipaddr4' '$ipaddr' + '4 $msg_netmask' '$netmask' + '5 $msg_options' '$options' + " + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + local tag="$( f_dialog_menutag )" + + # Return if "Cancel" was chosen (-1) or ESC was pressed (255) + [ $retval -eq $SUCCESS ] || return $retval + + # + # Call the below ``modifier functions'' whose job it is to take + # input from the user and assign the newly-acquired values back + # to the ipaddr, netmask, and options variables for us to re- + # read and display in the summary dialog. + # + case "$tag" in + X\ *) break;; + 2\ *) # + # Do not proceed if/when there are NFS-mounts currently + # active. If the network is changed while NFS-exported + # directories are mounted, the system may hang (if any + # NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_dhcp_status" \ + "$interface" "$dhcp_status" )" + local message="$( + printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" + )" + f_dialog_msgbox "$message" + continue + fi + + # + # Toggle DHCP status + # + if [ "$dhcp_status" = "$msg_enabled" ]; then + dhcp= + else + trap - SIGINT + ( # Execute within sub-shell to allow/catch Ctrl-C + trap 'exit $FAILURE' SIGINT + msg=$( printf "$msg_scanning_for_dhcp" "$interface" ) + if [ "$USE_XDIALOG" ]; then + ( + f_quietly ifconfig $interface delete + f_quietly dhclient $interface + ) | + f_xdialog_info "$msg" + else + f_dialog_info "$msg" + f_quietly ifconfig $interface delete + f_quietly dhclient $interface + fi + ) + retval=$? + trap 'interrupt' SIGINT + if [ $retval -eq $SUCCESS ]; then + dhcp=1 + ipaddr=$( f_ifconfig_inet $interface ) + netmask=$( f_ifconfig_netmask $interface ) + options= + + # Fixup search/domain in resolv.conf(5) + hostname=$( f_sysrc_get \ + 'hostname:-$(hostname)' ) + f_dialog_resolv_conf_update "$hostname" + fi + fi + ;; + 3\ *) f_dialog_input_ipaddr "$interface" "$ipaddr" + [ $? -eq $SUCCESS ] && dhcp=;; + 4\ *) f_dialog_input_netmask "$interface" "$netmask" + [ $? -eq $SUCCESS -a "$_netmask" ] && dhcp=;; + 5\ *) f_dialog_menu_media_options "$interface" "$options" + [ $? -eq $SUCCESS ] && dhcp=;; + esac + done + + # + # Save only if the user changed at least one feature of the interface + # + if [ "$ipaddr" != "$ipaddr_orig" -o \ + "$netmask" != "$netmask_orig" -o \ + "$options" != "$options_orig" -o \ + "$dhcp" != "$dhcp_orig" ] + then + f_dialog_info "$( printf "$msg_saving_network_interface" \ + "$interface" )" + + local value= + if [ "$dhcp" ]; then + f_sysrc_delete defaultrouter + value=DHCP + else + value="inet $ipaddr netmask $netmask" + value="$value${options:+ }$options" + fi + + f_sysrc_set ifconfig_$interface "$value" + fi + + # + # Re/Apply the settings if desired + # + if [ ! "$dhcp" ]; then + f_dialog_yesno "Would you like to bring the $interface" \ + "interface up right now?" + if [ $? -eq $SUCCESS ]; then + f_dialog_info "$( printf "$msg_bring_interface_up" \ + "$interface" )" + + local dr="$( f_sysrc_get defaultrouter )" err + if [ "$dr" = "NO" -o ! "$dr" ]; then + dr=$( f_route_get_default ) + [ "$dr" ] && f_sysrc_set defaultrouter "$dr" + fi + # + # Make a backup of resolv.conf(5) before using + # ifconfig(8) and then restore it afterward. This + # allows preservation of nameservers acquired via + # DHCP on FreeBSD-8.x (normally lost as ifconfig(8) + # usage causes dhclient(8) to exit which scrubs + # resolv.conf(5) by-default upon termination). + # + f_quietly cp -fp "$RESOLV_CONF" "$RESOLV_CONF.$$" + err=$( ifconfig $interface inet $ipaddr \ + netmask $netmask $options 2>&1 ) + if [ $? -eq $SUCCESS ]; then + if [ "$dr" -a "$dr" != "NO" ]; then + err=$( route add default "$dr" 2>&1 ) + [ $? -eq $SUCCESS ] || \ + dialog_msgbox "$err" + fi + else + dialog_msgbox "$err" + fi + if cmp -s "$RESOLV_CONF" "$RESOLV_CONF.$$"; then + f_quietly rm -f "$RESOLV_CONF.$$" + else + f_quietly mv -f "$RESOLV_CONF.$$" "$RESOLV_CONF" + fi + fi + fi + + return $SUCCESS +} + +fi # ! $_NETWORKING_DEVICE_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/hostname.subr b/usr.sbin/bsdconfig/networking/include/hostname.subr new file mode 100644 index 0000000000000..0f671fe2f56e4 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/hostname.subr @@ -0,0 +1,211 @@ +if [ ! "$_NETWORKING_HOSTNAME_SUBR" ]; then _NETWORKING_HOSTNAME_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/sysrc.subr +f_include $BSDCFG_LIBE/include/dialog.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/resolv.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_validate_hostname $hostname +# +# Returns zero if the given argument (a fully-qualified hostname) is compliant +# with standards set-forth in RFC's 952 and 1123 of the Network Working Group: +# +# RFC 952 - DoD Internet host table specification +# http://tools.ietf.org/html/rfc952 +# +# RFC 1123 - Requirements for Internet Hosts - Application and Support +# http://tools.ietf.org/html/rfc1123 +# +# See http://en.wikipedia.org/wiki/Hostname for a brief overview. +# +# The return status for invalid hostnames is one of: +# 255 Entire hostname exceeds the maximum length of 255 characters. +# 63 One or more individual labels within the hostname (separated by +# dots) exceeds the maximum of 63 characters. +# 1 One or more individual labels within the hostname contains one +# or more invalid characters. +# 2 One or more individual labels within the hostname starts or +# ends with a hyphen (hyphens are allowed, but a label cannot +# begin or end with a hyphen). +# 3 One or more individual labels within the hostname are null. +# +# If the hostname is determined to be invalid, the appropriate error will be +# displayed using the f_dialog_msgbox function. +# +f_dialog_validate_hostname() +{ + local fqhn="$1" + + ( # Operate within a sub-shell to protect the parent environment + + # Return error if the hostname exceeds 255 characters + [ ${#fqhn} -gt 255 ] && exit 255 + + IFS="." # Split on `dot' + for label in $fqhn; do + + # Return error if the label exceeds 63 characters + [ ${#label} -gt 63 ] && exit 63 + + # Return error if the label is null + [ "$label" ] || exit 3 + + # Return error if label begins/ends with dash + case "$label" in + -*|*-) exit 2 + esac + + # Return error if the label contains any invalid chars + echo "$label" | grep -q '^[[:alnum:]-]*$' || exit 1 + + done + ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_contains_invalid_chars" "$fqhn" )";; + 2) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_starts_or_ends_with_hyphen" "$fqhn" )";; + 3) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_is_null" "$fqhn" )";; + 63) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_exceeds_max_length" "$fqhn" )";; + 255) f_dialog_msgbox "$( printf \ + "$msg_hostname_exceeds_max_length" "$fqhn" )";; + esac + + return $retval +} + +# f_dialog_input_hostname +# +# Edits the current hostname. +# +f_dialog_input_hostname() +{ + local hostname="$( f_sysrc_get 'hostname:-$(hostname)' )" + local hostname_orig="$hostname" # for change-tracking + + local msg + if [ "$USE_XDIALOG" ]; then + msg="$xmsg_please_enter_fqhn" + else + msg="$msg_please_enter_fqhn" + fi + + local hline="$hline_alnum_punc_tab_enter" + + # + # Loop until the user provides taint-free input. + # + local size height width + while :; do + + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hostname" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$hostname\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + hostname=$( f_dialog_inputstr ) + + [ $retval -eq $SUCCESS ] || return $retval + + # Taint-check the user's input + f_dialog_validate_hostname "$hostname" && break + + done + + # + # Save hostname only if the user changed the hostname. + # + if [ "$hostname" != "$hostname_orig" ]; then + f_dialog_info "$msg_saving_hostname" + f_sysrc_set hostname "$hostname" + fi + + # + # Update resolv.conf(5) search/domain directives + # + f_dialog_resolv_conf_update "$hostname" + + # + # Only ask to apply setting if the current hostname is different than + # the stored configuration (in rc.conf(5)). + # + if [ "$( hostname )" != "$( f_sysrc_get hostname )" ]; then + [ ! "$USE_XDIALOG" ] && dialog_clear + + # + # If connected via ssh(1) and performing X11-Forwarding, don't + # allow the hostname to be changed to prevent the fatal error + # "X11 connection rejected because of wrong authentication." + # + if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" ]; then + f_dialog_msgbox "$( + printf "$msg_activate_hostname_x11warning" \ + "$( hostname )" "$hostname" + )" + else + f_dialog_yesno "$( + printf "$msg_activate_hostname" \ + "$( hostname )" "$hostname" \ + )" \ + && hostname "$hostname" + fi + fi + + return $SUCCESS +} + +fi # ! $_NETWORKING_HOSTNAME_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/ipaddr.subr b/usr.sbin/bsdconfig/networking/include/ipaddr.subr new file mode 100644 index 0000000000000..7333302c98286 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/ipaddr.subr @@ -0,0 +1,236 @@ +if [ ! "$_NETWORKING_IPADDR_SUBR" ]; then _NETWORKING_IPADDR_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/sysrc.subr +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/strings.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_ifconfig_inet $interface +# +# Returns the IPv4 address associated with $interface. +# +f_ifconfig_inet() +{ + local interface="$1" + ifconfig "$interface" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( $1 == "inet" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_dialog_validate_ipaddr $ipaddr +# +# Returns zero if the given argument (an IP address) is of the proper format. +# +# The return status for invalid IP address is one of: +# 1 One or more individual octets within the IP address (separated +# by dots) contains one or more invalid characters. +# 2 One or more individual octets within the IP address are null +# and/or missing. +# 3 One or more individual octets within the IP address exceeds the +# maximum of 255 (or 2^8, being an octet comprised of 8 bits). +# 4 The IP address has either too few or too many octets. +# +# If the IP address is determined to be invalid, the appropriate error will be +# displayed using the above dialog_msgbox function. +# +f_dialog_validate_ipaddr() +{ + local ip="$1" + + ( # Operate within a sub-shell to protect the parent environment + + # Track number of octets for error checking + noctets=0 + + IFS="." # Split on `dot' + for octet in $ip; do + + # Return error if the octet is null + [ "$octet" ] || exit 2 + + # Return error if not a whole integer + f_isinteger "$octet" || exit 1 + + # Return error if not a positive integer + [ $octet -ge 0 ] || exit 1 + + # Return error if the octet exceeds 255 + [ $octet -gt 255 ] && exit 3 + + noctets=$(( $noctets + 1 )) + + done + + [ $noctets -eq 4 ] || exit 4 + ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip" )";; + 2) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_is_null" "$ip" )";; + 3) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_exceeds_max_value" "$ip" )";; + 4) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_missing_or_extra" "$ip" )";; + esac + + return $retval +} + +# f_dialog_input_ipaddr $interface $ipaddr +# +# Allows the user to edit a given IP address. If the user does not cancel or +# press ESC, the $ipaddr environment variable will hold the newly-configured +# value upon return. +# +# Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the +# netmask at the same time as the IP address. If such a format is entered by +# the user, the $netmask environment variable will hold the newly-configured +# netmask upon return. +# +f_dialog_input_ipaddr() +{ + local interface="$1" _ipaddr="$2" _input + + # + # Return with-error when there are NFS-mounts currently active. If the + # IP address is changed while NFS-exported directories are mounted, the + # system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_ipaddr" \ + "$interface" "$_ipaddr" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_ipaddr" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_ipaddr\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + _input=$( f_dialog_inputstr ) + + # + # Return error status if: + # - User has not made any changes to the given value + # - User has either pressed ESC or chosen Cancel/No + # + [ "$_ipaddr" = "$_input" ] && return $FAILURE + [ $retval -eq $SUCCESS ] || return $retval + + # Return success if NULL value was entered + [ "$_input" ] || return $SUCCESS + + # Take only the first "word" of the user's input + _ipaddr="$_input" + _ipaddr="${_ipaddr%%[$IFS]*}" + + # Taint-check the user's input + f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break + done + + # + # Support the syntax: IP_ADDRESS/NBITS + # + local _netmask="" + case "$_ipaddr" in + */*) + local nbits="${_ipaddr#*/}" n=0 + _ipaddr="${_ipaddr%%/*}" + + # + # Taint-check $nbits to be (a) a positive whole-integer, + # and (b) to be less than or equal to 32. Otherwise, set + # $n so that the below loop never executes. + # + ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \ + || n=4 + + while [ $n -lt 4 ]; do + _netmask="$_netmask${_netmask:+.}$(( + (65280 >> ($nbits - 8 * $n) & 255) + * ((8*$n) < $nbits & $nbits <= (8*($n+1))) + + 255 * ($nbits > (8*($n+1))) + ))" + n=$(( $n + 1 )) + done + ;; + esac + + ipaddr="$_ipaddr" + [ "$_netmask" ] && netmask="$_netmask" + + return $SUCCESS +} + +fi # ! $_NETWORKING_IPADDR_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/media.subr b/usr.sbin/bsdconfig/networking/include/media.subr new file mode 100644 index 0000000000000..0f0f9fe826974 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/media.subr @@ -0,0 +1,235 @@ +if [ ! "$_NETWORKING_MEDIA_SUBR" ]; then _NETWORKING_MEDIA_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/strings.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_ifconfig_options $interface +# +# Returns any/all extra ifconfig(8) parameters associated with $interface. +# +f_ifconfig_options() +{ + local interface="$1" + [ "$interface" ] || return $SUCCESS + + # + # Loop over the options, removing what we don't want + # + ( + set -- $( f_sysrc_get ifconfig_$interface ) + + # + # Return if the the interface is configured for DHCP + # + glob="[Dd][Hh][Cc][Pp]" + case "$*" in + $glob|[Ss][Yy][Nn][Cc]$glob|[Nn][Oo][Ss][Yy][Nn][Cc]$glob) + exit $SUCCESS + esac + + output= + while [ $# -gt 0 ]; do + case "$1" in + inet|netmask) shift 1;; + *) output="$output${output:+ }$1" + esac + shift 1 + done + echo "$output" + ) +} + +# f_ifconfig_media $interface +# +# Returns list of supported media for $interface. +# +f_ifconfig_media() +{ + local interface="$1" + ifconfig -m "$interface" 2> /dev/null | awk \ + ' + BEGIN { media_found = 0 } + { + if ( media_found == 1 ) { print; next } + } + ( $1 $2 == "supported" "media:" ) \ + { + media_found = 1 + next + } + END { exit ! media_found } + ' +} + +# f_dialog_input_options $interface +# +# Input custom interface options. +# +f_dialog_input_options() +{ + local interface="$1" + + # + # Return with-error when there are NFS-mounts currently active. If the + # options are changed while NFS-exported directories are mounted, + # the system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_options" \ + "$interface" "$options" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$( printf "$msg_please_enter_mediaopts" "$interface" )" + local hline="$hline_alnum_punc_tab_enter" + + $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --inputbox "$msg" 9 70 \ + "$options" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + local _options="$( f_dialog_inputstr )" + + [ $retval -eq $SUCCESS ] && options="$_options" + + return $retval +} + +# f_dialog_menu_media_options $interface +# +# Display a menu of additional media options for the given network interface. +# +f_dialog_menu_media_options() +{ + local interface="$1" _options="$2" + # + # Not all network interfaces support additional media options, but + # when available we should prompt the user to select from a list + # of available options (or none, as is the first/default option). + # + + # + # Return with-error when there are NFS-mounts currently active. If the + # media options are changed while NFS-exported directories are mounted, + # the system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_options" \ + "$interface" "$_options" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + # + # Build list of additional media options + # + local opt_none="$msg_no_options" + local opt_cust="$msg_custom" + local supported_media="$( + f_ifconfig_media $interface | \ + ( index=1 + + echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" + echo "'$opt_none'" + index=$(( $index + 1 )) + + echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" + echo "'$opt_cust'" + index=$(( $index + 1 )) + + while read media_options; do + [ $index -lt ${#DIALOG_MENU_TAGS} ] || break + echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" + echo "'$media_options'" + index=$(( $index + 1 )) + done + ) + )" + + local msg + if [ "$USE_XDIALOG" ]; then + msg=$( printf "$xmsg_supported_media_options" \ + "$interface" "$interface" ) + else + msg=$( printf "$msg_supported_media_options" \ + "$interface" "$interface" ) + fi + + local hline="$hline_arrows_tab_enter" + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" 21 60 12 \ + $supported_media \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + if [ $retval -eq $SUCCESS ]; then + local tag="$( f_dialog_menutag )" + options=$( eval f_dialog_menutag2item \"\$tag\" \ + $supported_media ) + [ "$options" = "$opt_none" ] && options= + + if [ "$options" = "$opt_cust" ]; then + options="$_options" + f_dialog_input_options "$interface" + retval=$? + fi + fi + + return $retval +} + +fi # ! $_NETWORKING_MEDIA_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/messages.subr b/usr.sbin/bsdconfig/networking/include/messages.subr new file mode 100644 index 0000000000000..dac8e20d7baa2 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/messages.subr @@ -0,0 +1,100 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER" +hline_arrows_tab_enter="Press arrows, TAB or ENTER" +hline_num_punc_tab_enter="Use numbers, punctuation, TAB or ENTER" +msg_activate_default_router="Would you like to activate the new defaultrouter right now?\nIf you choose NO or press ESC, changes will be applied\nduring the next boot.\n\n Current Default Router: %s\n New Default Router: %s\n" +msg_activate_hostname="Would you like to activate the new hostname right now?\nIf you choose NO or press ESC, changes will be applied\nduring the next boot.\n\n Current Hostname: %s\n New Hostname: %s\n\nNOTE: Your shell prompt may still reflect the original\nhostname until your next login." +msg_activate_hostname_x11warning="WARNING! Activating the new hostname during an X11-Forwarded\n ssh(1) session will cause an X11 authentication error.\n\n Current Hostname: %s\n New Hostname: %s\n\nNOTE: Settings will become active upon reboot or if you\n relaunch this utility either locally or on the console." +msg_add="Add" +msg_add_nameserver="Add a new nameserver" +msg_bring_interface_up="Would you like to bring the %s interface up right now?" +msg_cancel="Cancel" +msg_current_default_router="Current Default Route/Gateway: %s" +msg_current_dhcp_status="Current DHCP status for %s: %s" +msg_current_ipaddr="Current IP Address for %s: %s" +msg_current_options="Current Options for %s: %s" +msg_current_subnet="Current Subnet Mask for %s: %s" +msg_custom="Custom (Manual)" +msg_default_router="Default Router/Gateway" +msg_dhcp="DHCP" +msg_disabled="Disabled" +msg_dns_configuration="DNS Nameserver Configuration:\nChoose Exit when finished else Cancel." +msg_dns_nameservers="DNS nameservers" +msg_enabled="Enabled" +msg_exit="Exit" +msg_hostname_domain="Hostname/Domain" +msg_hostname_exceeds_max_length="ERROR! The hostname entered exceeds the maximum length of\n255 characters.\n\nInvalid Hostname: %s" +msg_hostname_label_contains_invalid_chars="ERROR! One or more individual labels within the hostname\n(separated by dots) contains one or more invalid characters.\nLabels are case-insensitive and must contain only 0-9, a-z,\n or dash (though must not begin with or end with a dash).\n\nInvalid Hostname: %s" +msg_hostname_label_exceeds_max_length="ERROR! One or more individual labels within the hostname\n(separated by dots) exceeds the maximum of 63 characters.\n\nInvalid Hostname: %s" +msg_hostname_label_is_null="ERROR! One or more individual labels within the hostname\n(separated by dots) are null.\n\nInvalid Hostname: %s" +msg_hostname_label_starts_or_ends_with_hyphen="ERROR! One or more individual labels within the hostname\n(separated by dots) starts or ends with a hyphen (hyphens\nare allowed, but a label cannot begin or end with a hyphen).\n\nInvalid Hostname: %s" +msg_internal_error_nsindex_value="FATAL! dialog_input_nameserver_edit_awk: variable\nnsindex must be a whole positive integer greater-\nthan or equal-to zero.\n\nInvalid nsindex: %s" +msg_ipaddr4="ipaddr" +msg_ipv4_addr_octet_contains_invalid_chars="ERROR! One or more individual octets within the IP address\n(separated by dots) contains one or more invalid characters.\nOctets must contain only the characters 0-9.\n\nInvalid IP Address: %s" +msg_ipv4_addr_octet_exceeds_max_value="ERROR! One or more individual octets within the IP address\n(separated by dots) exceeds the maximum of 255.\n\nInvalid IP Address: %s" +msg_ipv4_addr_octet_is_null="ERROR! One or more individual octets within the IP address\n(separated by dots) are null and/or missing.\n\nInvalid IP Address: %s" +msg_ipv4_addr_octet_missing_or_extra="ERROR! The IP address entered has either too few or too many\noctets.\n\nInvalid IP Address: %s" +msg_ipv4_mask_field_contains_invalid_chars="ERROR! One or more individual fields within the subnet mask\n(separated by dots) contains one or more invalid characters.\n\nInvalid Subnet Mask: %s" +msg_ipv4_mask_field_exceeds_max_value="ERROR! One or more individual fields within the subnet mask\n(separated by dots) exceeds the maximum of 255.\n\nInvalid Subnet Mask: %s" +msg_ipv4_mask_field_invalid_value="ERROR! One or more individual fields within the subnet mask\n(separated by dots) contains one or more invalid integers.\nFields must be one of 0/128/192/224/240/248/252/254/255.\n\nInvalid Subnet Mask: %s" +msg_ipv4_mask_field_is_null="ERROR! One or more individual fields within the subnet mask\n(separated by dots) are null and/or missing.\n\nInvalid Subnet Mask: %s" +msg_ipv4_mask_field_missing_or_extra="ERROR! The subnet mask entered has either too few or too many\nfields.\n\nInvalid Subnet Mask: %s" +msg_netmask="netmask" +msg_network_configuration="%s Network Configuration:\nChoose Save/Exit when finished or Cancel." +msg_network_interfaces="Network Interfaces" +msg_network_management="Network Management" +msg_networking_devices="Networking Devices" +msg_nfs_mounts_may_cause_hang="WARNING! Changing this setting while NFS directories are\nmounted may cause the system to hang. Please exit this\nutility and dismount any/all remaining NFS-mounts before\nattempting to change this setting.\n\n%s" +msg_no_network_interfaces="No network interfaces detected." +msg_no_options="No options (Default)" +msg_ok="OK" +msg_options="options" +msg_please_enter_default_router="Please enter the IP address of your default\nrouter/gateway. The address entered will be\napplied as the default gateway for all interfaces\nusing route(4)." +msg_please_enter_fqhn="Please enter your fully qualified hostname (e.g. full.example.com). The\ndomain portion of the hostname will be configured in resolv.conf(5)." +msg_please_enter_mediaopts="Please enter additional network media options to be passed to ifconfig(8) for the %s interface:" +msg_please_enter_nameserver="Please enter the new IP address of the DNS nameserver:" +msg_please_enter_nameserver_existing="Please enter the new IP address of the DNS nameserver\n(set to the NULL string [Ctrl-U] to remove entry):" +msg_please_enter_new_ip_addr="Please enter the new IP address of the %s interface:" +msg_please_enter_subnet_mask="Please enter the new network subnet mask for the %s interface:" +msg_probing_network_interfaces="Probing network interface devices..." +msg_removing_nameserver="Removing DNS nameserver from resolv.conf(5)..." +msg_resolv_conf_changed_while_editing="ERROR! resolv.conf(5) has changed while editing this\nvalue. Please try again after waiting a few seconds." +msg_resolv_conf_entry_no_longer_exists="ERROR! The entry you are trying to edit no longer\nexists in resolv.conf(5). Please try again after\nwaiting a few seconds." +msg_return_to_previous_menu="Return to previous menu" +msg_save_exit="Save/Exit" +msg_saving_default_router="Saving new default router/gateway settings..." +msg_saving_hostname="Saving new hostname/domain settings..." +msg_saving_nameserver="Saving new DNS nameserver to resolv.conf(5)..." +msg_saving_nameserver_existing="Editing DNS nameserver in resolv.conf(5)..." +msg_saving_network_interface="Saving %s network interface settings..." +msg_scanning_for_dhcp="Scanning for DHCP servers on %s interface..." +msg_select_network_interface="Select a network interface to configure.\n\n* Interface is marked as \"active\"" +msg_supported_media_options="Below is a list of supported media options for the %s interface. Please select the options that you would like to set for the %s network interface" +xmsg_please_enter_fqhn="Please enter your fully qualified hostname (e.g. foo.bar.com).\n The domain portion of the hostname will be configured in resolv.conf(5)." +xmsg_please_enter_nameserver_existing="Please enter the new IP address of the DNS nameserver\n(set to the NULL string to remove entry):" +xmsg_supported_media_options="Below is a list of supported media options for the %s interface. Please\nselect the options that you would like to set for the %s network interface" diff --git a/usr.sbin/bsdconfig/networking/include/netmask.subr b/usr.sbin/bsdconfig/networking/include/netmask.subr new file mode 100644 index 0000000000000..169acca007df4 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/netmask.subr @@ -0,0 +1,215 @@ +if [ ! "$_NETWORKING_NETMASK_SUBR" ]; then _NETWORKING_NETMASK_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/strings.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_ifconfig_netmask $interface +# +# Returns the IPv4 subnet mask associated with $interface. +# +f_ifconfig_netmask() +{ + local interface="$1" octets + octets=$( ifconfig "$interface" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( $1 == "inet" ) \ + { + printf "%s %s %s %s\n", + substr($4,3,2), + substr($4,5,2), + substr($4,7,2), + substr($4,9,2) + found = 1 + exit + } + END { exit ! found } + ' ) || return $FAILURE + + local octet netmask= + for octet in $octets; do + netmask="$netmask${netmask:+.}$( printf "%u" "0x$octet" )" + done + echo $netmask +} + +# f_dialog_validate_netmask $netmask +# +# Returns zero if the given argument (a subnet mask) is of the proper format. +# +# The return status for invalid IP address is one of: +# 1 One or more individual fields within the subnet mask (separated +# by dots) contains one or more invalid characters. +# 2 One or more individual fields within the subnet mask are null +# and/or missing. +# 3 One or more individual fields within the subnet mask exceeds +# the maximum of 255 (a full 8-bit register). +# 4 The subnet mask has either too few or too many fields. +# 5 One or more individual fields within the subnet mask is an +# invalid integer (only 0,128,192,224,240,248,252,254,255 are +# valid integers). +# +# If the subnet mask is determined to be invalid, the appropriate error will be +# displayed using the f_dialog_msgbox function. +# +f_dialog_validate_netmask() +{ + local mask="$1" + + ( # Operate within a sub-shell to protect the parent environment + + # Track number of fields for error checking + nfields=0 + + IFS="." # Split on `dot' + for field in $mask; do + + # Return error if the field is null + [ "$field" ] || exit 2 + + # Return error if not a whole positive integer + f_isinteger "$field" || exit 1 + + # Return error if the field exceeds 255 + [ $field -gt 255 ] && exit 3 + + # Return error if the field is an invalid integer + case "$field" in + 0|128|192|224|240|248|252|254|255) :;; + *) exit 5;; + esac + + nfields=$(( $nfields + 1 )) + + done + + [ $nfields -eq 4 ] || exit 4 + ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_contains_invalid_chars" "$mask" )";; + 2) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_is_null" "$mask" )";; + 3) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_exceeds_max_value" "$mask" )";; + 4) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_missing_or_extra" "$mask" )";; + 5) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_invalid_value" "$mask" )";; + esac + + return $retval +} + +# f_dialog_input_netmask $interface $netmask +# +# Edits the IP netmask of the given interface. +# +f_dialog_input_netmask() +{ + local interface="$1" _netmask="$2" _input + + # + # Return with-error when there are NFS-mounts currently active. If the + # subnet mask is changed while NFS-exported directories are mounted, + # the system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_subnet" \ + "$interface" "$_netmask" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$( printf "$msg_please_enter_subnet_mask" "$interface" )" + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_netmask" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_netmask\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + _input=$( f_dialog_inputstr ) + + # + # Return error status if: + # - User has not made any changes to the given value + # - User has either pressed ESC or chosen Cancel/No + # + [ "$_netmask" = "$_input" ] && return $FAILURE + [ $retval -eq $SUCCESS ] || return $retval + + # Return success if NULL value was entered + [ "$_input" ] || return $SUCCESS + + # Take only the first "word" of the user's input + _netmask="$_input" + _netmask="${_netmask%%[$IFS]*}" + + # Taint-check the user's input + f_dialog_validate_netmask "$_netmask" && break + done + + netmask="$_netmask" +} + +fi # ! $_NETWORKING_NETMASK_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/resolv.subr b/usr.sbin/bsdconfig/networking/include/resolv.subr new file mode 100644 index 0000000000000..8eab18e186a02 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/resolv.subr @@ -0,0 +1,509 @@ +if [ ! "$_NETWORKING_RESOLV_SUBR" ]; then _NETWORKING_RESOLV_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/strings.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Path to resolv.conf(5). +# +: ${RESOLV_CONF:="/etc/resolv.conf"} + +# +# When updating resolv.conf(5), should we populate the `search' directive with +# all possible sub-domains? In example, if the domain is "sub.domain.com", when +# the below option is set to 1, include both "sub.domain.com" and "domain.com" +# in the `search' directive, otherwise use only "sub.domain.com". +# +# When enabled (set to 1), specify the minimum number of dots required for each +# `search' domain by setting the second option below, `RESOLVER_SEARCH_NDOTS'. +# +: ${RESOLVER_SEARCH_DOMAINS_ALL:=1} +: ${RESOLVER_SEARCH_NDOTS:=1} + +############################################################ FUNCTIONS + +# f_resolv_conf_domain +# +# Returns the domain configured in resolv.conf(5). +# +f_resolv_conf_domain() +{ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( tolower($1) == "domain" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_resolv_conf_search +# +# Returns the search configured in resolv.conf(5). +# +f_resolv_conf_search() +{ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + { + tl0 = tolower($0) + if ( match(tl0, /^[[:space:]]*search[[:space:]]+/) ) { + search = substr($0, RLENGTH + 1) + sub(/[[:space:]]*#.*$/, "", search) + gsub(/[[:space:]]+/, " ", search) + print search + found = 1 + exit + } + } + END { exit ! found } + ' +} + +# f_resolv_conf_nameservers +# +# Returns nameserver(s) configured in resolv.conf(5). +# +f_resolv_conf_nameservers() +{ + awk \ + ' + BEGIN { found = 0 } + ( $1 == "nameserver" ) \ + { + print $2 + found = 1 + } + END { exit ! found } + ' \ + "$RESOLV_CONF" 2> /dev/null +} + +# f_dialog_resolv_conf_update $hostname +# +# Updates the search/domain directives in resolv.conf(5) given a valid fully- +# qualified hostname. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_dialog_resolv_conf_update_awk=' +# Variables that should be defined on the invocation line: +# -v domain="domain" +# -v search_all="0|1" +# -v search_ndots="1+" +# +BEGIN { + domain_found = search_found = 0 + + if ( search_all ) { + search = "" + subdomain = domain + if ( search_ndots < 1 ) + search_ndots = 1 + + ndots = split(subdomain, labels, ".") - 1 + while ( ndots-- >= search_ndots ) { + if ( length(search) ) search = search " " + search = search subdomain + sub(/[^.]*\./, "", subdomain) + } + } + else search = domain +} +{ + if ( domain_found && search_found ) { print; next } + + tl0 = tolower($0) + if ( ! domain_found && \ + match(tl0, /^[[:space:]]*domain[[:space:]]+/) ) \ + { + if ( length(domain) ) { + printf "%s%s\n", substr($0, 0, RLENGTH), domain + domain_found = 1 + } + } + else if ( ! search_found && \ + match(tl0, /^[[:space:]]*search[[:space:]]+/) ) \ + { + if ( length(search) ) { + printf "%s%s\n", substr($0, 0, RLENGTH), search + search_found = 1 + } + } + else print +} +END { + if ( ! search_found && length(search) ) + printf "search\t%s\n", search + if ( ! domain_found && length(domain) ) + printf "domain\t%s\n", domain +} +' +f_dialog_resolv_conf_update() +{ + local hostname="$1" + + # + # Extrapolate the desired domain search parameter for resolv.conf(5) + # + local search ndots domain="${hostname#*.}" + if [ "$RESOLVER_SEARCH_DOMAINS_ALL" = "1" ]; then + search="" + ndots=$( IFS=.; set -- $domain; echo $(( $# - 1 )) ) + while [ $ndots -ge ${RESOLVER_SEARCH_NDOTS:-1} ]; do + search="$search${search:+ }$domain" + domain="${domain#*.}" + ndots=$(( $ndots - 1 )) + done + domain="${hostname#*.}" + else + search="$domain" + fi + + # + # Save domain/search information only if different from resolv.conf(5) + # + if [ "$domain" != "$( f_resolv_conf_domain )" -o \ + "$search" != "$( f_resolv_conf_search )" ] + then + f_dialog_info "Saving new domain/search settings" \ + "to resolv.conf(5)..." + + # + # Create a new temporary file to write our resolv.conf(5) + # update with our new `domain' and `search' directives. + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions and ownership (mktemp(1) creates the + # temporary file with 0600 permissions -- change the + # permissions and ownership to match resolv.conf(5) before + # we write it out and mv(1) it into place). + # + local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null )" + local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null )" + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on resolv.conf(5), replacing only the last + # occurrences of `domain' and `search' directives (or add + # them to the top if not found), in strict-adherence to the + # following entry in resolver(5): + # + # The domain and search keywords are mutually exclusive. + # If more than one instance of these keywords is present, + # the last instance will override. + # + # NOTE: If RESOLVER_SEARCH_DOMAINS_ALL is set to `1' in the + # environment, all sub-domains will be added to the `search' + # directive, not just the FQDN. + # + local domain="${hostname#*.}" new_contents + [ "$domain" = "$hostname" ] && domain= + new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null ) + new_contents=$( echo "$new_contents" | awk \ + -v domain="$domain" \ + -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \ + -v search_ndots="${RESOLVER_SEARCH_NDOTS:-1}" \ + "$f_dialog_resolv_conf_update_awk" ) + + # + # Write the temporary file contents and move the temporary + # file into place. + # + echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE + f_quietly mv "$tmpfile" "$RESOLV_CONF" + + fi +} + +# f_dialog_input_nameserver [ $n $nameserver ] +# +# Allows the user to edit a given nameserver. The first argument is the +# resolv.conf(5) nameserver ``instance'' integer. For example, this will be one +# if editing the first nameserver instance, two if editing the second, three if +# the third, ad nauseum. If this argument is zero, null, or missing, the value +# entered by the user (if non-null) will be added to resolv.conf(5) as a new +# `nameserver' entry. The second argument is the IPv4 address of the nameserver +# to be edited -- this will be displayed as the initial value during the edit. +# +# Taint-checking is performed when editing an existing entry (when the second +# argument is one or higher) in that the first argument must match the current +# value of the Nth `nameserver' instance in resolv.conf(5) else an error is +# generated discarding any/all changes. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_dialog_input_nameserver_edit_awk=' +# Variables that should be defined on the invocation line: +# -v nsindex="1+" +# -v old_value="..." +# -v new_value="..." +# +BEGIN { + if ( nsindex < 1 ) exit 1 + found = n = 0 +} +{ + if ( found ) { print; next } + + if ( match(tolower($0), /^[[:space:]]*nameserver[[:space:]]+/)) { + if ( ++n == nsindex ) { + if ( $2 != old_value ) exit 2 + if ( new_value != "" ) printf "%s%s\n", \ + substr($0, 0, RLENGTH), new_value + found = 1 + } + else print + } + else print +} +END { if ( ! found ) exit 3 } +' +f_dialog_input_nameserver() +{ + local index="${1:-0}" old_ns="$2" new_ns + local ns="$old_ns" + + # + # Perform sanity checks + # + f_isinteger "$index" || return $FAILURE + [ $index -ge 0 ] || return $FAILURE + + local msg + if [ $index -gt 0 ]; then + if [ "$USE_XDIALOG" ]; then + msg="$xmsg_please_enter_nameserver_existing" + else + msg="$msg_please_enter_nameserver_existing" + fi + else + msg="$msg_please_enter_nameserver" + fi + + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$ns" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$ns\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + new_ns=$( f_dialog_inputstr ) + + [ $retval -eq $SUCCESS ] || return $retval + + # Take only the first "word" of the user's input + new_ns="${new_ns%%[$IFS]*}" + + # Taint-check the user's input + [ "$new_ns" ] || break + f_dialog_validate_ipaddr "$new_ns" && break + + # Update prompt to allow user to re-edit previous entry + ns="$new_ns" + + done + + # + # Save only if the user changed the nameserver. + # + if [ $index -eq "0" -a "$new_ns" ]; then + f_dialog_info "$msg_saving_nameserver" + printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF" + return $SUCCESS + elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then + if [ "$new_ns" ]; then + msg="$msg_saving_nameserver_existing" + else + msg="$msg_removing_nameserver" + fi + f_dialog_info "$msg" + + # + # Create a new temporary file to write our new resolv.conf(5) + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Quietly fixup permissions and ownership + # + local mode owner + mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null ) + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on resolv.conf(5) + # + local new_contents + new_contents=$( awk -v nsindex="$index" \ + -v old_value="$old_ns" \ + -v new_value="$new_ns" \ + "$f_dialog_input_nameserver_edit_awk" \ + "$RESOLV_CONF" ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_die 1 "$msg_internal_error_nsindex_value" "$nsindex";; + 2) f_dialog_msgbox "$msg_resolv_conf_changed_while_editing" + return $retval;; + 3) f_dialog_msgbox "$msg_resolv_conf_entry_no_longer_exists" + return $retval;; + esac + + # + # Write the temporary file contents and move the temporary + # file into place. + # + echo "$new_contents" > "$tmpfile" || return $FAILURE + f_quietly mv "$tmpfile" "$RESOLV_CONF" + fi +} + +# f_dialog_menu_nameservers +# +# Edit the nameservers in resolv.conf(5). +# +f_dialog_menu_nameservers() +{ + local opt_exit="$msg_return_to_previous_menu" + local opt_add="$msg_add_nameserver" + local hline="$hline_arrows_tab_enter" + local prompt size + + # + # Loop forever until the user has finished configuring nameservers + # + prompt="$msg_dns_configuration" + while :; do + # + # Re/Build list of nameservers + # + local nameservers="$( f_resolv_conf_nameservers )" + local menu_list="$( + index=1 + + echo "'X $msg_exit' '$opt_exit'" + index=$(( $index + 1 )) + + echo "'A $msg_add' '$opt_add'" + index=$(( $index + 1 )) + + for ns in $nameservers; do + [ $index -lt ${#DIALOG_MENU_TAGS} ] || break + tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) + echo "'$tag nameserver' '$ns'" + index=$(( $index + 1 )) + done + )" + + # + # Display configuration-edit menu + # + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + local tag="$( f_dialog_menutag )" ns="" + + # Return if "Cancel" was chosen (-1) or ESC was pressed (255) + [ $retval -eq $SUCCESS ] || return $retval + + case "$tag" in + "X $msg_exit") break;; + "A $msg_add") + f_dialog_input_nameserver + ;; + *) + n=$( eval f_dialog_menutag2index \"\$tag\" $menu_list ) + ns=$( eval f_dialog_menutag2item \"\$tag\" $menu_list ) + f_dialog_input_nameserver $(( $n - 2 )) "$ns" + ;; + esac + done +} + +fi # ! $_NETWORKING_RESOLV_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/routing.subr b/usr.sbin/bsdconfig/networking/include/routing.subr new file mode 100644 index 0000000000000..f4c70176198a6 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/routing.subr @@ -0,0 +1,168 @@ +if [ ! "$_NETWORKING_ROUTING_SUBR" ]; then _NETWORKING_ROUTING_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/sysrc.subr +f_include $BSDCFG_LIBE/include/dialog.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_route_get_default +# +# Returns the IP address of the currently active default router. +# +f_route_get_default() +{ + route -n get default 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( $1 == "gateway:" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_dialog_input_defaultrouter +# +# Edits the default router. +# +f_dialog_input_defaultrouter() +{ + # + # Get the defaultrouter. When this is not configured, the default is + # "NO", however we don't ever want to present this default to the user + # in the following dialog. If the current value is "NO", then try to + # obtain the value from the running system using route(8). + # + # NOTE: Our `f_route_get_default' function will return NULL if the + # system does not have an active default router set (which is what we + # want). + # + local defaultrouter="$( f_sysrc_get 'defaultrouter:-NO' )" + local defaultrouter_orig="$defaultrouter" # for change-tracking + case "$defaultrouter" in + [Nn][Oo]) + defaultrouter=$( f_route_get_default ) + ;; + esac + + # + # Return with-error when there are NFS-mounts currently active. If the + # default router/gateway is changed while NFS-exported directories are + # mounted, the system will hang. + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_default_router" \ + "$defaultrouter" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$msg_please_enter_default_router" + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$defaultrouter" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$defaultrouter\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + defaultrouter=$( f_dialog_inputstr ) + + [ "$defaultrouter" ] || return $SUCCESS + [ $retval -eq $SUCCESS ] || return $retval + + # Taint-check the user's input + f_dialog_validate_ipaddr "$defaultrouter" && break + done + + # + # Save only if the user changed the default router/gateway. + # + if [ "$defaultrouter" != "$defaultrouter_orig" ]; then + f_dialog_info "$msg_saving_default_router" + + # Save the default router/gateway + f_sysrc_set defaultrouter "$defaultrouter" + fi + + # + # Only ask to apply setting if the current defaultrouter is different + # than the stored configuration (in rc.conf(5)). + # + if [ "$( f_route_get_default )" != "$defaultrouter" ]; then + f_dialog_clear + f_dialog_yesno "$( + printf "$msg_activate_default_router" \ + "$( f_route_get_default )" "$defaultrouter" + )" + + if [ $? -eq $SUCCESS ]; then + local err + + # Apply the default router/gateway + f_quietly route delete default + err=$( route add default "$defaultrouter" 2>&1 ) + if [ $? -ne $SUCCESS ]; then + f_dialog_msgbox "$err" + return $FAILURE + fi + fi + fi +} + +fi # ! $_NETWORKING_ROUTING_SUBR diff --git a/usr.sbin/bsdconfig/networking/nameservers b/usr.sbin/bsdconfig/networking/nameservers new file mode 100755 index 0000000000000..ae8e812e342e4 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/nameservers @@ -0,0 +1,73 @@ +#!/bin/sh +#- +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="120.networking" +f_include $BSDCFG_LIBE/$APP_DIR/include/resolv.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_dns_nameservers" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Change the DNS nameservers +# +f_dialog_menu_nameservers + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/networking/networking b/usr.sbin/bsdconfig/networking/networking new file mode 100755 index 0000000000000..8b9cc8fab5f7b --- /dev/null +++ b/usr.sbin/bsdconfig/networking/networking @@ -0,0 +1,146 @@ +#!/bin/sh +#- +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + + menu_list=" + 'X' '$msg_exit' + '1' '$msg_hostname_domain' + '2' '$msg_network_interfaces' + '3' '$msg_default_router' + '4' '$msg_dns_nameservers' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_network_management" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + X) # Exit + break + ;; + + 1) # Hostname/Domain + $BSDCFG_LIBE/$APP_DIR/hostname ${USE_XDIALOG:+-X} + ;; + + 2) # Network Interfaces + $BSDCFG_LIBE/$APP_DIR/devices ${USE_XDIALOG:+-X} + + # Make subsequent uses of this menu faster by not performing + # "ifconfig up" (limiting the pain one must endure). See also + # `$BSDCFG_LIBE/$APP_DIR/include/device.subr'. + # + export DIALOG_MENU_NETDEV_KICK_INTERFACES= + ;; + + 3) # Default Router/Gateway + $BSDCFG_LIBE/$APP_DIR/defaultrouter ${USE_XDIALOG:+-X} + ;; + + 4) # DNS nameservers + $BSDCFG_LIBE/$APP_DIR/nameservers ${USE_XDIALOG:+-X} + ;; + + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/password/INDEX b/usr.sbin/bsdconfig/password/INDEX new file mode 100644 index 0000000000000..224897b40cb59 --- /dev/null +++ b/usr.sbin/bsdconfig/password/INDEX @@ -0,0 +1,53 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Root Password" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Set the system manager's password" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="password|password" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="password" diff --git a/usr.sbin/bsdconfig/password/Makefile b/usr.sbin/bsdconfig/password/Makefile new file mode 100644 index 0000000000000..499090708544e --- /dev/null +++ b/usr.sbin/bsdconfig/password/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/040.password +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= password + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/password/USAGE b/usr.sbin/bsdconfig/password/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/password/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/password/include/Makefile b/usr.sbin/bsdconfig/password/include/Makefile new file mode 100644 index 0000000000000..1c1d0e1eabb34 --- /dev/null +++ b/usr.sbin/bsdconfig/password/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/040.password/include +FILES= messages.subr password.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/password/include/messages.subr b/usr.sbin/bsdconfig/password/include/messages.subr new file mode 100644 index 0000000000000..ac01ccce82da8 --- /dev/null +++ b/usr.sbin/bsdconfig/password/include/messages.subr @@ -0,0 +1,35 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER" +msg_cancel="Cancel" +msg_ok="OK" +msg_password="Password" +msg_password_changed="Password successfully changed." +msg_password_is_empty="Password is empty." +msg_passwords_do_not_match="Passwords do not match." +msg_reenter_password="Re-enter Password" +msg_root_password="Root Password" diff --git a/usr.sbin/bsdconfig/password/include/password.subr b/usr.sbin/bsdconfig/password/include/password.subr new file mode 100644 index 0000000000000..846b76b54fa99 --- /dev/null +++ b/usr.sbin/bsdconfig/password/include/password.subr @@ -0,0 +1,122 @@ +if [ ! "$_PASSWORD_PASSWORD_SUBR" ]; then _PASSWORD_PASSWORD_SUBR=1 +# +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr + +APP_DIR="040.password" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_input_password +# +# Prompt the user to enter a password (twice). If the user does not cancel or +# press ESC, the $pw_password environment variable will hold the password. +# +f_dialog_input_password() +{ + local hline="$hline_alnum_punc_tab_enter" + local msg size rmsg rsize + + msg=$( printf "$msg_password" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "" \ + "$hline" ) + + rmsg=$( printf "$msg_reenter_password" ) + rsize=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$rmsg" \ + "" \ + "$hline" ) + + # + # Loop until the user provides taint-free/valid input + # + local retval _password1 _password2 + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$msg\" $size \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password1=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$rmsg\" $rsize \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password2=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for NULL entry + if ! [ "$_password1" -o "$_password2" ]; then + f_show_msg "$msg_password_is_empty" + continue + fi + + # Check for password mismatch + if [ "$_password1" != "$_password2" ]; then + f_show_msg "$msg_passwords_do_not_match" + continue + fi + + pw_password="$_password1" + break + done + + return $SUCCESS +} + +fi # ! $_PASSWORD_PASSWORD_SUBR diff --git a/usr.sbin/bsdconfig/password/password b/usr.sbin/bsdconfig/password/password new file mode 100755 index 0000000000000..2cbd54c8a8f5b --- /dev/null +++ b/usr.sbin/bsdconfig/password/password @@ -0,0 +1,82 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="040.password" +f_include $BSDCFG_LIBE/$APP_DIR/include/password.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Default login of system manager +# +USER_ROOT=root + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_root_password" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +if f_dialog_input_password; then + err=$( echo "$pw_password" | pw usermod $USER_ROOT -h 0 2>&1 ) || + f_die $? "%s" "$err" + f_show_msg "$msg_password_changed" +fi + +return $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/security/INDEX b/usr.sbin/bsdconfig/security/INDEX new file mode 100644 index 0000000000000..1a271ec48d215 --- /dev/null +++ b/usr.sbin/bsdconfig/security/INDEX @@ -0,0 +1,54 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Security" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Set Security Parameters" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="security|security" +menu_selection="kern_securelevel|kern_securelevel" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="security" diff --git a/usr.sbin/bsdconfig/security/Makefile b/usr.sbin/bsdconfig/security/Makefile new file mode 100644 index 0000000000000..c230973951b3f --- /dev/null +++ b/usr.sbin/bsdconfig/security/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/130.security +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= kern_securelevel security + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/security/USAGE b/usr.sbin/bsdconfig/security/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/security/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/security/include/Makefile b/usr.sbin/bsdconfig/security/include/Makefile new file mode 100644 index 0000000000000..7cdfcc6921440 --- /dev/null +++ b/usr.sbin/bsdconfig/security/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/130.security/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/security/include/messages.subr b/usr.sbin/bsdconfig/security/include/messages.subr new file mode 100644 index 0000000000000..162cebd8fbae9 --- /dev/null +++ b/usr.sbin/bsdconfig/security/include/messages.subr @@ -0,0 +1,48 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_arrows_tab_enter="Press arrows, TAB or ENTER" +hline_select_securelevel_to_operate_at="Select a securelevel to operate at" +msg_cancel="Cancel" +msg_disable_securelevels="Disable securelevels" +msg_disabled="Disabled" +msg_exit="Exit" +msg_exit_this_menu="Exit this menu (returning to previous)" +msg_highly_secure="Highly Secure" +msg_highly_secure_mode="Highly secure mode" +msg_menu_text="This menu allows you to configure aspects of the operating system security\npolicy. Please read the system documentation carefully before modifying\nthese settings, as they may cause service disruption if used improperly.\n\nMost settings will take affect only following a system reboot." +msg_network_secure="Network Secure" +msg_network_secure_mode="Network secure mode" +msg_nfs_port="NFS port" +msg_nfs_port_desc="Require that the NFS clients use reserved ports" +msg_ok="OK" +msg_secure="Secure" +msg_secure_mode="Secure mode" +msg_securelevel="Securelevel" +msg_securelevel_desc="Configure securelevels for the system" +msg_securelevels_menu_text="This menu allows you to select the securelevel your system runs with.\nWhen operating at a securelevel, certain root privileges are disabled,\nwhich may increase resistance to exploits and protect system integrity.\nIn secure mode system flags may not be overridden by the root user,\naccess to direct kernel memory is limited, and kernel modules may not\nbe changed. In highly secure mode, mounted file systems may not be\nmodified on-disk, tampering with the system clock is prohibited. In\nnetwork secure mode configuration changes to firewalling are prohibited.\n " +msg_securelevels_menu_title="Securelevel Configuration Menu" +msg_system_security_options_menu="System Security Options Menu" diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel new file mode 100755 index 0000000000000..f457e942e4c75 --- /dev/null +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -0,0 +1,135 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="130.security" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_select_securelevel_to_operate_at" + local prompt="$msg_securelevels_menu_text" + + menu_list=" + 'X $msg_exit' '$msg_exit_this_menu' + '$msg_disabled' '$msg_disable_securelevels' + '$msg_secure' '$msg_secure_mode' + '$msg_highly_secure' '$msg_highly_secure_mode' + '$msg_network_secure' '$msg_network_secure_mode' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_securelevels_menu_title" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +dialog_menu_main +retval=$? +mtag=$( f_dialog_menutag ) + +[ $retval -eq 0 ] || f_die + +case "$mtag" in +"$msg_disabled") + f_sysrc_set kern_securelevel_enable "NO" + ;; +"$msg_secure") + f_sysrc_set kern_securelevel_enable "YES" + f_sysrc_set kern_securelevel "1" + ;; +"$msg_highly_secure") + f_sysrc_set kern_securelevel_enable "YES" + f_sysrc_set kern_securelevel "2" + ;; +"$msg_network_secure") + f_sysrc_set kern_securelevel_enable "YES" + f_sysrc_set kern_securelevel "3" + ;; +esac + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/security/security b/usr.sbin/bsdconfig/security/security new file mode 100755 index 0000000000000..25754a569830b --- /dev/null +++ b/usr.sbin/bsdconfig/security/security @@ -0,0 +1,149 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="130.security" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + local prompt="$msg_menu_text" + + menu_list=" + 'X <<< $msg_exit' '$msg_exit_this_menu' + " # END-QUOTE + + case "$( f_sysrc_get kern_securelevel_enable )" in + [Yy][Ee][Ss]) + local kern_securelevel="$( f_sysrc_get kern_securelevel )" + if [ ${#kern_securelevel} -eq 1 ] && + f_isinteger "$kern_securelevel" + then menu_list="$menu_list + '2 [$kern_securelevel] $msg_securelevel'" + else menu_list="$menu_list + '2 [X] $msg_securelevel'" + fi + menu_list="$menu_list '$msg_securelevel_desc'" + ;; + *) menu_list="$menu_list + '2 [ ] $msg_securelevel' '$msg_securelevel_desc'" + esac + + case "$( f_sysrc_get nfs_reserved_port_only )" in + [Yy][Ee][Ss]) menu_list="$menu_list + '3 [X] $msg_nfs_port' '$msg_nfs_port_desc'";; + *) menu_list="$menu_list + '3 [ ] $msg_nfs_port' '$msg_nfs_port_desc'";; + esac + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_system_security_options_menu" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X <<< $msg_exit") break ;; + "2 ["?"] $msg_securelevel") # Configure securelevels for the system + $BSDCFG_LIBE/$APP_DIR/kern_securelevel ${USE_XDIALOG:+-X} ;; + "3 [X] $msg_nfs_port") # Require that NFS clients use reserved ports + f_sysrc_set nfs_reserved_port_only "NO";; + "3 [ ] $msg_nfs_port") # Same; Toggle value + f_sysrc_set nfs_reserved_port_only "YES";; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/INDEX b/usr.sbin/bsdconfig/startup/INDEX new file mode 100644 index 0000000000000..1b6f5cd9ace9c --- /dev/null +++ b/usr.sbin/bsdconfig/startup/INDEX @@ -0,0 +1,58 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Startup" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Set Startup Parameters" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="startup|startup" +menu_selection="startup_misc|misc" +menu_selection="startup_rcadd|rcadd" +menu_selection="startup_rcconf|rcconf" +menu_selection="startup_rcdelete|rcdelete" +menu_selection="startup_rcvar|rcvar" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="startup" diff --git a/usr.sbin/bsdconfig/startup/Makefile b/usr.sbin/bsdconfig/startup/Makefile new file mode 100644 index 0000000000000..dc198da33e286 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/140.startup +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= misc rcadd rcconf rcdelete rcedit rcvar startup + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/startup/USAGE b/usr.sbin/bsdconfig/startup/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/startup/include/Makefile b/usr.sbin/bsdconfig/startup/include/Makefile new file mode 100644 index 0000000000000..c155eb0b3492d --- /dev/null +++ b/usr.sbin/bsdconfig/startup/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/140.startup/include +FILES= messages.subr rcconf.subr rcedit.subr rcvar.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/startup/include/messages.subr b/usr.sbin/bsdconfig/startup/include/messages.subr new file mode 100644 index 0000000000000..b3cb57f18e895 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/include/messages.subr @@ -0,0 +1,112 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER" +hline_arrows_tab_enter="Press arrows, TAB or ENTER" +msg_accounting="Accounting" +msg_accounting_desc="This host wishes to run process accounting." +msg_add_custom="Add Custom" +msg_add_from_list="Add From List" +msg_add_new="Add New" +msg_add_new_desc="Add new directive" +msg_add_new_help="Add new rc.conf(5) configuration directive" +msg_add_startup_directive="Add Startup Directive" +msg_all="All" +msg_all_desc="Select all directives" +msg_all_help="Select all displayed rc.conf(5) configuration directives" +msg_apm="APM" +msg_apm_desc="Auto-power management services (typically laptops)" +msg_are_you_sure_you_want_to_delete="Are you sure you want to delete the \`%s' directive\nfrom the rc.conf(5) collection of system configuration files?" +msg_are_you_sure_you_want_delete_the_following="Are you sure you want to delete the following directives\nfrom the rc.conf(5) collection of system configuration files?" +msg_cancel="Cancel" +msg_choose_view_details="Choose View Details" +msg_creating_menu_list="Creating menu list...\nThis may take a while." +msg_creating_rcconf_map="Creating rc.conf(5) map...\nThis may take a while." +msg_creating_rcvar_map="Creating rcvar map...\nThis may take a while." +msg_default_value="Default: %s" +msg_delete="Delete" +msg_delete_desc="Delete directive(s)" +msg_delete_help="Select one or more directives from a list to delete" +msg_delete_selected="Delete Selected" +msg_delete_selected_desc="Delete selected directive(s)" +msg_delete_selected_help="Delete each of the selected rc.conf(5) configuration directives" +msg_delete_startup_directives="Delete Startup Directive(s)" +msg_deleting_selected_directives="Deleting selected directive(s)..." +msg_desc="Description" +msg_desc_desc="Toggle display of system description" +msg_details="Details" +msg_exit="Exit" +msg_exit_cancel="Exit/Cancel" +msg_exit_cancel_desc="Return to previous menu" +msg_exit_cancel_help="Return to the previous menu (selected items are untouched)" +msg_exit_desc="Return to previous menu" +msg_exit_help="Exit this menu (returning to previous)" +msg_exit_this_menu="Exit this menu (returning to previous)" +msg_info="Info" +msg_lpd="lpd" +msg_lpd_desc="This host has a printer and wants to run lpd." +msg_miscellaneous_startup_services="Miscellaneous Startup Services" +msg_miscellaneous_menu_text="This menu allows you to configure various aspects of your system's\nstartup configuration. Use [SPACE] or [ENTER] to select items, and\n[TAB] to move to the buttons. Select Exit to leave this menu." +msg_named="named" +msg_named_desc="Run a local name server on this host" +msg_named_flags="named flags" +msg_named_flags_desc="Set default flags to named (if enabled)" +msg_nis_client="NIS client" +msg_nis_client_desc="This host wishes to be an NIS client." +msg_nis_domainname="NIS domainname" +msg_nis_domainname_desc="Set NIS domainname (if enabled)" +msg_nis_server="NIS Server" +msg_nis_server_desc="This host wishes to be an NIS server." +msg_none="None" +msg_none_desc="Un-Select all directives" +msg_none_help="Un-Select all rc.conf(5) configuration directives" +msg_ok="OK" +msg_please_enter_a_new_value="Please enter a new value for \`%s' (Default: %s):" +msg_please_enter_rcvar_name="Please enter rc.conf(5) variable name:" +msg_please_select_an_rcconf_directive="Please select an rc.conf(5) directive:" +msg_rcvar_contains_invalid_chars="ERROR! rc.conf(5) variable name contains invalid characters.\n Name may only consist of letters [a-zA-Z], numbers [0-9],\n or underscore [_] and must not start with a number." +msg_rcvar_must_start_with="ERROR! rc.conf(5) variable name must start with\n a letter [a-zA-Z] or underscore [_]." +msg_reset="Reset" +msg_reset_desc="Reset to default view settings" +msg_sco="SCO" +msg_sco_desc="This host wants to be able to run IBCS2 binaries." +msg_show_configured="Show Configured" +msg_show_configured_desc="Calculate rc.conf(5) locations (slow)" +msg_show_default_value="Show Default/Value" +msg_show_default_value_desc="Show default/configured values" +msg_show_value="Show Value" +msg_show_value_desc="Show configured startup value" +msg_startup="Startup" +msg_startup_dirs="Startup dirs" +msg_startup_dirs_desc="Set the list of dirs to look for startup scripts" +msg_svr4="SVR4" +msg_svr4_desc="This host wants to be able to run SVR4 binaries." +msg_toggle_startup_services="Toggle Startup Services" +msg_value_required="Value Required" +msg_view_details="View Details" +msg_view_details_desc="Choose view details" +msg_view_details_help="Choose which details are shown in the current view" +msg_view_edit_startup_configuration="View/Edit Startup Configuration" diff --git a/usr.sbin/bsdconfig/startup/include/rcconf.subr b/usr.sbin/bsdconfig/startup/include/rcconf.subr new file mode 100644 index 0000000000000..88bed495b45c2 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/include/rcconf.subr @@ -0,0 +1,466 @@ +if [ ! "$_STARTUP_RCCONF_SUBR" ]; then _STARTUP_RCCONF_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ GLOBALS + +# +# Initialize in-memory cache variables +# +STARTUP_RCCONF_MAP= +_STARTUP_RCCONF_MAP= + +# +# Define what a variable looks like +# +STARTUP_RCCONF_REGEX="^[[:alpha:]_][[:alnum:]_]*=" + +# +# Default path to on-disk cache file(s) +# +STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" + +############################################################ FUNCTIONS + +# f_startup_rcconf_list +# +# Produce a list of non-default configuration variables configured in the +# rc.conf(5) collection of files. +# +f_startup_rcconf_list() +{ + ( # Operate within a sub-shell to protect the parent environment + . "$RC_DEFAULTS" > /dev/null + f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files + source_rc_confs > /dev/null + export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" + export RC_DEFAULTS + set | awk -F= " + function test_print(var) + { + if ( var == \"OPTIND\" ) return + if ( var == \"PATH\" ) return + if ( var == \"RC_DEFAULTS\" ) return + if ( var == \"STARTUP_RCCONF_REGEX\" ) return + if ( var == \"_rc_conf_files_file\" ) return + if ( var == \"rc_conf_files\" ) + { + if ( ENVIRON[\"_rc_conf_files_file\"] == \ + ENVIRON[\"RC_DEFAULTS\"] ) return + } + print var + } + /$STARTUP_RCCONF_REGEX/ { test_print(\$1) }" + ) +} + +# f_startup_rcconf_map +# +# Produce a map (beit from in-memory cache or on-disk cache) of rc.conf(5) +# variables and their descriptions. The map returned has the following format: +# +# var description +# +# With each as follows: +# +# var the rc.conf(5) variable +# description description of the variable +# +f_startup_rcconf_map() +{ + # If the in-memory cached value is available, return it immediately + if [ "$_STARTUP_RCCONF_MAP" ]; then + echo "$STARTUP_RCCONF_MAP" + return $SUCCESS + fi + + # + # create the in-memory cache (potentially from validated on-disk cache) + # + + # + # Calculate digest used to determine if the on-disk global persistant + # cache file (containg this digest on the first line) is valid and can + # be used to quickly populate the cache value for immediate return. + # + local rc_defaults_digest + rc_defaults_digest=$( md5 < "$RC_DEFAULTS" ) + + # + # Check to see if the global persistant cache file exists + # + if [ -f "$STARTUP_RCCONF_MAP_CACHEFILE" ]; then + # + # Attempt to populate the in-memory cache with the (soon to be) + # be validated on-disk cache. If validation fails, fall-back to + # the current value and provide error exit status. + # + STARTUP_RCCONF_MAP=$( + ( # Get digest as the first word on the first line + read digest rest_ignored + + # + # If the stored digest matches the calculated- + # one populate the in-memory cache from the on- + # disk cache and provide success exit status. + # + if [ "$digest" = "$rc_defaults_digest" ]; then + cat + exit $SUCCESS + else + # Otherwise, return the current value + echo "$STARTUP_RCCONF_MAP" + exit $FAILURE + fi + ) < "$STARTUP_RCCONF_MAP_CACHEFILE" + ) + export STARTUP_RCCONF_MAP + if [ $? -eq $SUCCESS ]; then + export _STARTUP_RCCONF_MAP=1 + echo "$STARTUP_RCCONF_MAP" + return $SUCCESS + fi + # Otherwise, fall-thru to create in-memory cache from scratch + fi + + # + # If we reach this point, we need to generate the data from scratch + # (and after we do, we'll attempt to create the global persistant + # cache file to speed up future executions). + # + + STARTUP_RCCONF_MAP=$( + f_clean_env --except \ + PATH \ + RC_DEFAULTS \ + STARTUP_RCCONF_REGEX \ + f_sysrc_desc_awk + . "$RC_DEFAULTS" + + # Unset variables we don't want reported + unset source_rc_confs_defined + + for var in $( set | awk -F= " + function test_print(var) + { + if ( var == \"OPTIND\" ) return + if ( var == \"PATH\" ) return + if ( var == \"RC_DEFAULTS\" ) return + if ( var == \"STARTUP_RCCONF_REGEX\" ) return + if ( var == \"f_sysrc_desc_awk\" ) return + print var + } + /$STARTUP_RCCONF_REGEX/ { test_print(\$1) } + " ); do + echo $var "$( f_sysrc_desc $var )" + done + ) + export STARTUP_RCCONF_MAP + export _STARTUP_RCCONF_MAP=1 + echo "$STARTUP_RCCONF_MAP" + + # + # Attempt to create the persistant global cache + # + + # Create a new temporary file to write to + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # Write the temporary file contents + echo "$rc_defaults_digest" > "$tmpfile" + echo "$STARTUP_RCCONF_MAP" >> "$tmpfile" + + # Finally, move the temporary file into place + case "$STARTUP_RCCONF_MAP_CACHEFILE" in + */*) f_quietly mkdir -p "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" + esac + mv "$tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" +} + +# f_startup_rcconf_map_expand +# +# Expands the map ($RCCONF_MAP) into the shell environment namespace by +# creating _${var}_desc variables containing the description of each variable +# encountered. +# +# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. +# +f_startup_rcconf_map_expand() +{ + eval "$( echo "$RCCONF_MAP" | awk ' + BEGIN { + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + var = $1 + desc = $0 + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf "_%s_desc='\''%s'\''\n", var, desc + printf "export _%s_desc\n", var + }' )" +} + +# f_dialog_input_view_details +# +# Display a menu for selecting which details are to be displayed. The following +# variables are tracked/modified by the menu/user's selection: +# +# SHOW_DESC Show or hide descriptions +# +# Mutually exclusive options: +# +# SHOW_VALUE Show the value (default; override only) +# SHOW_VALUE_DEFAULT Show both value and default +# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in +# +# Each variable is treated as a boolean (NULL for false, non-NULL for true). +# +# Variables are exported for later-required awk(1) ENVIRON visibility. +# +f_dialog_input_view_details() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + local prompt="" + + local md=" " m1=" " m2=" " m3=" " + [ "$SHOW_DESC" ] && md="X" + [ "$SHOW_VALUE" ] && m1="*" + [ "$SHOW_DEFAULT_VALUE" ] && m2="*" + [ "$SHOW_CONFIGURED" ] && m3="*" + + menu_list=" + 'X $msg_exit' '$msg_exit_this_menu' + 'R $msg_reset' '$msg_reset_desc' + 'D [$md] $msg_desc' '$msg_desc_desc' + '1 ($m1) $msg_show_value' '$msg_show_value_desc' + '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' + '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + f_dialog_title "$msg_choose_view_details" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + local mtag="$( f_dialog_menutag )" + + f_dialog_title_restore + + [ $retval -eq 0 ] || return $SUCCESS + [ "$mtag" = "X $msg_exit" ] && return $SUCCESS + + case "$mtag" in + "R $msg_reset") + SHOW_VALUE=1 + SHOW_DESC=1 + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + ;; + "D [X] $msg_desc") SHOW_DESC= ;; + "D [ ] $msg_desc") SHOW_DESC=1 ;; + "1 ("?") $msg_show_value") + SHOW_VALUE=1 + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + ;; + "2 ("?") $msg_show_default_value") + SHOW_VALUE= + SHOW_DEFAULT_VALUE=1 + SHOW_CONFIGURED= + ;; + "3 ("?") $msg_show_configured") + SHOW_VALUE= + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED=1 + ;; + esac +} + +# f_dialog_input_rclist +# +# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This +# function should be treated like a call to dialog(1) (the exit status should +# be captured and f_dialog_menutag() should be used to clean-up and get the +# user's response). +# +f_dialog_input_rclist() +{ + local size + local hline="$hline_arrows_tab_enter" + local prompt="$msg_please_select_an_rcconf_directive" + local menu_list + + menu_list=" + 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_help'} + " # END-QUOTE + + if [ ! "$_RCCONF_MAP" ]; then + # Generate RCCONF_MAP of `var desc ...' per-line + f_dialog_info "$msg_creating_rcconf_map" + RCCONF_MAP=$( f_startup_rcconf_map ) + export RCCONF_MAP + # Generate _${var}_desc variables from $RCCONF_MAP + f_startup_rcconf_map_expand + export _RCCONF_MAP=1 + fi + + menu_list="$menu_list $( + export SHOW_DESC + echo "$RCCONF_MAP" | awk ' + BEGIN { + prefix = "" + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + rcvar = $1 + printf "%s'\'' '\'\''", rcvar + if ( ENVIRON["SHOW_DESC"] ) { + desc = $0 + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf " '\''%s'\''", desc + } + printf "\n" + }' + )" + + set -f # noglob + + size=$( eval f_dialog_menu_${SHOW_DESC:+with_help_}size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + ${SHOW_DESC:+--item-help} \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_input_rcvar [$init] +# +# Allows the user to enter the name for a new rc.conf(5) variable. If the user +# does not cancel or press ESC, the $rcvar variable will hold the newly- +# configured value upon return. +# +f_dialog_input_rcvar() +{ + local msg="$msg_please_enter_rcvar_name" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size _input="$1" + while :; do + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for invalid entry (1of2) + if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then + f_show_msg "$msg_rcvar_must_start_with" + continue + fi + + # Check for invalid entry (2of2) + if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" + then + f_show_msg "$msg_rcvar_contains_invalid_chars" + continue + fi + + rcvar="$_input" + break + done + + f_dprintf "f_dialog_input_rcvar: rcvar->[$rcvar]" + + return $SUCCESS +} + +fi # ! $_STARTUP_RCCONF_SUBR diff --git a/usr.sbin/bsdconfig/startup/include/rcedit.subr b/usr.sbin/bsdconfig/startup/include/rcedit.subr new file mode 100644 index 0000000000000..325f1fe7e3bad --- /dev/null +++ b/usr.sbin/bsdconfig/startup/include/rcedit.subr @@ -0,0 +1,106 @@ +if [ ! "$_STARTUP_RCEDIT_SUBR" ]; then _STARTUP_RCEDIT_SUBR=1 +# +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_rcedit $var [[--] $init ...] +# +# Allow the user to enter a new value for a given rc.conf(5) variable. If the +# user does not cancel or press ESC, the variable will be saved without +# confirmation. +# +# If the second argument is non-NULL, it will be processed as the initial text +# to be displayed, overriding the default behavior to display the currently +# configured value as the initial text. +# +# If instead the second argument is "--", then the third argument (NULL or +# otherwise) will be treated as the initial text. +# +f_dialog_rcedit() +{ + local msg size retval var="$1" _input + local hline="$hline_alnum_punc_tab_enter" + + msg=$( printf "$msg_please_enter_a_new_value" \ + "$var" "$( f_sysrc_get_default "$var" )" ) + + shift 1 # var + if [ "$1" ]; then + [ "$1" = "--" ] && shift 1 # -- + _input="$1" + else + _input=$( f_sysrc_get "$var" ) + fi + + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + local opterm="--" + [ "$USE_XDIALOG" ] && opterm= + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + $opterm \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Return if the value has not changed from current + local cur_val="$( f_sysrc_get "$var" )" + [ "$_input" = "$cur_val" ] && return $SUCCESS + + f_dprintf "$var: [$cur_val]->[$_input]" + + err=$( f_sysrc_set "$var" "$_input" 2>&1 ) || + f_show_msg "$err" +} + +fi # ! $_STARTUP_RCEDIT_SUBR diff --git a/usr.sbin/bsdconfig/startup/include/rcvar.subr b/usr.sbin/bsdconfig/startup/include/rcvar.subr new file mode 100644 index 0000000000000..c7ad74d5315f0 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/include/rcvar.subr @@ -0,0 +1,206 @@ +if [ ! "$_STARTUP_RCVAR_SUBR" ]; then _STARTUP_RCVAR_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/sysrc.subr + +############################################################ CONFIGURATION + +# +# Default path to the `/etc/rc.d' directory where service(8) scripts are stored +# +: ${ETC_RC_D:=/etc/rc.d} + +# +# Default path to `/etc/rc.subr' (for find_local_scripts_new()) +# +: ${ETC_RC_SUBR:=/etc/rc.subr} + +############################################################ GLOBALS + +# +# Initialize in-memory cache variables +# +STARTUP_RCVAR_MAP= +_STARTUP_RCVAR_MAP= + +# +# Define what an rcvar looks like +# +STARTUP_RCVAR_REGEX='[[:alpha:]_][[:alnum:]_]*="([Yy][Ee][Ss]|[Nn][Oo])"' + +# +# Default path to on-disk cache file(s) +# +STARTUP_RCVAR_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcvar_map.cache" + +############################################################ FUNCTIONS + +# f_startup_rcvar_map +# +# Produce a map (beit from in-memory cache or on-disk cache) of rc.d scripts +# and their associated rcvar's. The map returned has the following format: +# +# rcvar default script description +# +# With each as follows: +# +# rcvar the variable used to enable this rc.d script +# default default value for this variable +# script the rc.d script in-question +# description description of the variable from rc.conf(5) defaults +# +f_startup_rcvar_map() +{ + # If the in-memory cached value is available, return it immediately + if [ "$_STARTUP_RCVAR_MAP" ]; then + echo "$STARTUP_RCVAR_MAP" + return $SUCCESS + fi + + # + # create the in-memory cache (potentially from validated on-disk cache) + # + + # Get a list of /etc/rc.d scripts ... + local file rc_script_list= + for file in "$ETC_RC_D"/*; do + [ -f "$file" ] || continue + [ -x "$file" ] || continue + rc_script_list="$rc_script_list${rc_script_list:+ }$file" + done + # ... and /usr/local/etc/rc.d scripts + rc_script_list="$rc_script_list${rc_script_list:+ }$( + local_startup=$( f_sysrc_get local_startup ) + f_include "$ETC_RC_SUBR" + find_local_scripts_new + echo $local_rc + )" + + # + # Calculate a digest given the checksums of all dependencies (scripts and + # the defaults file). This digest will be used to determine if an on-disk + # global persistant cache file (containg this digest on the first line) + # is valid and can be used to quickly populate the cache value for + # immediate return. + # + local rc_script_list_digest + rc_script_list_digest=$( cd "$ETC_RC_D" && + cksum "$RC_DEFAULTS" $rc_script_list | md5 ) + + # + # Check to see if the global persistant cache file exists + # + if [ -f "$STARTUP_RCVAR_MAP_CACHEFILE" ]; then + # + # Attempt to populate the in-memory cache with the (soon to be) + # be validated on-disk cache. If validation fails, fall-back to + # the current value and provide error exit status. + # + STARTUP_RCVAR_MAP=$( + ( # Get digest as the first word on the first line + read digest rest_ignored + + # + # If the stored digest matches the calculated-one + # populate the in-memory cache from the on-disk + # cache and provide success exit status. + # + if [ "$digest" = "$rc_script_list_digest" ]; then + cat + exit $SUCCESS + else + # Otherwise, return the current value + echo "$STARTUP_RCVAR_MAP" + exit $FAILURE + fi + ) < "$STARTUP_RCVAR_MAP_CACHEFILE" + ) + export STARTUP_RCVAR_MAP + if [ $? -eq $SUCCESS ]; then + export _STARTUP_RCVAR_MAP=1 + echo "$STARTUP_RCVAR_MAP" + return $SUCCESS + fi + # Otherwise, fall-through to create in-memory cache from scratch + fi + + # + # If we reach this point, we need to generate the data from scratch + # (and after we do, we'll attempt to create the global persistant + # cache file to speed up future executions). + # + + STARTUP_RCVAR_MAP=$( + for script in $rc_script_list; do + rcvar_list=$( $script rcvar | awk -F= \ + -v script="$script" ' + /^'"$STARTUP_RCVAR_REGEX"'/ { + if ( $2 ~ /^"[Yy][Ee][Ss]"$/ ) + print $1 ",YES" + else + print $1 ",NO" + }' ) + for entry in $rcvar_list; do + rcvar="${entry%%,*}" + rcvar_default=$( f_sysrc_get_default "$rcvar" ) + [ "$rcvar_default" ] || + rcvar_default="${entry#*,}" + rcvar_desc=$( f_sysrc_desc "$rcvar" ) + echo $rcvar ${rcvar_default:-NO} \ + $script "$rcvar_desc" + done + done | sort -u + ) + export STARTUP_RCVAR_MAP + export _STARTUP_RCVAR_MAP=1 + echo "$STARTUP_RCVAR_MAP" + + # + # Attempt to create the persistant global cache + # + + # Create a new temporary file to write to + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # Write the temporary file contents + echo "$rc_script_list_digest" > "$tmpfile" + echo "$STARTUP_RCVAR_MAP" >> "$tmpfile" + + # Finally, move the temporary file into place + case "$STARTUP_RCVAR_MAP_CACHEFILE" in + */*) f_quietly mkdir -p "${STARTUP_RCVAR_MAP_CACHEFILE%/*}" + esac + mv "$tmpfile" "$STARTUP_RCVAR_MAP_CACHEFILE" +} + +fi # ! $_STARTUP_RCVAR_SUBR diff --git a/usr.sbin/bsdconfig/startup/misc b/usr.sbin/bsdconfig/startup/misc new file mode 100755 index 0000000000000..1dd2694098839 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/misc @@ -0,0 +1,406 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ GLOBALS + +# +# Operating environment +# +UNAME_P=$( uname -p ) # Processor Architecture (i.e. i386) + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list var_list size + local hline="$hline_arrows_tab_enter" + local prompt="$msg_miscellaneous_menu_text" + + menu_list=" + 'X <<< $msg_exit' '$msg_exit_this_menu' + " # END-QUOTE + + var_list=" + accounting_enable + local_startup + lpd_enable + named_enable + named_flags + nis_client_enable + nis_server_enable + nisdomainname + startup_dirs + " # END-QUOTE + + if [ "$UNAME_P" = "i386" ]; then + var_list="$var_list + apm_enable + ibcs2_enable + svr4_enable + " # END-QUOTE + fi + + menu_list="$menu_list $( + . "$RC_DEFAULTS" + source_rc_confs + export $var_list + export msg_apm msg_apm_desc + export msg_startup_dirs msg_startup_dirs_desc + export msg_named msg_named_desc + export msg_named_flags msg_named_flags_desc + export msg_nis_client msg_nis_client_desc + export msg_nis_domainname msg_nis_domainname_desc + export msg_nis_server msg_nis_server_desc + export msg_accounting msg_accounting_desc + export msg_lpd msg_lpd_desc + export msg_sco msg_sco_desc + export msg_svr4 msg_svr4_desc + :| awk \ + -v uname_p="$UNAME_P" \ + -v menu_tags="$DIALOG_MENU_TAGS" \ + -v menu_fmt="'%c [%c] %s' '%s'\n" \ + -v sep_fmt="' ' ' -- '\n" \ + 'END { + i = 1 + if ( uname_p == "i386" ) + { + # + # APM: Auto-power management services + # (typically laptops) + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["apm_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_apm"], + ENVIRON["msg_apm_desc"] + } + + # Separator + printf sep_fmt + + # + # Startup dirs: Set the list of dirs to look for + # startup scripts + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( length(ENVIRON["local_startup"]) > 0 \ + ? "X" : " " ), + ENVIRON["msg_startup_dirs"], + ENVIRON["msg_startup_dirs_desc"] + + # + # named: Run a local name server on this host + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["named_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_named"], + ENVIRON["msg_named_desc"] + + # + # named flags: Set default flags to named (if enabled) + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( length(ENVIRON["named_flags"]) > 0 \ + ? "X" : " " ), + ENVIRON["msg_named_flags"], + ENVIRON["msg_named_flags_desc"] + + # + # NIS client: This host wishes to be an NIS client. + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["nis_client_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_nis_client"], + ENVIRON["msg_nis_client_desc"] + + # + # NIS domainname: Set NIS domainname (if enabled) + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( length(ENVIRON["nisdomainname"]) > 0 \ + ? "X" : " " ), + ENVIRON["msg_nis_domainname"], + ENVIRON["msg_nis_domainname_desc"] + + # + # NIS server: This host wishes to be an NIS server. + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["nis_server_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_nis_server"], + ENVIRON["msg_nis_server_desc"] + + # Separator + printf sep_fmt + + # + # Accounting: This host wishes to run process + # accounting. + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["accounting_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_accounting"], + ENVIRON["msg_accounting_desc"] + + # + # lpd: This host has a printer and wants to run lpd. + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["lpd_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_lpd"], + ENVIRON["msg_lpd_desc"] + + if ( uname_p == "i386" ) + { + # + # SCO: This host wants to be able to run IBCS2 + # binaries. + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["ibcs2_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_sco"], + ENVIRON["msg_sco_desc"] + + # + # SVR4: This host wants to be able to run SVR4 + # binaries. + # + printf menu_fmt, substr(menu_tags, i++, 1), + ( ENVIRON["svr4_enable"] ~ \ + /^[Yy][Ee][Ss]$/ ? "X" : " " ), + ENVIRON["msg_svr4"], + ENVIRON["msg_svr4_desc"] + } + }' + )" + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# dialog_input_value [ $prompt [ $init ] ] +# +# Prompt the user to input a value. If the user does not cancel or press ESC, +# the return value is zero ($SUCCESS) and $value holds the user's input. +# +dialog_input_value() +{ + local hline="$hline_alnum_tab_enter" + local size prompt="$1" _input="$2" + + f_dialog_title "$msg_value_required" + + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$prompt" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$prompt\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + f_dialog_title_restore + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + value="$_input" + return $SUCCESS +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_miscellaneous_startup_services" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X <<< $msg_exit") break ;; + ?" ["?"] $msg_apm") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set apm_enable NO 2>&1 ) ;; + ?" [ ] "*) err=$( f_sysrc_set apm_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_startup_dirs") + dialog_input_value \ + "$msg_startup_dirs_desc" \ + "$( f_sysrc_get local_startup )" + if [ $? -eq $SUCCESS ]; then + err=$( f_sysrc_set local_startup "$value" ) || + f_show_msg "%s\n" "$err" + fi + ;; + ?" ["?"] $msg_named") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set named_enable NO 2>&1 ) ;; + ?" [ ] "*) err=$( f_sysrc_set named_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_named_flags") + dialog_input_value \ + "$msg_named_flags_desc" \ + "$( f_sysrc_get named_flags )" + if [ $? -eq $SUCCESS ]; then + err=$( f_sysrc_set named_flags "$value" ) || + f_show_msg "%s\n" "$err" + fi + ;; + ?" ["?"] $msg_nis_client") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set nis_client_enable NO 2>&1 ) ;; + ?" [ ] "*) + err=$( f_sysrc_set nis_client_enable YES 2>&1 ) \ + && err=$( f_sysrc_set rpcbind_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_nis_domainname") + dialog_input_value \ + "$msg_nis_domainname_desc" \ + "$( f_sysrc_get nisdomainname )" + if [ $? -eq $SUCCESS ]; then + err=$( f_sysrc_set nisdomainname "$value" ) || + f_show_msg "%s\n" "$err" + fi + ;; + ?" ["?"] $msg_nis_server") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set nis_server_enable NO 2>&1 ) ;; + ?" [ ] "*) + err=$( f_sysrc_set nis_server_enable YES 2>&1 ) \ + && err=$( f_sysrc_set rpcbind_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_accounting") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set accounting_enable NO 2>&1 ) ;; + ?" [ ] "*) err=$( f_sysrc_set accounting_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_lpd") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set lpd_enable NO 2>&1 ) ;; + ?" [ ] "*) err=$( f_sysrc_set lpd_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_sco") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set ibcs2_enable NO 2>&1 ) ;; + ?" [ ] "*) err=$( f_sysrc_set ibcs2_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + ?" ["?"] $msg_svr4") + case "$mtag" in + ?" [X] "*) err=$( f_sysrc_set svr4_enable NO 2>&1 ) ;; + ?" [ ] "*) err=$( f_sysrc_set svr4_enable YES 2>&1 ) ;; + esac + [ $? -eq $SUCCESS ] || f_show_msg "%s\n" "$err" ;; + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/rcadd b/usr.sbin/bsdconfig/startup/rcadd new file mode 100755 index 0000000000000..1fba829824cda --- /dev/null +++ b/usr.sbin/bsdconfig/startup/rcadd @@ -0,0 +1,153 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="140.startup" +f_include $BSDCFG_LIBE/$APP_DIR/include/rcconf.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ GLOBALS + +# +# Options +# +# Inherit SHOW_DESC value if set, otherwise default to 1 +( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + local prompt="" + + menu_list=" + '1' '$msg_add_from_list' + '2' '$msg_add_custom' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_add_startup_directive" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + 1) # Add From List + # Loop for easy return + while :; do + f_dialog_input_rclist + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || break + + case "$mtag" in + "X $msg_exit" ) break ;; + *) # Anything else is a directive + rcvar="${mtag# }" + $BSDCFG_LIBE/$APP_DIR/rcedit \ + ${USE_XDIALOG:+-X} \ + "$rcvar" || continue + esac + break + done + [ $retval -eq 0 ] || continue + ;; + 2) # Add Custom + f_dialog_input_rcvar || continue + $BSDCFG_LIBE/$APP_DIR/rcedit ${USE_XDIALOG:+-X} "$rcvar" || + continue + ;; + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf new file mode 100755 index 0000000000000..a18cad1f4f249 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -0,0 +1,246 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="140.startup" +f_include $BSDCFG_LIBE/$APP_DIR/include/rcconf.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ GLOBALS + +# +# Global map/menu-list for the main menu +# +RCCONF_MAP= +_RCCONF_MAP= +RCCONF_MENU_LIST= + +# +# Options +# +# Inherit SHOW_DESC value if set, otherwise default to 1 +( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +# Selectively inherit SHOW_* value (in order of preference) +if [ "$SHOW_DEFAULT_VALUE" ]; then + SHOW_DEFAULT_VALUE=1 + SHOW_CONFIGURED= + SHOW_VALUE= +elif [ "$SHOW_CONFIGURED" ]; then + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED=1 + SHOW_VALUE= +else + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + SHOW_VALUE=1 +fi + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local size + local hline="$hline_arrows_tab_enter" + local prompt="" + + RCCONF_MENU_LIST=" + 'X $msg_exit' '$msg_exit_desc' + ${SHOW_DESC:+'$msg_exit_help'} + '> $msg_add_new' '$msg_add_new_desc' + ${SHOW_DESC:+'$msg_add_new_help'} + '> $msg_delete' '$msg_delete_desc' + ${SHOW_DESC:+'$msg_delete_help'} + ${USE_XDIALOG:+ + '> $msg_view_details' '$msg_view_details_desc' + ${SHOW_DESC:+'$msg_view_details_help'} + } + " # END-QUOTE + + if [ ! "$_RCCONF_MAP" ]; then + # Genreate RCCONF_MAP of `var desc ...' per-line + f_dialog_info "$msg_creating_rcconf_map" + RCCONF_MAP=$( f_startup_rcconf_map ) + export RCCONF_MAP + # Generate _${var}_desc variables from $RCCONF_MAP + f_startup_rcconf_map_expand + export _RCCONF_MAP=1 + fi + + # Show infobox for modes that take a while to calculate/display + [ "$SHOW_DEFAULT_VALUE" -o "$SHOW_CONFIGURED" ] && + f_dialog_info "$msg_creating_menu_list" + + RCCONF_MENU_LIST="$RCCONF_MENU_LIST $( + . "$RC_DEFAULTS" > /dev/null + source_rc_confs > /dev/null + var_list=$( f_startup_rcconf_list ) + for var in $var_list; do + eval export $var + [ "$SHOW_DEFAULT_VALUE" ] && export \ + _${var}_default="$( f_sysrc_get_default $var )" + [ "$SHOW_CONFIGURED" ] && export \ + _${var}_file="$( f_sysrc_find $var )" + done + export SHOW_VALUE SHOW_DESC SHOW_DEFAULT_VALUE SHOW_CONFIGURED + export msg_default_value + echo "$var_list" | awk ' + BEGIN { + prefix = "" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + var = $1 + printf "%s'\'' '\''", var + if ( ENVIRON["SHOW_DEFAULT_VALUE"] ) { + default = ENVIRON["_" var "_default"] + gsub(/'\''/, "'\''\\'\'\''", default) + value = ENVIRON[var] + gsub(/'\''/, "'\''\\'\'\''", value) + printf ENVIRON["msg_default_value"] "; %s", + default, value + } else if ( ENVIRON["SHOW_CONFIGURED"] ) { + printf "%s", ENVIRON["_" var "_file"] + } else { # SHOW_VALUE (default behavior) + value = ENVIRON[var] + gsub(/'\''/, "'\''\\'\'\''", value) + printf "%s", value + } + printf "'\''" + if ( ENVIRON["SHOW_DESC"] ) { + desc = ENVIRON["_" var "_desc"] + gsub(/'\''/, "'\''\\'\'\''", desc) + printf " '\''%s'\''", desc + } + printf "\n" + }' + )" + + set -f # noglob + + size=$( eval f_dialog_menu_${SHOW_DESC:+with_help_}size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $RCCONF_MENU_LIST ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --help-button \ + --help-label \"\$msg_details\" \ + ${SHOW_DESC:+--item-help} \ + --menu \"\$prompt\" $size \ + $RCCONF_MENU_LIST \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_view_edit_startup_configuration" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + if [ "$USE_XDIALOG" ]; then + case "$mtag" in + "> $msg_view_details") + f_dialog_input_view_details + continue + esac + elif [ $retval -eq 2 ]; then + # The ``Help'' button (labeled "Details") was pressed + f_dialog_input_view_details + continue + fi + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + "> $msg_add_new") + $BSDCFG_LIBE/$APP_DIR/rcadd ${USE_XDIALOG:+-X} + ;; + "> $msg_delete") + # rcdelete has a similar interface that can inherit the below: + export SHOW_VALUE SHOW_DESC SHOW_DEFAULT_VALUE SHOW_CONFIGURED + $BSDCFG_LIBE/$APP_DIR/rcdelete ${USE_XDIALOG:+-X} + ;; + *) # Anything else is a variable to edit + $BSDCFG_LIBE/$APP_DIR/rcedit ${USE_XDIALOG:+-X} "${mtag# }" + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete new file mode 100755 index 0000000000000..acec48e599d58 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -0,0 +1,355 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="140.startup" +f_include $BSDCFG_LIBE/$APP_DIR/include/rcconf.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ GLOBALS + +# +# Global map/menu-list for the main menu +# +RCCONF_MAP= +_RCCONF_MAP= +RCCONF_MENU_LIST= + +# +# Options +# +# Inherit SHOW_DESC value if set, otherwise default to 1 +( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +# Selectively inherit SHOW_* value (in order of preference) +if [ "$SHOW_DEFAULT_VALUE" ]; then + SHOW_DEFAULT_VALUE=1 + SHOW_CONFIGURED= + SHOW_VALUE= +elif [ "$SHOW_CONFIGURED" ]; then + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED=1 + SHOW_VALUE= +else + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + SHOW_VALUE=1 +fi + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local size + local hline="$hline_arrows_tab_enter" + local prompt="" + + RCCONF_MENU_LIST=" + 'X $msg_exit_cancel' '$msg_exit_cancel_desc' + ${SHOW_DESC:+'$msg_exit_cancel_help'} + '> $msg_delete_selected' '$msg_delete_selected_desc' + ${SHOW_DESC:+'$msg_delete_selected_help'} + '> $msg_all' '$msg_all_desc' + ${SHOW_DESC:+'$msg_all_help'} + '> $msg_none' '$msg_none_desc' + ${SHOW_DESC:+'$msg_none_help'} + ${USE_XDIALOG:+ + '> $msg_view_details' '$msg_view_details_desc' + ${SHOW_DESC:+'$msg_view_details_help'} + } + " # END-QUOTE + + if [ ! "$_RCCONF_MAP" ]; then + # Genreate RCCONF_MAP of `var desc ...' per-line + f_dialog_info "$msg_creating_rcconf_map" + RCCONF_MAP=$( f_startup_rcconf_map ) + export RCCONF_MAP + # Generate _${var}_desc variables from $RCCONF_MAP + f_startup_rcconf_map_expand + export _RCCONF_MAP=1 + fi + + # Show infobox for modes that take a while to calculate/display + [ "$SHOW_DEFAULT_VALUE" -o "$SHOW_CONFIGURED" ] && + f_dialog_info "$msg_creating_menu_list" + + RCCONF_MENU_LIST="$RCCONF_MENU_LIST $( + . "$RC_DEFAULTS" > /dev/null + source_rc_confs > /dev/null + var_list=$( f_startup_rcconf_list ) + for var in $var_list; do + eval export $var + [ "$SHOW_DEFAULT_VALUE" ] && export \ + _${var}_default="$( f_sysrc_get_default $var )" + [ "$SHOW_CONFIGURED" ] && export \ + _${var}_file="$( f_sysrc_find $var )" + done + export SHOW_VALUE SHOW_DESC SHOW_DEFAULT_VALUE SHOW_CONFIGURED + export msg_default_value + echo "$var_list" | awk ' + BEGIN { + prefix = "" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + var = $1 + printf "%s'\'' '\''[", var + if ( ENVIRON["_" var "_delete"] ) + printf "X" + else + printf " " + printf "] " + if ( ENVIRON["SHOW_DEFAULT_VALUE"] ) { + default = ENVIRON["_" var "_default"] + gsub(/'\''/, "'\''\\'\'\''", default) + value = ENVIRON[var] + gsub(/'\''/, "'\''\\'\'\''", value) + printf ENVIRON["msg_default_value"] "; %s", + default, value + } else if ( ENVIRON["SHOW_CONFIGURED"] ) { + printf "%s", ENVIRON["_" var "_file"] + } else { # SHOW_VALUE (default behavior) + value = ENVIRON[var] + gsub(/'\''/, "'\''\\'\'\''", value) + printf "%s", value + } + printf "'\''" + if ( ENVIRON["SHOW_DESC"] ) { + desc = ENVIRON["_" var "_desc"] + gsub(/'\''/, "'\''\\'\'\''", desc) + printf " '\''%s'\''", desc + } + printf "\n" + }' + )" + + set -f # noglob + + size=$( eval f_dialog_menu_${SHOW_DESC:+with_help_}size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $RCCONF_MENU_LIST ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --help-button \ + --help-label \"\$msg_details\" \ + ${SHOW_DESC:+--item-help} \ + --menu \"\$prompt\" $size \ + $RCCONF_MENU_LIST \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# dialog_menu_delete $var1 [$var2 ...] +# +# Get the user's blessing to delete one or more variables. +# if (and only-if) the user confirms (does not press ESC or Cancel/NO). +# +dialog_menu_delete() +{ + local size + local hline="$hline_arrows_tab_enter" + local prompt="$msg_are_you_sure_you_want_delete_the_following" + local var_list menu_list + + [ $# -ge 1 ] || return $FAILURE + + if [ $# -eq 1 ]; then + msg=$( printf "$msg_are_you_sure_you_want_to_delete" \ + "$delete_vars" ) + f_dialog_noyes "$msg" + return $? + fi + + var_list=$( for var in $*; do echo "$var"; done | sort -u ) + menu_list=$( + . "$RC_DEFAULTS" + source_rc_confs + echo "$var_list" | awk ' + BEGIN { + prefix = "" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + var = $1 + printf "%s'\'' '\'\''\n", var + }' + ) + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + local defaultno="defaultno" + [ "$USE_XDIALOG" ] && defaultno="default-no" + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --$defaultno \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_delete_startup_directives" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + if [ "$USE_XDIALOG" ]; then + case "$mtag" in + "> $msg_view_details") + f_dialog_input_view_details + continue + esac + elif [ $retval -eq 2 ]; then + # The ``Help'' button (labeled "Details") was pressed + f_dialog_input_view_details + continue + fi + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit_cancel") break ;; + "> $msg_delete_selected") + delete_vars= + for var in $( f_startup_rcconf_list ); do + [ "$( eval echo \$_${var}_delete )" ] || continue + delete_vars="$delete_vars${delete_vars:+ }$var" + done + unset var # no longer needed + if dialog_menu_delete $delete_vars; then + f_dialog_title "$msg_info" + f_dialog_info "$msg_deleting_selected_directives" + f_dialog_title_restore + for var in $delete_vars; do + err=$( f_sysrc_delete $var 2>&1 ) + if [ $? -ne $SUCCESS ]; then + f_show_msg "%s\n" "$err" + break + fi + done + unset var # no longer needed + fi + ;; + "> $msg_all") + for var in $( f_startup_rcconf_list ); do + setvar _${var}_delete 1 + export _${var}_delete + done + unset var # no longer needed + ;; + "> $msg_none") + var_list=$( set | awk -F= " + /$STARTUP_RCCONF_REGEX/ { + if (\$1 ~ /^_[[:alpha:]_][[:alnum:]_]*_delete/) + print \$1 + }" + ) + [ "$var_list" ] && unset $var_list + unset var_list # no longer needed + ;; + *) # Anything else is a variable to edit + var="${mtag# }" + + # Toggle the state-variable and loop back to menu + if ( eval : \${_${var}_delete?} ) > /dev/null 2>&1; then + unset _${var}_delete + else + setvar _${var}_delete 1 + export _${var}_delete + fi + unset var # no longer needed + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/rcedit b/usr.sbin/bsdconfig/startup/rcedit new file mode 100755 index 0000000000000..19d19410b04b8 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/rcedit @@ -0,0 +1,69 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="140.startup" +f_include $BSDCFG_LIBE/$APP_DIR/include/rcedit.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +f_dialog_rcedit "$@" + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar new file mode 100755 index 0000000000000..e66f3bbb04a46 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -0,0 +1,200 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/sysrc.subr + +APP_DIR="140.startup" +f_include $BSDCFG_LIBE/$APP_DIR/include/rcvar.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ GLOBALS + +# +# Global map/menu-list for the main menu +# +RCVAR_MAP= +_RCVAR_MAP= +RCVAR_MENU_LIST= + +# +# Options +# +# Inherit SHOW_DESC value if set, otherwise default to 1 +( ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local size + local hline="$hline_arrows_tab_enter" + local prompt="" + + RCVAR_MENU_LIST=" + 'X $msg_exit' '$msg_exit_this_menu' + ${SHOW_DESC:+'$msg_exit_this_menu'} + " # END-QUOTE + + if [ ! "$_RCVAR_MAP" ]; then + # Genreate RCVAR_MAP of `rcvar dflt script desc ...' per-line + f_dialog_info "$msg_creating_rcvar_map" + RCVAR_MAP=$( f_startup_rcvar_map ) + export RCVAR_MAP + export _RCVAR_MAP=1 + fi + + RCVAR_MENU_LIST="$RCVAR_MENU_LIST $( + . "$RC_DEFAULTS" > /dev/null + source_rc_confs > /dev/null + for rcvar in $( echo "$RCVAR_MAP" | awk '{print $1}' ); do + eval export $rcvar + done + export SHOW_DESC msg_default_value + echo "$RCVAR_MAP" | awk ' + BEGIN { + prefix = "" + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + rcvar = $1 + default = $2 + script = $3 + printf "%s'\'' '\''", rcvar + if ( ENVIRON[rcvar] ~ /[Yy][Ee][Ss]/ ) + printf "[X] " + else + printf "[ ] " + printf "%s; " ENVIRON["msg_default_value"], + script, default + printf "'\''" + if ( ENVIRON["SHOW_DESC"] ) { + desc = $0 + sub(rword, "", desc) + sub(rword, "", desc) + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf " '\''%s'\''", desc + } + printf "\n" + }' + )" + + set -f # noglob + + size=$( eval f_dialog_menu_${SHOW_DESC:+with_help_}size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $RCVAR_MENU_LIST ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + ${SHOW_DESC:+--item-help} \ + --menu \"\$prompt\" $size \ + $RCVAR_MENU_LIST \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_toggle_startup_services" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + *) # Anything else is an rcvar to toggle + + rcvar="${mtag# }" + value=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ + \"\$mtag\" $RCVAR_MENU_LIST ) + + # Determine the new [toggled] value to use + case "$value" in + "[X]"*) value="NO";; + *) value="YES";; + esac + + err=$( f_sysrc_set "$rcvar" "$value" 2>&1 ) || + f_show_msg "$err" + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/startup/startup b/usr.sbin/bsdconfig/startup/startup new file mode 100755 index 0000000000000..70e82e0981351 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/startup @@ -0,0 +1,128 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + local prompt="" + + menu_list=" + 'X' '$msg_exit' + '1' '$msg_toggle_startup_services' + '2' '$msg_view_edit_startup_configuration' + '3' '$msg_miscellaneous_startup_services' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_startup" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + X) # Exit + break ;; + 1) # Toggle Startup Services + $BSDCFG_LIBE/$APP_DIR/rcvar ${USE_XDIALOG:+-X} ;; + 2) # View/Edit Startup Configuration + $BSDCFG_LIBE/$APP_DIR/rcconf ${USE_XDIALOG:+-X} ;; + 3) # Miscellaneous Startup Services + $BSDCFG_LIBE/$APP_DIR/misc ${USE_XDIALOG:+-X} ;; + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/timezone/INDEX b/usr.sbin/bsdconfig/timezone/INDEX new file mode 100644 index 0000000000000..af17594ccf980 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/INDEX @@ -0,0 +1,53 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Timezone" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Set up Time Zone" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="timezone|timezone" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="timezone" diff --git a/usr.sbin/bsdconfig/timezone/Makefile b/usr.sbin/bsdconfig/timezone/Makefile new file mode 100644 index 0000000000000..f77a37b693fa4 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/090.timezone +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= timezone + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/timezone/USAGE b/usr.sbin/bsdconfig/timezone/USAGE new file mode 100644 index 0000000000000..a88369cf2173c --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/USAGE @@ -0,0 +1,42 @@ +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [-ehnrSsvX] [-C chroot_dir] [zinfo_file | zinfo_name] +OPTIONS: + -h Print usage statement and exit. + -e Only return success on exit if user selects a timezone AND + the selected timezone was successfully installed. By default + (without this flag), success is always returned unless an + error has occurred. + -n Do not create or copy files. + -r Reinstall the zoneinfo file installed last time. The name is + obtained from /var/db/zoneinfo. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -s Skip the initial question about adjusting the clock if + not set to UTC. + -v Verbose. Enable extra output when installing the zone file. + -X Enable the use of Xdialog(1) instead of dialog(1). diff --git a/usr.sbin/bsdconfig/timezone/include/Makefile b/usr.sbin/bsdconfig/timezone/include/Makefile new file mode 100644 index 0000000000000..a4c5ba1df3bea --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/090.timezone/include +FILES= continents.subr countries.subr iso3166.subr menus.subr \ + messages.subr zones.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/timezone/include/continents.subr b/usr.sbin/bsdconfig/timezone/include/continents.subr new file mode 100644 index 0000000000000..126020cdf936d --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/continents.subr @@ -0,0 +1,138 @@ +if [ ! "$_TIMEZONE_CONTINENTS_SUBR" ]; then _TIMEZONE_CONTINENTS_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 + +APP_DIR="090.timezone" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# List of worldly continents/oceans (export'ed for awk(1) ENVIRON visibility) +# +export CONTINENTS=" + africa + america + antarctica + arctic + asia + atlantic + australia + europe + indian + pacific + utc +" + +# +# Directory name of each continent/ocean (in _PATH_ZONEINFO) +# +export continent_africa_name="Africa" +export continent_america_name="America" +export continent_antarctica_name="Antarctica" +export continent_arctic_name="Arctic" +export continent_asia_name="Asia" +export continent_atlantic_name="Atlantic" +export continent_australia_name="Australia" +export continent_europe_name="Europe" +export continent_indian_name="Indian" +export continent_pacific_name="Pacific" +export continent_utc_name="UTC" + +# +# Export i18n menu texts of continents/oceans for awk(1) ENVIRON visibility +# NOTE: These are defined in messages.subr included above. +# +export continent_africa_title +export continent_america_title +export continent_antarctica_title +export continent_arctic_title +export continent_asia_title +export continent_atlantic_title +export continent_australia_title +export continent_europe_title +export continent_indian_title +export continent_pacific_title +export continent_utc_title + +############################################################ FUNCTIONS + +# f_continent $cont $property +# +# Returns a single property of a given continent. Available properties are: +# +# name Directory name of continent/ocean as it appears in +# _PATH_ZONEINFO. +# title Menu text of this continent/ocean to be displayed in the +# continent-selection menu. +# nitems Number of submenu items associated with this +# continent/ocean. +# tlc_N 2-character country code of the Nth submenu item associated +# with this continent displayed in the country-selection menu +# (which appears after continent selection). +# menu_list Menu-list of regions for this continent. +# +f_continent() +{ + local cont="$1" property="$2" + eval echo \"\${continent_${cont}_$property}\" +} + +# f_find_continent $title +# +# Returns continent identifier given continent title. +# +f_find_continent() +{ + local cont + for cont in $CONTINENTS; do + if [ "$1" = "$( f_continent $cont title )" ]; then + echo "$cont" + return $SUCCESS + fi + done + return $FAILURE +} + +# f_OCEANP $cont +# +# Returns "1" if the first argument is an ocean, otherwise NULL. +# +f_OCEANP() +{ + case "$1" in + arctic|atlantic|indian|pacific) + echo 1 + esac +} + +fi # ! $_TIMEZONE_CONTINENTS_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/countries.subr b/usr.sbin/bsdconfig/timezone/include/countries.subr new file mode 100644 index 0000000000000..9c6a164e213ab --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/countries.subr @@ -0,0 +1,78 @@ +if [ ! "$_TIMEZONE_COUNTRIES_SUBR" ]; then _TIMEZONE_COUNTRIES_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# f_country $code $property +# +# Returns a single property of a given country. Available properties are: +# +# name Name of the country as read from _PATH_ISO3166. +# nzones Number of zones within the country (-1 if country has +# only a single zone). +# filename The filename portion of the TZ field (after the `/') as +# read from _PATH_ZONETAB. +# cont The principal continent in which the country lies (appears +# before the `/' in the TZ field of _PATH_ZONETAB). +# filename_N Like filename, but for the Nth zone when the country has +# multiple zones (nzones > 0). +# cont_N Like cont, but for the Nth zone when the country has +# multiple zones (nzones > 0). +# descr_N Like name, but for the Nth zone when the country has +# multiple zones (nzones > 0) +# +f_country() +{ + local code="$1" property="$2" + eval echo \"\${country_${code}_$property}\" +} + +# f_sort_countries +# +# Sorts alphabetically the 2-character country codes listed in $COUNTRIES based +# on the name of each country. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sort_countries_awk=' +{ + split($0, array, /[[:space:]]+/) + for (item in array) + { + tlc = array[item] + print ENVIRON["country_" tlc "_name"] " " tlc + } +} +' +f_sort_countries() +{ + COUNTRIES=$( echo "$COUNTRIES" | awk "$f_sort_countries_awk" | + sort | awk '{print $NF}' ) + export COUNTRIES +} + +fi # ! $_TIMEZONE_COUNTRIES_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/iso3166.subr b/usr.sbin/bsdconfig/timezone/include/iso3166.subr new file mode 100644 index 0000000000000..d69f3da4d0a37 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/iso3166.subr @@ -0,0 +1,197 @@ +if [ ! "$_TIMEZONE_ISO3166_SUBR" ]; then _TIMEZONE_ISO3166_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 + +APP_DIR="090.timezone" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Standard pathnames +# +_PATH_ISO3166="/usr/share/misc/iso3166" + +# +# Export required i18n messages for awk(1) ENVIRON visibility +# +export msg_country_code_multiply_defined +export msg_invalid_code +export msg_invalid_format + +############################################################ FUNCTIONS + +# f_read_iso3166_table +# +# Read the ISO 3166 country code database in _PATH_ISO3166: +# /usr/share/misc/iso3166 on FreeBSD +# /usr/share/zoneinfo/iso3166.tab on Linux, Mac OS X, and Cygwin +# +# The format of this file on FreeBSD is: +# two three number name +# +# The format of this file on Linux, Mac OS X, and Cygwin is: +# two name +# +# With each of the following elements (described below) being separated by a +# single tab character: +# +# two ISO 3166 2-character country code +# three ISO 3166 3-character country code (if provided) +# number ISO 3166 numeric country code (if provided) +# name Human-readable country name (may contain spaces) +# +# Variables created by this function: +# +# COUNTRIES +# A space-separated list of 2-character country codes. +# country_CODE_name +# The country `name' (as described above). +# +# where CODE is the 2-character country code. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_read_iso3166_table_awk=' +# Variables that should be defined on the invocation line: +# -v progname="progname" +# +BEGIN { + lineno = 0 + failed = 0 +} +function die(fmt, argc, argv) +{ + printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname + for (n = 1; n <= argc; n++) + printf " \"%s\"", argv[n] + print "" + failed++ + exit 1 +} +function add_country(tlc, name) +{ + if (country_name[tlc]) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + argv[4] = name + die(ENVIRON["msg_country_code_multiply_defined"], 4, argv) + } + + country_name[tlc] = name +} +function print_country_name(tlc) +{ + name = country_name[tlc] + gsub(/"/, "\\\"", name) + printf "country_%s_name=\"%s\"\n", tlc, name + printf "export country_%s_name\n", tlc +} +/^#/ { + lineno++ + next +} +!/^#/ { + lineno++ + + # Split the current record (on TAB) into an array + split($0, line, /\t/) + + # Get the ISO3166-1 (Alpha 1) 2-letter country code + tlc = line[1] + + # + # Validate the two-character country code + # + if (length(tlc) != 2) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_invalid_format"], 2, argv) + } + if (!match(tlc, /^[A-Z][A-Z]$/)) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVIRON["msg_invalid_code"], 3, argv) + } + + # + # Calculate the substr start-position of the name + # + name_start = 0 + n = 4 + if (FILENAME ~ /\.tab$/) + n = 2 + while (--n) + { + # + # Validate field-length of 2nd/3rd columns while we are here + # + if (n > 1 && length(line[n]) != 3) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_invalid_format"], 2, argv) + } + + name_start += length(line[n]) + 1 + } + + # Get the name field + name = substr($0, name_start + 1) + + add_country(tlc, name) +} +END { + list = "" + for (tlc in country_name) + { + list = list (length(list) > 0 ? " " : "") tlc + print_country_name(tlc) + } + printf "COUNTRIES=\"%s\"\n", list + print "export COUNTRIES" +} +' +f_read_iso3166_table() +{ + eval $( awk -v progname="$pgm" \ + "$f_read_iso3166_table_awk" \ + "$_PATH_ISO3166" ) +} + +fi # ! $_TIMEZONE_ISO3166_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/menus.subr b/usr.sbin/bsdconfig/timezone/include/menus.subr new file mode 100644 index 0000000000000..46bdc0446b809 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/menus.subr @@ -0,0 +1,220 @@ +if [ ! "$_TIMEZONE_MENUS_SUBR" ]; then _TIMEZONE_MENUS_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr + +############################################################ GLOBALS + +# +# Export special included variables required by awk(1) for `ENVIRON' visibility +# +export DIALOG_MENU_TAGS + +############################################################ FUNCTIONS + +# f_make_menus +# +# Creates the tag/item ordered-pair list environment variables for the +# continent and country menus. +# +# Required variables [from continents.subr]: +# +# CONTINENTS +# Space-separated list of continents. +# continent_*_title +# Desired menu text for the continent represented by *. +# +# Required variables [created by f_read_iso3166_table from iso3166.subr]: +# +# COUNTRIES +# Space-separated list of 2-character country codes. +# country_*_name :: when country_*_nzones < 0 +# Desired menu text for the country-zone represented by *, the 2- +# character country code. +# +# Required variables [created by f_read_zones from zones.subr]: +# +# country_*_nzones +# Number of zones for the country represented by *, the 2- +# character country code. Should be -1 if the country has only +# one single zone, otherwise 1 or greater to indicate how many +# zones the country has. +# country_*_cont :: when country_*_nzones < 0 +# Principal continent (or ocean) in which the country-zone +# represented by *, the 2-character country code, resides. +# country_*_cont_N :: when country_*_nzones > 0 +# Principal continent (or ocean) in which zone-N of the country +# represented by * resides, the 2-character country code. +# country_*_descr_N :: when country_*_nzones > 0 +# Desired submenu text for zone-N of the country represented by +# *, the 2-character country code. +# +# Variables created by this function: +# +# continent_menu_list +# Menu-list of continents. +# continent_*_nitems +# Number of items associated with the continent represented by *, +# the continent identifier. +# continent_*_tlc_N +# 2-character country code of the Nth item in the continent menu +# for the continent represented by *, the continent identifier. +# continent_*_menu_list +# Menu-list of countries/zones for each continent represented by +# *, the continent identifier. +# country_*_menu_list +# For countries that have multiple zones, this is the submenu- +# list of zones for said country represented by *, the 2- +# character country code. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_make_menus_awk=' +function add_zone_n_to_country_menu(tlc, n) +{ + zone_title = ENVIRON["country_" tlc "_descr_" n] + gsub(/'\''/, "'\''\\'\'''\''", zone_title) + country_menu_list[tlc] = country_menu_list[tlc] \ + ( length(country_menu_list[tlc]) > 0 ? "\n" : "" ) \ + n " '\''" zone_title "'\''" +} +BEGIN { + # + # First, count up all the countries in each continent/ocean. + # Be careful to count those countries which have multiple zones + # only once for each. NB: some countries are in multiple + # continents/oceans. + # + i = split(ENVIRON["COUNTRIES"], countries, /[[:space:]]+/) + for (cp = 1; cp <= i; cp++) + { + tlc = countries[cp] + title = ENVIRON["country_" tlc "_name"] + gsub(/'\''/, "'\''\\'\'''\''", title) + nzones = ENVIRON["country_" tlc "_nzones"] + if (!nzones) + { + # Country has no zones + continue + } + else if (nzones < 0) + { + # Country has only one zone + cont = ENVIRON["country_" tlc "_cont"] + nitems = ++continent_nitems[cont] + continent_tlc[cont,nitems] = tlc + continent_title[cont,nitems] = title + } + else + { + # Country has one or more zones + for (n = 1; n <= nzones; n++) + { + add_zone_n_to_country_menu(tlc, n) + cont = ENVIRON["country_" tlc "_cont_" n] + for (x = 1; x < n; x++) + { + contx = ENVIRON["country_"tlc"_cont_"x] + if (cont == contx) break + } + if (x == n) + { + nitems = ++continent_nitems[cont] + continent_tlc[cont,nitems] = tlc + continent_title[cont,nitems] = title + } + } + } + } +} +END { + tags = ENVIRON["DIALOG_MENU_TAGS"] + cont_menu_list = "" + tagn = 0 + + # + # Assemble the menu items in the menu list for each continent/ocean. + # + i = split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) + for (item = 1; item <= i; item++) + { + cont = array[item] + if (!cont) continue + + if (++tagn >= length(tags)) break + tag = substr(tags, tagn, 1) + cont_menu_list = cont_menu_list \ + ( length(cont_menu_list) > 0 ? "\n" : "" ) \ + "'\''" tag "'\'' '\''" \ + ENVIRON["continent_" cont "_title"] "'\''" + + nitems = continent_nitems[cont] + printf "continent_%s_nitems=%d\n", cont, nitems + + menu_list = "" + for (n = 1; n <= nitems; n++) + { + printf "continent_%s_tlc_%d=%s\n", + cont, n, continent_tlc[cont,n] + + title = continent_title[cont,n] + menu_list = menu_list \ + ( length(menu_list) > 0 ? "\n" : "" ) \ + n " '\''" title "'\''" + } + + gsub(/"/, "\\\"", menu_list) + printf "continent_%s_menu_list=\"%s\"\n", cont, menu_list + } + + gsub(/"/, "\\\"", continent_menu_list) + printf "continent_menu_list=\"%s\"\n", cont_menu_list + print "export continent_menu_list" + + # + # Dump the submenus of countries with multiple zones + # + for (tlc in country_menu_list) + { + menu_list = country_menu_list[tlc] + gsub(/"/, "\\\"", menu_list) + printf "country_%s_menu_list=\"%s\"\n", tlc, menu_list + } +} +' +f_make_menus() +{ + eval $( :| awk "$f_make_menus_awk" ) +} + +fi # ! $_TIMEZONE_MENUS_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/messages.subr b/usr.sbin/bsdconfig/timezone/include/messages.subr new file mode 100644 index 0000000000000..6a7f045cd4f2f --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/messages.subr @@ -0,0 +1,78 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +continent_africa_title="Africa" +continent_america_title="America -- North and South" +continent_antarctica_title="Antarctica" +continent_arctic_title="Arctic Ocean" +continent_asia_title="Asia" +continent_atlantic_title="Atlantic Ocean" +continent_australia_title="Australia" +continent_europe_title="Europe" +continent_indian_title="Indian Ocean" +continent_pacific_title="Pacific Ocean" +continent_utc_title="UTC" +msg_cancel="Cancel" +msg_cannot_open_for_reading="Cannot open %s for reading. Does it exist?" +msg_confirmation="Confirmation" +msg_conflicting_zone_definition="%s:%d: conflicting zone definition" +msg_copied_timezone_file="Copied timezone file from %s to %s" +msg_copying_file="Copying %s to %s" +msg_country_code_invalid="%s:%d: country code \`%s' invalid" +msg_country_code_multiply_defined="%s:%d: country code \`%s' multiply defined: %s" +msg_country_code_unknown="%s:%d: country code \`%s' unknown" +msg_country_time_zones="%s Time Zones" +msg_country_title="Countries in %s" +msg_created_symlink="Created symbolic link from %s to %s" +msg_creating_symlink="Creating symbolic link %s to %s" +msg_default_zone_provided="Default timezone provided" +msg_done="Done" +msg_error="Error" +msg_error_reading="Error reading %s." +msg_info="Info" +msg_invalid_code="%s:%d: invalid code \`%s'" +msg_invalid_country_code="%s:%d: invalid country code \`%s'" +msg_invalid_format="%s:%d: invalid format" +msg_invalid_region="%s:%d: invalid region \`%s'" +msg_invalid_zone_name="%s:%d: invalid zone name \`%s'" +msg_is_machine_clock_utc="Is the machine's CMOS clock set to UTC? If it is set to local time,\nor you don't know, please choose NO here!" +msg_island_and_group_title="Islands and groups in the %s" +msg_look_reasonable="Does the abbreviation \`%s' look reasonable?" +msg_no="No" +msg_ok="OK" +msg_removed_file="Removed %s" +msg_removing_file="Removing %s" +msg_select_country="Select a country" +msg_select_island_or_group="Select an island or group" +msg_select_local_or_utc="Select local or UTC (Greenwhich Mean Time) clock" +msg_select_region="Select a region" +msg_select_zone="Select a zone which observes the same time as your locality." +msg_time_zone="Time Zone" +msg_unable_to_determine_name_from_db="Unable to determine earlier installed zoneinfo name. Check %s" +msg_use_default_zone="Use the default \`%s' zone?" +msg_yes="Yes" +msg_zone_multiply_defined="%s:%d: zone multiply defined" +msg_zone_must_have_description="%s:%d: zone must have description" diff --git a/usr.sbin/bsdconfig/timezone/include/zones.subr b/usr.sbin/bsdconfig/timezone/include/zones.subr new file mode 100644 index 0000000000000..704d02f975239 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/include/zones.subr @@ -0,0 +1,670 @@ +if [ ! "$_TIMEZONE_ZONES_SUBR" ]; then _TIMEZONE_ZONES_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr + +APP_DIR="090.timezone" +f_include $BSDCFG_LIBE/$APP_DIR/include/continents.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Standard pathnames +# +_PATH_ZONETAB="/usr/share/zoneinfo/zone.tab" +_PATH_ZONEINFO="/usr/share/zoneinfo" +_PATH_LOCALTIME="/etc/localtime" +_PATH_DB="/var/db/zoneinfo" + +# +# Export required i18n messages for awk(1) ENVIRON visibility +# +export msg_conflicting_zone_definition +export msg_country_code_invalid +export msg_country_code_unknown +export msg_invalid_country_code +export msg_invalid_format +export msg_invalid_region +export msg_invalid_zone_name +export msg_zone_multiply_defined +export msg_zone_must_have_description + +############################################################ FUNCTIONS + +# f_read_zones +# +# Read the zone descriptions database in _PATH_ZONETAB: +# /usr/share/zoneinfo/zone.tab on all OSes +# +# The format of this file (on all OSes) is: +# code coordinates TZ comments +# +# With each of the following elements (described below) being separated by a +# single tab character: +# +# code +# The ISO 3166 2-character country code. +# coordinates +# Latitude and logitude of the zone's principal location in ISO +# 6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM +# or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long- +# itude (+ is east). +# TZ +# Zone name used in value of TZ environment variable. +# comments +# Comments; present if and only if the country has multiple rows. +# +# Required variables [from continents.subr]: +# +# CONTINENTS +# Space-separated list of continents. +# continent_*_name +# Directory element in _PATH_ZONEINFO for the continent +# represented by *. +# +# Required variables [created by f_read_iso3166_table from iso3166.subr]: +# +# country_CODE_name +# Country name of the country represented by CODE, the 2- +# character country code. +# +# Variables created by this function: +# +# country_CODE_nzones +# Either set to `-1' to indicate that the 2-character country +# code has only a single zone associated with it (and therefore +# you should query the `country_CODE_*' environment variables), +# or set to `0' or higher to indicate how many zones are assoc- +# iated with the given country code. When multiple zones are +# configured for a single code, you should instead query the +# `country_CODE_*_N' environment variables (e.g., `echo +# $country_AQ_descr_1' prints the description of the first +# timezone in Antarctica). +# country_CODE_filename +# The ``filename'' portion of the TZ value that appears after the +# `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man' +# from `Europe/Isle_of_Man'). +# country_CODE_cont +# The ``continent'' portion of the TZ value that appears before +# the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from +# `Europe/Isle_of_Man'). +# country_CODE_descr +# The comments associated with the ISO 3166 code entry (if any). +# +# NOTE: CODE is the 2-character country code. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_read_zones_awk=' +# Variables that should be defined on the invocation line: +# -v progname="progname" +# +BEGIN { + lineno = 0 + failed = 0 + + # + # Initialize continents array/map (name => id) + # + split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) + for (item in array) + { + cont = array[item] + if (!cont) continue + name = ENVIRON["continent_" cont "_name"] + continents[name] = cont + } +} +function die(fmt, argc, argv) +{ + printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname + for (n = 1; n <= argc; n++) + printf " \"%s\"", argv[n] + print "" + failed++ + exit 1 +} +function find_continent(name) +{ + return continents[name] +} +function add_zone_to_country(lineno, tlc, descr, file, cont) +{ + # + # Validate the two-character country code + # + if (!match(tlc, /^[A-Z][A-Z]$/)) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVRION["msg_country_code_invalid"], 3, argv) + } + if (!ENVIRON["country_" tlc "_name"]) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVIRON["msg_country_code_unknown"], 3, argv) + } + + # + # Add Zone to an array that we will parse at the end + # + if (length(descr) > 0) + { + if (country_nzones[tlc] < 0) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_conflicting_zone_definition"], 2, argv) + } + + n = ++country_nzones[tlc] + country_cont[tlc,n] = cont + country_filename[tlc,n] = file + country_descr[tlc,n] = descr + } + else + { + if (country_nzones[tlc] > 0) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_zone_must_have_description"], 2, argv) + } + if (country_nzones[tlc] < 0) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_zone_multiply_defined"], 2, argv) + } + + country_nzones[tlc] = -1 + country_cont[tlc] = cont + country_filename[tlc] = file + } +} +function print_country_code(tlc) +{ + nz = country_nzones[tlc] + + printf "country_%s_nzones=%d\n", tlc, nz + printf "export country_%s_nzones\n", tlc + + if (nz < 0) + { + printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc] + printf "export country_%s_cont\n", tlc + printf "country_%s_filename=\"%s\"\n", + tlc, country_filename[tlc] + } + else + { + n = 0 + while ( ++n <= nz ) + { + printf "country_%s_cont_%d=\"%s\"\n", + tlc, n, country_cont[tlc,n] + printf "export country_%s_cont_%d\n", tlc, n + printf "country_%s_filename_%d=\"%s\"\n", + tlc, n, country_filename[tlc,n] + printf "country_%s_descr_%d=\"%s\"\n", + tlc, n, country_descr[tlc,n] + } + } +} +/^#/ { + lineno++ + next +} +!/^#/ { + lineno++ + + # + # Split the current record (on TAB) into an array + # + if (split($0, line, /\t/) < 2) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_invalid_format"], 2, argv) + } + + # Get the ISO3166-1 (Alpha 1) 2-letter country code + tlc = line[1] + + # + # Validate the two-character country code + # + if (length(tlc) != 2) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVIRON["msg_invalid_country_code"], 3, argv) + } + + # Get the TZ field + tz = line[3] + + # + # Validate the TZ field + # + if (!match(tz, "/")) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tz + die(ENVIRON["msg_invalid_zone_name"], 3, argv) + } + + # + # Get the continent portion of the TZ field + # + contbuf = tz + sub("/.*$", "", contbuf) + + # + # Validate the continent + # + cont = find_continent(contbuf) + if (!cont) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = contbuf + die(ENVIRON["msg_invalid_region"], 3, argv) + } + + # + # Get the filename portion of the TZ field + # + filename = tz + sub("^[^/]*/", "", filename) + + # + # Calculate the substr start-position of the comment + # + descr_start = 0 + n = 4 + while (--n) + descr_start += length(line[n]) + 1 + + # Get the comment field + descr = substr($0, descr_start + 1) + + add_zone_to_country(lineno, tlc, descr, filename, cont) +} +END { + if (failed) exit failed + for (tlc in country_nzones) + print_country_code(tlc) +} +' +f_read_zones() +{ + eval $( awk -v progname="$pgm" \ + "$f_read_zones_awk" \ + "$_PATH_ZONETAB" ) +} + +# f_install_zoneinfo_file $filename +# +# Installs a zone file to _PATH_LOCALTIME. +# +f_install_zoneinfo_file() +{ + local zoneinfo_file="$1" + local copymode title msg err size + + if [ -L "$_PATH_LOCALTIME" ]; then + copymode= + elif [ ! -e "$_PATH_LOCALTIME" ]; then + # Nothing there yet... + copymode=1 + else + copymode=1 + fi + + if [ "$VERBOSE" ]; then + if [ ! "$zoneinfo_file" ]; then + msg=$( printf "$msg_removing_file" "$_PATH_LOCALTIME" ) + elif [ "$copymode" ]; then + msg=$( printf "$msg_copying_file" \ + "$zoneinfo_file" "$_PATH_LOCALTIME" ) + else + msg=$( printf "$msg_creating_symlink" \ + "$_PATH_LOCALTIME" "$zoneinfo_file" ) + fi + if [ "$USEDIALOG" ]; then + f_dialog_title "$msg_info" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + size=$( f_dialog_buttonbox_size "$title" \ + "$btitle" "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" + fi + fi + + if [ "$REALLYDOIT" ]; then + f_dialog_title "$msg_error" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + + if [ ! "$zoneinfo_file" ]; then + + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( rm -f "$_PATH_DB" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + if [ "$VERBOSE" ]; then + title="$msg_done" + msg=$( printf "$msg_removed_file" \ + "$_PATH_LOCALTIME" ) + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" + fi + fi + + return $SUCCESS + + fi # ! zoneinfo_file + + if [ "$copymode" ]; then + + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + else # ! copymode + + err=$( ( :< "$zoneinfo_file" ) 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + fi # copymode + + if [ "$VERBOSE" ]; then + title="$msg_done" + if [ "$copymode" ]; then + msg=$( printf "$msg_copied_timezone_file" \ + "$zoneinfo_file" \ + "$_PATH_LOCALTIME" ) + else + msg=$( printf "$msg_created_symlink" \ + "$_PATH_LOCALTIME" \ + "$zoneinfo_file" ) + fi + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" "$btitle" "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" + fi + fi + + fi # REALLYDOIT + + return $SUCCESS +} + +# f_install_zoneinfo $zoneinfo +# +# Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo +# will be written to _PATH_DB (usable later with the `-r' flag). +# +f_install_zoneinfo() +{ + local zoneinfo="$1" + local rv + + f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo" + rv=$? + + # Save knowledge for later + if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then + if : 2> /dev/null > "$_PATH_DB"; then + cat <<-EOF > "$_PATH_DB" + $zoneinfo + EOF + fi + fi + + return $rv +} + +# f_confirm_zone $filename +# +# Prompt the user to confirm the new timezone data. The first (and only) +# argument should be the pathname to the zoneinfo file, either absolute or +# relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles"). +# +# The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is +# pressed (see dialog(1) for additional details). +# +f_confirm_zone() +{ + local filename="$1" + f_dialog_title "$msg_confirmation" + local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + local tm_zone="$( TZ="$filename" date +%Z )" + local prompt="$( printf "$msg_look_reasonable" "$tm_zone" )" + local height=5 width=72 + + if [ "$USE_XDIALOG" ]; then + height=$(( $height + 4 )) + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --ok-label "$msg_yes" \ + --cancel-label "$msg_no" \ + --yesno "$prompt" $height $width + else + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --yes-label "$msg_yes" \ + --no-label "$msg_no" \ + --yesno "$prompt" $height $width + fi +} + +# f_set_zone_utc +# +# Resets to the UTC timezone. +# +f_set_zone_utc() +{ + f_confirm_zone "" || return $FAILURE + f_install_zoneinfo_file "" +} + +fi # ! $_TIMEZONE_ZONES_SUBR diff --git a/usr.sbin/bsdconfig/timezone/timezone b/usr.sbin/bsdconfig/timezone/timezone new file mode 100755 index 0000000000000..2b41d908d3094 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/timezone @@ -0,0 +1,448 @@ +#!/bin/sh +#- +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr +f_include $BSDCFG_LIBE/include/strings.subr + +APP_DIR="090.timezone" +f_include $BSDCFG_LIBE/$APP_DIR/include/continents.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/countries.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/iso3166.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/menus.subr +f_include $BSDCFG_LIBE/$APP_DIR/include/zones.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Standard pathnames +# +_PATH_DB="/var/db/zoneinfo" +_PATH_WALL_CMOS_CLOCK="/etc/wall_cmos_clock" + +############################################################ GLOBALS + +# +# Options +# +REALLYDOIT=1 +REINSTALL= +USEDIALOG=1 +SKIPUTC= +VERBOSE= +TZ_OR_FAIL= +CHROOTENV= + +# +# Dummy vars (populated dynamically) +# +COUNTRIES= # list of 2-character country codes created by f_read_iso3166_table + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local title="$DIALOG_TITLE" + local btitle="$DIALOG_BACKTITLE" + local prompt="$msg_select_region" + local size + + size=$( eval f_dialog_menu_size \ + \"\$title\" \ + \"\$btitle\" \ + \"\$prompt\" \ + \"\" \ + $continent_menu_list ) + + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $continent_menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts C:ehnrsSvX flag; do + case "$flag" in + C) CHROOTENV="$OPTARG";; + e) TZ_OR_FAIL=1;; + n) REALLYDOIT=;; + r) REINSTALL=1 + USEDIALOG=;; + s) SKIPUTC=1;; + v) VERBOSE=1;; + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_time_zone" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Process `-C chroot_directory' command-line argument +# +if [ "$CHROOTENV" ]; then + _PATH_ZONETAB="$CHROOTENV$_PATH_ZONETAB" + _PATH_ISO3166="$CHROOTENV$_PATH_ISO3166" + _PATH_ZONEINFO="$CHROOTENV$_PATH_ZONEINFO" + _PATH_LOCALTIME="$CHROOTENV$_PATH_LOCALTIME" + _PATH_DB="$CHROOTENV$_PATH_DB" + _PATH_WALL_CMOS_CLOCK="$CHROOTENV$_PATH_WALL_CMOS_CLOCK" +fi + +# +# Process `-r' command-line option +# +if [ "$REINSTALL" ]; then + [ -f "$_PATH_DB" -a -r "$_PATH_DB" ] || + f_die 1 "$msg_cannot_open_for_reading" "$_PATH_DB" + zoneinfo=$( cat "$_PATH_DB" ) || + f_die 1 "$msg_error_reading" "$_PATH_DB" + [ "$zoneinfo" ] || + f_die 1 "$msg_unable_to_determine_name_from_db" "$_PATH_DB" + f_install_zoneinfo "$zoneinfo" + exit $? +fi + +# +# If the arguments on the command-line do not specify a file, +# then interpret it as a zoneinfo name +# +if [ $# -ge 1 ]; then + zoneinfo="$1" + + if [ ! -f "$zoneinfo" ]; then + USEDIALOG= + f_install_zoneinfo "$zoneinfo" + exit $? + fi + + # FALLTHROUGH +fi + +# +# Process the UTC option +# +if [ "$_PATH_WALL_CMOS_CLOCK" -a ! "$SKIPUTC" ]; then + f_dialog_title "$msg_select_local_or_utc" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + msg="$msg_is_machine_clock_utc" + + if [ "$USE_XDIALOG" ]; then + defaultno="default-no" + height=10 width=77 + else + defaultno="defaultno" + height=7 width=73 + fi + + if [ "$USE_XDIALOG" ]; then + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --$defaultno \ + --ok-label "$msg_yes" \ + --cancel-label "$msg_no" \ + --yesno "$msg" $height $width + result=$? + else + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --$defaultno \ + --yes-label "$msg_yes" \ + --no-label "$msg_no" \ + --yesno "$msg" $height $width + result=$? + fi + + if [ $result -eq 0 ]; then + # User chose YES + [ "$REALLYDOIT" ] && + f_quietly rm -f "$_PATH_WALL_CMOS_CLOCK" + else + # User chose NO, pressed ESC (or Ctrl-C), or closed box + [ "$REALLYDOIT" ] && + ( umask 222 && :> "$_PATH_WALL_CMOS_CLOCK" ) + fi +fi + +# +# Process optional default zone argument +# +if [ $# -ge 1 ]; then + default="$1" + + f_dialog_title "$msg_default_zone_provided" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + msg=$( printf "\n$msg_use_default_zone" "$default" ) + size=$( f_dialog_buttonbox_size "$title" "$btitle" "$msg" ) + + if [ "$USE_XDIALOG" ]; then + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_yes\" \ + --cancel-label \"\$msg_no\" \ + --yesno \"\$msg\" $size + result=$? + else + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --yes-label \"\$msg_yes\" \ + --no-label \"\$msg_no\" \ + --yesno \"\$msg\" $size + result=$? + fi + + if [ $result -eq 0 ]; then + # User chose YES + f_install_zoneinfo_file "$default" + result=$? + [ ! "$USE_XDIALOG" ] && f_dialog_clear + exit $result + fi + + [ ! "$USE_XDIALOG" ] && f_dialog_clear +fi + +# +# Override the user-supplied umask +# +umask 022 + +# +# Read databases and perform initialization +# +f_read_iso3166_table # creates $COUNTRIES and $country_*_name +f_read_zones # creates $country_*_{descr,cont,filename} +f_sort_countries # sorts the countries listed for each continent +f_make_menus # creates $continent_menu_list and $continent_*_menu_list + +# +# Launch application main menu +# +NEED_CONTINENT=1 +NEED_COUNTRY=1 +while :; do + if [ "$NEED_CONTINENT" ]; then + dialog_menu_main # prompt the user to select a continent/ocean + retval=$? + mtag=$( f_dialog_menutag ) + + if [ $retval -ne 0 ]; then + [ "$TZ_OR_FAIL" ] && f_die + exit $SUCCESS + fi + + NEED_CONTINENT= + + continent=$( eval f_dialog_menutag2item \"\$mtag\" \ + $continent_menu_list ) + cont=$( f_find_continent "$continent" ) + cont_title=$( f_continent $cont title ) + nitems=$( f_continent $cont nitems ) + isocean=$( f_OCEANP $cont ) + fi + + if [ "$NEED_COUNTRY" ]; then + if [ "$cont_title" = "$continent_utc_title" ]; then + if f_set_zone_utc; then + break + else + NEED_CONTINENT=1 + continue + fi + fi + + # + # Short cut -- if there's only one country, don't post a menu. + # + if [ $nitems -eq 1 ]; then + tag=1 + else + # + # It's amazing how much good grammar really matters... + # + if [ ! "$isocean" ]; then + title=$( printf "$msg_country_title" \ + "$cont_title" ) + f_dialog_title "$title" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + prompt="$msg_select_country" + else + title=$( printf "$msg_island_and_group_title" \ + "$cont_title" ) + f_dialog_title "$title" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + prompt="$msg_select_island_or_group" + fi + + # + # Calculate size of menu + # + menu_list=$( f_continent $cont menu_list ) + size=$( eval f_dialog_menu_size \ + \"\$title\" \ + \"\$btitle\" \ + \"\$prompt\" \ + \"\" \ + $menu_list ) + + # + # Launch the country selection menu + # + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + retval=$? + tag=$( f_dialog_menutag ) + + if [ $retval -ne 0 ]; then + NEED_CONTINENT=1 + continue # back to main menu + fi + fi + + # Get the country code from the user's selection + tlc=$( f_continent $cont tlc_$tag ) + + NEED_COUNTRY= + fi + + # + # If the selection has only one zone (nzones == -1), + # just set it. + # + nzones=$( f_country $tlc nzones ) + if [ $nzones -lt 0 ]; then + real_cont=$( f_country $tlc cont ) + real_continent=$( f_continent $real_cont name ) + name=$( f_country $tlc name ) + filename=$( f_country $tlc filename ) + + if ! f_confirm_zone "$real_continent/$filename"; then + [ $nitems -eq 1 ] && NEED_CONTINENT=1 + NEED_COUNTRY=1 + continue + fi + else + title=$( printf "$msg_country_time_zones" \ + "$( f_country $tlc name )" ) + f_dialog_title "$title" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + prompt="$msg_select_zone" + menu_list=$( f_country $tlc menu_list ) + size=$( eval f_dialog_menu_size \"\$title\" \"\$btitle\" \ + \"\$prompt\" \"\" $menu_list ) + + # + # Launch the zone selection menu + # NOTE: This is as deep as we go + # + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + retval=$? + n=$( f_dialog_menutag ) + + if [ $retval -ne 0 ]; then + [ $nitems -eq 1 ] && NEED_CONTINENT=1 + NEED_COUNTRY=1 + continue + fi + + real_cont=$( f_country $tlc cont_$n ) + real_continent=$( f_continent $real_cont name ) + name=$( f_country $tlc name ) + filename=$( f_country $tlc filename_$n ) + + f_confirm_zone "$real_continent/$filename" || continue + fi + + [ $retval -eq 0 ] || continue # back to main menu + + if ! f_install_zoneinfo "$real_continent/$filename"; then + [ $nzones -lt 0 ] && NEED_COUNTRY=1 + else + break + fi +done + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/ttys/INDEX b/usr.sbin/bsdconfig/ttys/INDEX new file mode 100644 index 0000000000000..4ecc68e24a785 --- /dev/null +++ b/usr.sbin/bsdconfig/ttys/INDEX @@ -0,0 +1,53 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Ttys" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Configure Ttys" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="ttys|ttys" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="ttys" diff --git a/usr.sbin/bsdconfig/ttys/Makefile b/usr.sbin/bsdconfig/ttys/Makefile new file mode 100644 index 0000000000000..b77bc249b3675 --- /dev/null +++ b/usr.sbin/bsdconfig/ttys/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/150.ttys +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= ttys + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/ttys/USAGE b/usr.sbin/bsdconfig/ttys/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/ttys/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/ttys/include/Makefile b/usr.sbin/bsdconfig/ttys/include/Makefile new file mode 100644 index 0000000000000..9333abecd6f3a --- /dev/null +++ b/usr.sbin/bsdconfig/ttys/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/150.ttys/include +FILES= messages.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/ttys/include/messages.subr b/usr.sbin/bsdconfig/ttys/include/messages.subr new file mode 100644 index 0000000000000..296f75602f711 --- /dev/null +++ b/usr.sbin/bsdconfig/ttys/include/messages.subr @@ -0,0 +1,31 @@ +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +msg_configure_ttys="Configure TTYs" +msg_help_text="Configuration of system TTYs requires editing the /etc/ttys file.\nTypical configuration activities might include enabling getty(8)\non the first serial port to allow login via serial console after\nreboot, or to enable xdm. The default ttys file enables normal\nvirtual consoles, and most sites will not need to perform manual\nconfiguration.\n\nTo load /etc/ttys in the editor, select [Yes], otherwise, [No]." +msg_no_such_file_or_directory="%s: %s: No such file or directory" +msg_permission_denied="%s: %s: permission denied" +msg_user_confirmation_requested="User Confirmation Requested" diff --git a/usr.sbin/bsdconfig/ttys/ttys b/usr.sbin/bsdconfig/ttys/ttys new file mode 100755 index 0000000000000..83715ca849bc9 --- /dev/null +++ b/usr.sbin/bsdconfig/ttys/ttys @@ -0,0 +1,128 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="150.ttys" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# +# Default text-editor to use +# +: ${EDITOR:=ee} + + +# +# If X11 is requested, which terminal and what options should we use? +# +X11TERM=xterm +X11TERM_OPTS= + +# +# Location of ttys(5) +# +ETC_TTYS=/etc/ttys + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_configure_ttys" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init +f_dialog_title "$msg_user_confirmation_requested" +f_dialog_yesno "$msg_help_text" || exit $SUCCESS +f_dialog_title_restore + +# +# Make sure $EDITOR exists and is executable +# +case "$EDITOR" in +*/*) + [ -e "$EDITOR" ] || + f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$EDITOR" + [ -x "$EDITOR" ] || + f_die 1 "$msg_permission_denied" "$pgm" "$EDITOR" + ;; +*) + f_have "$EDITOR" || + f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$EDITOR" +esac + +# +# If Xdialog(1) is requested, we'll need to wrap bsdinstall(8) into xterm(1) +# +if [ "$USE_XDIALOG" ]; then + # + # Make sure $X11TERM exists and is executable + # + case "$X11TERM" in + */*) + [ -e "$X11TERM" ] || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "$X11TERM" + [ -x "$X11TERM" ] || f_die 1 \ + "$msg_permission_denied" "$pgm" "$X11TERM" + ;; + *) + f_have "$X11TERM" || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "$X11TERM" + esac + + exec $X11TERM $X11TERM_OPTS -e $EDITOR $ETC_TTYS +else + exec $EDITOR $ETC_TTYS +fi + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/INDEX b/usr.sbin/bsdconfig/usermgmt/INDEX new file mode 100644 index 0000000000000..81e43aa69aa6f --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/INDEX @@ -0,0 +1,60 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +# +# Title that will be shown on the bsdconfig menu +menu_title="Login/Group Management" +# +# a short descriptive line shown at the bottom of the bsdconfig menu. +# keep it short because any line longer than the terminal width will +# be truncated. +menu_help="Manage system user and/or group information" +# +# two-part variable that defines an action to take when 'keyword' +# is passed on a bsdconfig command line. variable takes the form +# "keyword|command" and multiple occurrences of the variable +# (with different 'keyword's, or different 'keyword's AND 'command's) +# are allowed. If 'command' begins with a '/' then the full +# path to the program is needed. If 'command' begins with anything +# else it is a path relative to the directory this INDEX file is in. +# 'keyword' can be i18n'ed but 'command' is the name of a script. +menu_selection="usermgmt|usermgmt" +menu_selection="useradd|useradd" +menu_selection="useredit|useredit" +menu_selection="userdel|userdel" +menu_selection="groupmgmt|usermgmt" +menu_selection="groupadd|groupadd" +menu_selection="groupedit|groupedit" +menu_selection="groupdel|groupdel" +# +# Items below this line do NOT need i18n translation---------------------- +# +# Name of the program to be run when this menu choice is selected. +# if it begins with a '/' then the full path to the program is needed. +# if it begins with anything else it is a path relative to the directory +# this INDEX file is in. +menu_program="usermgmt" diff --git a/usr.sbin/bsdconfig/usermgmt/Makefile b/usr.sbin/bsdconfig/usermgmt/Makefile new file mode 100644 index 0000000000000..7c1b3e50da9bb --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +NO_OBJ= + +SUBDIR= include + +FILESDIR= ${LIBEXECDIR}/bsdconfig/070.usermgmt +FILES= INDEX USAGE + +SCRIPTSDIR= ${FILESDIR} +SCRIPTS= groupadd groupdel groupedit groupinput \ + useradd userdel useredit userinput usermgmt + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/usermgmt/USAGE b/usr.sbin/bsdconfig/usermgmt/USAGE new file mode 100644 index 0000000000000..9aa4dca449286 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/USAGE @@ -0,0 +1,33 @@ +# Copyright (c) 2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +Usage: bsdconfig @PROGRAM_NAME@ [OPTIONS] + +OPTIONS: + -h Print usage statement and exit. + -S Secure X11 mode (implies `-X'). As root, always prompt-for + and validate sudo(8) username/password before starting. + -X Use Xdialog(1) in place of dialog(1). diff --git a/usr.sbin/bsdconfig/usermgmt/groupadd b/usr.sbin/bsdconfig/usermgmt/groupadd new file mode 100755 index 0000000000000..c4f4fde475c1c --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/groupadd @@ -0,0 +1,64 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr + +APP_DIR="070.usermgmt" + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Chain-load to groupinput to centralize code and minimize duplication +# +f_dialog_init +$BSDCFG_LIBE/$APP_DIR/groupinput ${USE_XDIALOG:+-X} mode="Add" + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/groupdel b/usr.sbin/bsdconfig/usermgmt/groupdel new file mode 100755 index 0000000000000..aa5a71dd32e82 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/groupdel @@ -0,0 +1,93 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include $BSDCFG_LIBE/$APP_DIR/include/group_input.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_delete $msg_group" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Loop until the user Exits, Cancels or presses ESC +# +while :; do + f_dialog_menu_group_list + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + + *) # anything else is a group name + $BSDCFG_LIBE/$APP_DIR/groupinput \ + ${USE_XDIALOG:+-X} mode="Delete" group="$mtag" + ;; + + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/groupedit b/usr.sbin/bsdconfig/usermgmt/groupedit new file mode 100755 index 0000000000000..beb11afb0b639 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/groupedit @@ -0,0 +1,93 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include $BSDCFG_LIBE/$APP_DIR/include/group_input.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_edit_view $msg_group" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Loop until the user Exits, Cancels or presses ESC +# +while :; do + f_dialog_menu_group_list + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + + *) # anything else is a group name + $BSDCFG_LIBE/$APP_DIR/groupinput \ + ${USE_XDIALOG:+-X} mode="Edit/View" group="$mtag" + ;; + + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/groupinput b/usr.sbin/bsdconfig/usermgmt/groupinput new file mode 100755 index 0000000000000..96186be258a73 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/groupinput @@ -0,0 +1,295 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include $BSDCFG_LIBE/$APP_DIR/include/group_input.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# set some reasonable defaults if /etc/adduser.conf does not exist. +[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf +: ${passwdtype:="yes"} + +############################################################ FUNCTIONS + +# save_changes +# +# Save any/all settings (actions performed depend on $mode value). +# +save_changes() +{ + local err retval=$SUCCESS + + case "$mode" in + Delete) + err=$( pw groupdel "$group_name" 2>&1 ) + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_error" "$err" + return $retval + fi + f_show_msg "$msg_group_deleted" + ;; + Add) + local cmd="pw groupadd -n '$group_name'" + [ "$group_gid" ] && cmd="$cmd -g '$group_gid'" + [ "$group_members" != "$cur_group_members" ] && + cmd="$cmd -M '$group_members'" + if [ "$pw_group_password_disable" ]; then + cmd="$cmd -h -" + elif [ "$group_password" ]; then + cmd="echo \"\$group_password\" | $cmd -h 0" + fi + f_dprintf "cmd=$cmd" + err=$( eval $cmd 2>&1 ) + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_error" "$err" + return $retval + fi + f_show_msg "$msg_group_added" + ;; + Edit/View) + local cmd="pw groupmod -n '$group_name'" + [ "$group_gid" ] && cmd="$cmd -g '$group_gid'" + [ "$group_members" != "$cur_group_members" ] && + cmd="$cmd -M '$group_members'" + if [ "$pw_group_password_disable" ]; then + cmd="$cmd -h -" + elif [ "$group_password" ]; then + cmd="echo \"\$group_password\" | $cmd -h 0" + fi + f_dprintf "cmd=$cmd" + err=$( eval $cmd 2>&1 ) + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_error" "$err" + return $retval + fi + f_show_msg "$msg_group_updated" + ;; + esac + + save_flag= + return $SUCCESS +} + +# dialog_title_update $mode +# +# Set the title based on the given $mode. +# +dialog_title_update() +{ + local mode="$1" + case "$mode" in + Add) f_dialog_title "$msg_add $msg_group" ;; + Edit/View) f_dialog_title "$msg_edit_view $msg_group: $group" ;; + Delete) f_dialog_title "$msg_delete $msg_group: $group" ;; + esac +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while [ $# -gt 0 ]; do + key="${1%%=*}" + value="${1#*=}" + f_dprintf "key=[$key] value=[$value]" + case "$key" in + mode) mode="$value";; + group) group="$value";; + esac + shift +done +f_dprintf "mode=[$mode] group=[$group]" + +# +# Initialize +# +f_dialog_init +dialog_title_update "$mode" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init +menu_text= +save_flag= +hline="$hline_arrows_tab_enter" + +if [ "$mode" = "Add" ]; then + # + # Ask a series of questions to pre-fill the editor screen. + # + # The defaults used in each dialog should allow the user to simply + # hit ENTER to proceed, because cancelling a single dialog will + # cause them to be returned to the main groupmenu. + # + + f_dialog_input_group_name || exit 0 + [ "$passwdtype" = "yes" ] && + { f_dialog_input_group_password || exit 0; } + f_dialog_input_group_gid || exit 0 + f_dialog_input_group_members || exit 0 +fi + +if [ "$mode" = "Edit/View" -o "$mode" = "Delete" ]; then + f_input_group "$group" || f_die 1 "$msg_group_not_found" +fi + +cur_group_name="$group_name" +cur_group_password="$group_password" +cur_group_gid="$group_gid" +cur_group_members="$group_members" + +[ "$mode" = "Delete" ] && save_flag=1 + +# +# Loop until the user decides to Exit, Cancel, or presses ESC +# +while :; do + dialog_title_update "$mode" + + menu_text= + menu_exit="$msg_exit" + if [ "$save_flag" ]; then + if [ "$mode" = "Delete" ]; then + menu_exit="$msg_delete/$msg_exit" + menu_text="$msg_delete_exit_or_cancel" + else + menu_exit="$msg_save/$msg_exit" + menu_text="$msg_save_exit_or_cancel" + fi + fi + + case "$mode" in + Delete) + menu_items=" + 'X' '$menu_exit' + '1' '$msg_group: $group_name' + '-' '$msg_password: -----' + '-' '$msg_group_id: $group_gid' + '-' '$msg_group_members: $group_members' + " # END-QUOTE + ;; + *) + menu_items=" + 'X' '$menu_exit' + '1' '$msg_group: $group_name' + '2' '$msg_password: -----' + '3' '$msg_group_id: $group_gid' + '4' '$msg_group_members: $group_members' + " # END-QUOTE + esac + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$menu_text\" \ + \"\$hline\" \ + $menu_items ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$menu_text\" $size \ + $menu_items \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + # Exit if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || f_die + + case "$mtag" in + X) # Exit + if [ "$save_flag" ]; then + save_changes || continue + fi + break + ;; + 1) # Group Name + case "$mode" in + Add) f_dialog_input_group_name "$group_name" ;; + Edit/View|Delete) + f_dialog_menu_group_list + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + # Loop if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || continue + + [ "$mtag" = "X $msg_exit" ] && continue + + group="$mtag" + f_input_group "$group" || f_die 1 "$msg_group_not_found" + cur_group_name="$group_name" + cur_group_password="$group_password" + cur_group_gid="$group_gid" + cur_group_members="$group_members" + [ "$mode" != "Delete" ] && save_flag= + esac + ;; + 2) # Password + f_dialog_input_group_password + ;; + 3) # GID + f_dialog_input_group_gid "$group_gid" + ;; + 4) # Users in Group + f_dialog_input_group_members "$group_members" + ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/include/Makefile b/usr.sbin/bsdconfig/usermgmt/include/Makefile new file mode 100644 index 0000000000000..15f88aa2ac2cd --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/include/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${LIBEXECDIR}/bsdconfig/070.usermgmt/include +FILES= group_input.subr messages.subr user_input.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/usermgmt/include/group_input.subr b/usr.sbin/bsdconfig/usermgmt/include/group_input.subr new file mode 100644 index 0000000000000..b4121b1b62903 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/include/group_input.subr @@ -0,0 +1,437 @@ +if [ ! "$_USERMGMT_GROUP_INPUT_SUBR" ]; then _USERMGMT_GROUP_INPUT_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/strings.subr +f_include_lang $BSDCFG_LIBE/include/messages.subr + +APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_input_group $group +# +# Given $group name or id, create the environment variables group_name, +# group_gid, and group_members (and group_password is reset to NULL). +# +f_input_group() +{ + eval $( pw groupshow "$1" | awk -F: ' + { + printf "group_name='\'%s\''\n", $1 + printf "group_password=\n" + printf "group_gid='\'%s\''\n", $3 + printf "group_members='\'%s\''\n", $4 + exit + }' ) +} + +# f_dialog_menu_group_list +# +# Allows the user to select a group from a list. +# +f_dialog_menu_group_list() +{ + local menu_list size + local hline="$hline_alnum_punc_tab_enter" + + menu_list=" + 'X $msg_exit' '' + " # END-QUOTE + + # Add groups from group(5) + menu_list="$menu_list $( pw groupshow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { + printf "'\'%s\'\ \'%s\''\n", $1, $1 + }' + )" + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_input_group_name [$group_name] +# +# Allows the user to enter a new groupname for a given group. If the user does +# not cancel or press ESC, the $group_name variable will hold the +# newly-configured value upon return. +# +# If $cur_group_name is defined, the user can enter that and by-pass error- +# checking (allowing the user to "revert" to an old value without, for example, +# being told that the groupname already exists). +# +f_dialog_input_group_name() +{ + local msg="$( printf "$msg_group" )" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size retval _name="$1" _input="$1" + while :; do + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for no-change + [ "$_input" = "$_name" ] && return $SUCCESS + + # Check for reversion + if [ "$_input" = "$cur_group_name" ]; then + group_name="$cur_group_name" + return $SUCCESS + fi + + # Check for NULL entry + if [ ! "$_input" ]; then + f_show_msg "$msg_group_is_empty" + continue + fi + + # Check for invalid entry + if ! echo "$_input" | grep -q "^[[:alpha:]]"; then + f_show_msg "$msg_group_must_start_with_letter" + continue + fi + + # Check for duplicate entry + if f_quietly pw groupshow -n "$_input"; then + f_show_msg "$msg_group_already_used" "$_input" + continue + fi + + group_name="$_input" + break + done + save_flag=1 + + f_dprintf "group_name: [$cur_group_name]->[$group_name]" + + return $SUCCESS +} + +# f_dialog_input_group_password +# +# Prompt the user to enter a password (twice). +# +f_dialog_input_group_password() +{ + local hline="$hline_alnum_punc_tab_enter" + local msg size rmsg rsize + + msg=$( printf "$msg_group_password" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "" \ + "$hline" ) + + rmsg=$( printf "$msg_reenter_group_password" ) + rsize=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$rmsg" \ + "" \ + "$hline" ) + + # + # Loop until the user provides taint-free/valid input + # + local retval _password1 _password2 + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$msg\" $size \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password1=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$rmsg\" $rsize \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password2=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for password mismatch + if [ "$_password1" != "$_password2" ]; then + f_show_msg "$msg_group_passwords_do_not_match" + continue + fi + + # Check for NULL entry + if [ ! "$_password1" ]; then + f_dialog_yesno \ + "$msg_disable_password_auth_for_group" || + continue + pw_group_password_disable=1 + else + pw_group_password_disable= + fi + + group_password="$_password1" + break + done + save_flag=1 + + f_dprintf "group_password: [$cur_group_password]->[$group_password]" + + return $SUCCESS +} + +# f_dialog_input_group_gid [$group_gid] +# +# Allow the user to enter a new GID for a given group. If the user does not +# cancel or press ESC, the $group_gid variable will hold the newly-configured +# value upon return. +# +f_dialog_input_group_gid() +{ + local msg size retval _input="$1" + local hline="$hline_num_tab_enter" + + msg=$( printf "$msg_group_id_leave_empty_for_default" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + group_gid="$_input" + save_flag=1 + + f_dprintf "group_gid: [$cur_group_gid]->[$group_gid]" + + return $SUCCESS +} + +# f_dialog_input_group_members [$group_members] +# +# Allow the user to modify a list of members for a given group. If the user does +# not cancel or press ESC, the $group_members variable will hold the newly- +# configured value upon return. +# +f_dialog_input_group_members() +{ + local menu_choice msg size retval _input="$1" + local hline="$hline_num_arrows_tab_enter" + local user + local menu_list + local all_users_valid + local _group_members + local checklist_users + + menu_list=" + 'X' '$msg_continue' + '1' '$msg_select_group_members_from_list' + '2' '$msg_enter_group_members_manually' + " # END-QUOTE + + while :; do + msg="$msg_group_members:" + menu_size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $menu_size \ + $menu_list \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + retval=$? + menu_choice=$( f_dialog_menutag ) + f_dprintf "retval=$retval menu_choice=[$menu_choice]" + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + case "$menu_choice" in + X) # Exit + break ;; + 1) # Select Group Members from a list + user_list=$( pw usershow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { printf "%s\n", $1 }' ) + checklist_users= + for user in $user_list; do + checklist_users="$checklist_users $user \"\"" + if echo "$_input" | grep -q "\<$user\>"; then + checklist_users="$checklist_users on" + else + checklist_users="$checklist_users off" + fi + done + + size=$( eval f_dialog_radiolist_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $checklist_users ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --separate-output \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --checklist \"\$msg\" $size \ + $checklist_users \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + _group_members=$( f_dialog_inputstr | tr '\n' ' ' | + sed -e 's/[[:space:]]\{1,\}/,/g;s/^,//;s/,$//' ) + + # Return to previous menu if user has either + # pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$_group_members" + ;; + 2) # Enter Group Members manually + hline="$hline_num_tab_enter" + msg=$( + printf "$msg_group_members ($msg_separated_by_commas)" + ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _group_members=$( f_dialog_inputstr ) + + # Return to previous menu if user has either + # pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$_group_members" + ;; + esac + done + + group_members="$_input" + save_flag=1 + f_dprintf "group_members: [$cur_group_members]->[$group_members]" + + return $SUCCESS +} + +fi # ! $_USERMGMT_GROUP_INPUT_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/include/messages.subr b/usr.sbin/bsdconfig/usermgmt/include/messages.subr new file mode 100644 index 0000000000000..e2e7bf3ec181b --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/include/messages.subr @@ -0,0 +1,111 @@ +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ + +hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER" +hline_alnum_tab_enter="Use alpha-numeric, TAB or ENTER" +hline_arrows_space_tab_enter="Use arrows, SPACE, TAB or ENTER" +hline_arrows_tab_enter="Press arrows, TAB or ENTER" +hline_num_arrows_tab_enter="Use numbers, arrows, TAB or ENTER" +hline_num_tab_enter="Use numbers, TAB or ENTER" +msg_account_expires_in_how_many_days="Account expires in how many days?" +msg_account_expires_on="Account Expires on" +msg_account_does_not_expire="Account does not expire" +msg_add="Add" +msg_add_group="Add Group" +msg_add_login="Add Login" +msg_cancel="Cancel" +msg_continue="Continue" +msg_create_dotfiles="Create Dotfiles" +msg_create_home_directory="Create Home Directory" +msg_delete="Delete" +msg_delete_exit_or_cancel="Choose Delete/Exit when finished or Cancel." +msg_delete_group="Delete Group" +msg_delete_home_directory="Delete Home Directory" +msg_delete_login="Delete Login" +msg_delete_primary_group="Delete Primary Group" +msg_deleting_home_directory="Deleting home directory..." +msg_disable_password_auth_for_account="Disable password authentication for this account?" +msg_disable_password_auth_for_group="Disable password authentication for this group?" +msg_edit_date_time_with_a_calendar="Edit date/time with a calendar" +msg_edit_group="Edit/View Group" +msg_edit_login="Edit/View Login" +msg_edit_view="Edit/View" +msg_enter_group_members_manually="Enter Group Members manually" +msg_enter_number_of_days_into_the_future="Enter number of days into the future" +msg_enter_value_manually="Edit value manually" +msg_error="ERROR!" +msg_exit="Exit" +msg_full_name="Full Name" +msg_group="Group" +msg_group_added="Group Added" +msg_group_already_used="%s: Group is already used." +msg_group_deleted="Group Deleted" +msg_group_is_empty="Group is empty." +msg_group_id="Group ID" +msg_group_id_leave_empty_for_default="Group ID (Leave empty for default)" +msg_group_members="Group Members" +msg_group_must_start_with_letter="Group must start with a letter." +msg_group_not_found="%s: Group not found." +msg_group_password="Group Password" +msg_group_passwords_do_not_match="Group Passwords do not match." +msg_group_updated="Group Updated" +msg_home_directory="Home Directory" +msg_invalid_number_of_days="Invalid number of days." +msg_invalid_number_of_seconds="Invalid number of seconds." +msg_login="Login" +msg_login_added="Login Added" +msg_login_already_used="%s: Login is already used." +msg_login_class="Login Class" +msg_login_deleted="Login Deleted" +msg_login_is_empty="Login is empty." +msg_login_management="Login/Group Management" +msg_login_must_start_with_letter="Login must start with a letter." +msg_login_not_found="Login not found." +msg_login_updated="Login Updated" +msg_member_of_groups="Member of Groups" +msg_n_a="N/A" +msg_number_of_seconds_since_epoch="Number of seconds since the Epoch\n(1 = %s)\nNULL or zero to disable:" +msg_no="No" +msg_ok="OK" +msg_password="Password" +msg_password_expires_in_how_many_days="Password expires in how many days?" +msg_password_expires_on="Password Expires on" +msg_passwords_do_not_match="Passwords do not match." +msg_password_does_not_expire="Password does not expire" +msg_reenter_group_password="Re-enter Group Password" +msg_reenter_password="Re-enter Password" +msg_save="Save" +msg_save_exit_or_cancel="Choose Save/Exit when finished or Cancel." +msg_separated_by_commas="Separated by commas" +msg_select_group_members_from_list="Select Group Members from a list" +msg_select_login_shell="Select Login Shell" +msg_shell="Shell" +msg_user="User" +msg_user_id="UID" +msg_user_id_leave_empty_for_default="UID (Leave empty for default)" +msg_warning="WARNING!" +msg_yes="Yes" diff --git a/usr.sbin/bsdconfig/usermgmt/include/user_input.subr b/usr.sbin/bsdconfig/usermgmt/include/user_input.subr new file mode 100644 index 0000000000000..a522f9d9e53fc --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/include/user_input.subr @@ -0,0 +1,1180 @@ +if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_INPUT_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/strings.subr +f_include_lang $BSDCFG_LIBE/include/messages.subr + +APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Default location of shells(5) +# +: ${ETC_SHELLS:=/etc/shells} + +############################################################ FUNCTIONS + +# f_get_member_groups $user +# +# Get a list of additional groups $user is a member of in group(5). +# +f_get_member_groups() +{ + echo $( pw groupshow -a | awk -F: "/[:,]$1(,|\$)/{print \$1}" ) +} + +# f_input_user $user +# +# Given $user name or id, create the environment variables pw_name, pw_uid, +# pw_gid, pw_class, pw_password_expire, pw_account_expire, pw_gecos, +# pw_home_dir, pw_shell, and pw_member_groups (and pw_password is reset to +# NULL). +# +f_input_user() +{ + local user="$1" + eval $( pw usershow "$user" | awk -F: ' + { + printf "pw_name='\'%s\''\n", $1 + printf "pw_password=\n" + printf "pw_uid='\'%s\''\n", $3 + printf "pw_gid='\'%s\''\n", $4 + printf "pw_class='\'%s\''\n", $5 + printf "pw_password_expire='\'%s\''\n", $6 + printf "pw_account_expire='\'%s\''\n", $7 + printf "pw_gecos='\'%s\''\n", $8 + printf "pw_home_dir='\'%s\''\n", $9 + printf "pw_shell='\'%s\''\n", $10 + }' ) + pw_member_groups=$( f_get_member_groups "$user" ) +} + +# f_dialog_menu_user_list +# +# Allows the user to select a login from a list. +# +f_dialog_menu_user_list() +{ + local menu_list size + local hline="$hline_alnum_punc_tab_enter" + + menu_list=" + 'X $msg_exit' '' + " # END-QUOTE + + # Add users from passwd(5) + menu_list="$menu_list $( pw usershow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { + printf "'\'%s\'\ \'%s\''\n", $1, $8 + }' + )" + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_input_member_groups [$member_groups] +# +# Allows the user to edit group memberships for a given user. If the user does +# not cancel or press ESC, the $pw_member_groups variable will hold the newly- +# configured value upon return. +# +f_dialog_input_member_groups() +{ + local msg="$( printf "$msg_member_of_groups" )" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size retval all_groups checklist_groups="" _member_groups="$1" + all_groups=$( pw groupshow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { + printf "%s\n", $1 + }' + ) + for grp in $all_groups; do + checklist_groups="$checklist_groups $grp $grp" + if echo "$_member_groups" | grep -q "\<$grp\>"; then + checklist_groups="$checklist_groups on" + else + checklist_groups="$checklist_groups off" + fi + done + + while :; do + size=$( eval f_dialog_radiolist_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $checklist_groups ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --separate-output \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --checklist \"\$msg\" $size \ + $checklist_groups \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _member_groups=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # + # Validate each of the groups the user has entered + # + local group all_groups_valid=1 + for group in $_member_groups; do + if ! f_quietly pw groupshow -n "$group"; then + f_show_msg "$msg_group_not_found" "$group" + all_groups_valid= + break + fi + done + [ "$all_groups_valid" ] || continue + + pw_member_groups="$_member_groups" + break + done + save_flag=1 + + local debug="pw_member_groups:" + f_dprintf "$debug [$cur_pw_member_groups]->[$pw_member_groups]" + + return $SUCCESS +} + +# f_dialog_input_name [$name] +# +# Allows the user to enter a new username for a given user. If the user does +# not cancel or press ESC, the $pw_name variable will hold the newly-configured +# value upon return. +# +# If $cur_pw_name is defined, the user can enter that and by-pass error- +# checking (allowing the user to "revert" to an old value without, for example, +# being told that the username already exists). +# +f_dialog_input_name() +{ + local msg="$( printf "$msg_login" )" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size retval _name="$1" _input="$1" + while :; do + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for no-change + [ "$_input" = "$_name" ] && return $SUCCESS + + # Check for reversion + if [ "$_input" = "$cur_pw_name" ]; then + pw_name="$cur_pw_name" + return $SUCCESS + fi + + # Check for NULL entry + if [ ! "$_input" ]; then + f_show_msg "$msg_login_is_empty" + continue + fi + + # Check for invalid entry + if ! echo "$_input" | grep -q "^[[:alpha:]]"; then + f_show_msg "$msg_login_must_start_with_letter" + continue + fi + + # Check for duplicate entry + if f_quietly pw usershow -n "$_input"; then + f_show_msg "$msg_login_already_used" "$_input" + continue + fi + + pw_name="$_input" + break + done + save_flag=1 + + f_dprintf "pw_name: [$cur_pw_name]->[$pw_name]" + + return $SUCCESS +} + +# f_dialog_input_password +# +# Prompt the user to enter a password (twice). +# +f_dialog_input_password() +{ + local hline="$hline_alnum_punc_tab_enter" + local msg size rmsg rsize + + msg=$( printf "$msg_password" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "" \ + "$hline" ) + + rmsg=$( printf "$msg_reenter_password" ) + rsize=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$rmsg" \ + "" \ + "$hline" ) + + # + # Loop until the user provides taint-free/valid input + # + local retval _password1 _password2 + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$msg\" $size \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password1=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$rmsg\" $rsize \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password2=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for password mismatch + if [ "$_password1" != "$_password2" ]; then + f_show_msg "$msg_passwords_do_not_match" + continue + fi + + # Check for NULL entry + if [ ! "$_password1" ]; then + f_dialog_yesno \ + "$msg_disable_password_auth_for_account" || + continue + pw_password_disable=1 + else + pw_password_disable= + fi + + pw_password="$_password1" + break + done + save_flag=1 + + f_dprintf "pw_password: [$cur_pw_password]->[$pw_password]" + + return $SUCCESS +} + +# f_dialog_input_gecos [$gecos] +# +# Allow the user to enter new GECOS information for a given user. This +# information is commonly used to store the ``Full Name'' of the user. If the +# user does not cancel or press ESC, the $pw_gecos variable will hold the +# newly-configured value upon return. +# +f_dialog_input_gecos() +{ + local msg size retval _input="$1" + local hline="$hline_alnum_punc_tab_enter" + + msg=$( printf "$msg_full_name" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_gecos="$_input" + save_flag=1 + + f_dprintf "pw_gecos: [$cur_pw_gecos]->[$pw_gecos]" + + return $SUCCESS +} + +# f_dialog_input_uid [$uid] +# +# Allow the user to enter a new UID for a given user. If the user does not +# cancel or press ESC, the $pw_uid variable will hold the newly-configured +# value upon return. +# +f_dialog_input_uid() +{ + local msg size retval _input="$1" + local hline="$hline_num_tab_enter" + + msg=$( printf "$msg_user_id_leave_empty_for_default" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_uid="$_input" + save_flag=1 + + f_dprintf "pw_uid: [$cur_pw_uid]->[$pw_uid]" + + return $SUCCESS +} + +# f_dialog_input_gid [$gid] +# +# Allow the user to enter a new primary GID for a given user. If the user does +# not cancel or press ESC, the $pw_gid variable will hold the newly-configured +# value upon return. +# +f_dialog_input_gid() +{ + local msg size retval _input="$1" + local hline="$hline_num_tab_enter" + + msg=$( printf "$msg_group_id_leave_empty_for_default" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_gid="$_input" + save_flag=1 + + f_dprintf "pw_gid: [$cur_pw_gid]->[$pw_gid]" + + return $SUCCESS +} + +# f_dialog_input_class [$class] +# +# Allow the user to enter a new login class for a given user. If the user does +# not cancel or press ESC, the $pw_class variable will hold the newly- +# configured value upon return. +# +f_dialog_input_class() +{ + local msg size retval _input="$1" + local hline="$hline_alnum_tab_enter" + + msg=$( printf "$msg_login_class" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_class="$_input" + save_flag=1 + + f_dprintf "pw_class: [$cur_pw_class]->[$pw_class]" + + return $SUCCESS +} + +# f_dialog_input_change [$seconds] +# +# Allow the user to enter a date/time (in number-of-seconds since the `epoch') +# for when a given user's password must be changed. If the user does not cancel +# or press ESC, the $pw_password_expire variable will hold the newly- +# configured value upon return. +# +f_dialog_input_change() +{ + local calendar_size timebox_size + local msg menu_size size retval _input="$1" + local hline="$hline_num_arrows_tab_enter" + + local menu_list=" + '1' '$msg_password_does_not_expire' + '2' '$msg_edit_date_time_with_a_calendar' + '3' '$msg_enter_number_of_days_into_the_future' + '4' '$msg_enter_value_manually' + " # END-QUOTE + + # + # Loop until the user provides taint-free/cancellation-free input + # + while :; do + msg="$msg_password_expires_on" + menu_size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $menu_size \ + $menu_list \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + retval=$? + date_type=$( f_dialog_menutag ) + f_dprintf "retval=$retval date_type=[$date_type]" + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + case "$date_type" in + 1) # Password does not expire + _input="" + break ;; + + 2) # Edit date/time with a calendar + local _input_date _input_time ret_date ret_time + + local secs="$_input" + { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= + _input_date=$( date -j -f "%s" -- "$secs" \ + "+%d %m %Y" 2> /dev/null ) + calendar_size=$( f_dialog_calendar_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --calendar \"\$msg\" $calendar_size \ + $_input_date \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_date=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_date=[$ret_date]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input_time= + [ "$secs" ] && _input_time=$( date -j \ + -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) + timebox_size=$( f_dialog_timebox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --timebox \"\$msg\" $timebox_size \ + $_input_time \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_time=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_time=[$ret_time]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input=$( date \ + -j -f "%d/%m/%Y %T" \ + -- "$ret_date $ret_time" \ + +%s 2> /dev/null ) + f_dprintf "_input=[$_input]" + break ;; + + 3) # Enter number of days into the future + local ret_days seconds="$( date +%s )" + + f_isinteger "$_input" || _input=0 + [ $_input -gt 0 -a $_input -gt $seconds ] && + ret_days=$(( ( $_input - $seconds ) / 86400 )) + f_isinteger "$ret_days" && + ret_days=$(( $ret_days + 1 )) + + msg="$msg_password_expires_in_how_many_days" + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$ret_days" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$ret_days\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_days=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + # Taint-check the user's input + if ! f_isinteger "$ret_days"; then + f_show_msg "$msg_invalid_number_of_days" + continue + fi + + f_dprintf "ret_days=[$ret_days]" + case "$ret_days" in + [-+]*) _input=$( date -v${ret_days}d +%s );; + 0) _input=$( date +%s );; + *) _input=$( date -v+${ret_days}d +%s );; + esac + f_dprintf "_input=[$_input]" + break ;; + + 4) # Enter value manually + local ret_secs + + msg=$( printf "$msg_number_of_seconds_since_epoch" \ + "$( date -r 1 "+%c %Z" )" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + ret_secs=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$ret_secs" + + # Taint-check the user's input + if ! f_isinteger "${_input:-0}"; then + f_show_msg "$msg_invalid_number_of_seconds" + continue + fi + + f_dprintf "_input=[$_input]" + break ;; + + esac + + done # Loop forever + + pw_password_expire="$_input" + save_flag=1 + + local debug=pw_password_expire + f_dprintf "$debug: [$cur_pw_password_expire]->[$pw_password_expire]" + + return $SUCCESS +} + +# f_dialog_input_expire [$seconds] +# +# Allow the user to enter a date/time (in number-of-seconds since the `epoch') +# for when a given user's account should become expired. If the user does not +# cancel or press ESC, the $pw_account_expire variable will hold the newly- +# configured value upon return. +# +f_dialog_input_expire() +{ + local calendar_size timebox_size + local msg menu_size size retval _input="$1" + local hline="$hline_num_arrows_tab_enter" + + local menu_list=" + '1' '$msg_account_does_not_expire' + '2' '$msg_edit_date_time_with_a_calendar' + '3' '$msg_enter_number_of_days_into_the_future' + '4' '$msg_enter_value_manually' + " # END-QUOTE + + # + # Loop until the user provides taint-free/cancellation-free input + # + while :; do + msg="$msg_account_expires_on" + menu_size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $menu_size \ + $menu_list \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + retval=$? + date_type=$( f_dialog_menutag ) + f_dprintf "retval=$retval date_type=[$date_type]" + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + case "$date_type" in + 1) # Account does not expire + _input="" + break ;; + + 2) # Edit date/time with a calendar + local _input_date _input_time ret_date ret_time + + local secs="$_input" + { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= + _input_date=$( date -j -f "%s" -- "$secs" \ + "+%d %m %Y" 2> /dev/null ) + calendar_size=$( f_dialog_calendar_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --calendar \"\$msg\" $calendar_size \ + $_input_date \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_date=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_date=[$ret_date]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input_time= + [ "$secs" ] && _input_time=$( date -j \ + -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) + timebox_size=$( f_dialog_timebox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --timebox \"\$msg\" $timebox_size \ + $_input_time \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_time=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_time=[$ret_time]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input=$( date \ + -j -f "%d/%m/%Y %T" \ + -- "$ret_date $ret_time" \ + +%s 2> /dev/null ) + f_dprintf "_input=[$_input]" + break ;; + + 3) # Enter number of days into the future + local ret_days seconds="$( date +%s )" + + f_isinteger "$_input" || _input=0 + [ $_input -gt 0 -a $_input -gt $seconds ] && + ret_days=$(( ( $_input - $seconds ) / 86400 )) + f_isinteger "$ret_days" && + ret_days=$(( $ret_days + 1 )) + + msg="$msg_account_expires_in_how_many_days" + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$ret_days" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$ret_days\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_days=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + # Taint-check the user's input + if ! f_isinteger "$ret_days"; then + f_show_msg "$msg_invalid_number_of_days" + continue + fi + + f_dprintf "ret_days=[$ret_days]" + case "$ret_days" in + [-+]*) _input=$( date -v${ret_days}d +%s );; + 0) _input=$( date +%s );; + *) _input=$( date -v+${ret_days}d +%s );; + esac + f_dprintf "_input=[$_input]" + break ;; + + 4) # Enter value manually + local ret_secs + + msg=$( printf "$msg_number_of_seconds_since_epoch" \ + "$( date -r 1 "+%c %Z" )" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + ret_secs=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$ret_secs" + + # Taint-check the user's input + if ! f_isinteger "${_input:-0}"; then + f_show_msg "$msg_invalid_number_of_seconds" + continue + fi + + f_dprintf "_input=[$_input]" + break ;; + + esac + + done # Loop forever + + pw_account_expire="$_input" + save_flag=1 + + local debug=pw_account_expire + f_dprintf "$debug: [$cur_pw_account_expire]->[$pw_account_expire]" + + return $SUCCESS +} + +# f_dialog_input_home_dir [$home_dir] +# +# Allow the user to enter a new home directory for a given user. If the user +# does not cancel or press ESC, the $pw_home_dir variable will hold the newly- +# configured value upon return. +# +f_dialog_input_home_dir() +{ + local msg size retval _input="$1" + local hline="$hline_alnum_punc_tab_enter" + + msg=$( printf "$msg_home_directory" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_home_dir="$_input" + save_flag=1 + + f_dprintf "pw_home_dir: [$cur_pw_home_dir]->[$pw_home_dir]" + + return $SUCCESS +} + +# f_dialog_input_home_create +# +# Prompt the user to confirm creation of a given user's home directory. If the +# user does not cancel (by choosing "No") or press ESC, the $pw_home_create +# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return +# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. +# +f_dialog_input_home_create() +{ + local retval + + f_dialog_yesno "$msg_create_home_directory" + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_home_create="$msg_yes" + else + pw_home_create="$msg_no" + fi + save_flag=1 + + f_dprintf "pw_home_create: [$cur_pw_home_create]->[$pw_home_create]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_group_delete +# +# Prompt the user to confirm deletion of a given user's primary group. If the +# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete +# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return +# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. +# +f_dialog_input_group_delete() +{ + local retval + + if f_isinteger "$pw_gid"; then + if [ $pw_gid -lt 1000 ]; then + f_dialog_noyes "$msg_delete_primary_group" + else + f_dialog_yesno "$msg_delete_primary_group" + fi + elif [ "$pw_gid" ]; then + local gid=0 + gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' ) + if f_isinteger "$gid" && [ $gid -lt 1000 ]; then + f_dialog_noyes "$msg_delete_primary_group" + else + f_dialog_yesno "$msg_delete_primary_group" + fi + else + f_dialog_yesno "$msg_delete_primary_group" + fi + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_group_delete="$msg_yes" + else + pw_group_delete="$msg_no" + fi + save_flag=1 + + f_dprintf "pw_group_delete: [$cur_pw_group_delete]->[$pw_group_delete]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_home_delete +# +# Prompt the user to confirm deletion of a given user's home directory. If the +# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete +# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return +# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. +# +f_dialog_input_home_delete() +{ + local retval + + f_dialog_yesno "$msg_delete_home_directory" + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_home_delete="$msg_yes" + else + pw_home_delete="$msg_no" + fi + save_flag=1 + + f_dprintf "pw_home_delete: [$cur_pw_home_delete]->[$pw_home_delete]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_dotfiles_create +# +# Prompt the user to confirm population of a given user's home directory with +# sample dotfiles. If the user does not cancel (by choosing "No") or press ESC, +# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise +# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to +# be i18n-compatible. +# +f_dialog_input_dotfiles_create() +{ + local retval + + f_dialog_yesno "$msg_create_dotfiles" + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_dotfiles_create="$msg_yes" + else + pw_dotfiles_create="$msg_no" + fi + save_flag=1 + + local debug="pw_dotfiles_create:" + f_dprintf "$debug: [$cur_pw_dotfiles_create]->[$pw_dotfiles_create]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_shell [$shell] +# +# Allow the user to select a new login shell for a given user. If the user does +# not cancel or press ESC, the $pw_home_dir variable will hold the newly- +# configured value upon return. +# +# +f_dialog_input_shell() +{ + local size retval shells shell_list _input="$1" + local hline="$hline_arrows_space_tab_enter" + local prompt="$msg_select_login_shell" + + shells=$( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" ) + shell_list=$( + for shell in $shells; do + if [ "$shell" = "$_input" ]; then + echo "'$shell' '' 'on'" + else + echo "'$shell' '' 'off'" + fi + done + ) + + size=$( eval f_dialog_radiolist_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $shell_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --radiolist \"\$prompt\" $size \ + $shell_list \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_shell="$_input" + save_flag=1 + + f_dprintf "pw_shell: [$cur_pw_shell]->[$pw_shell]" + + return $SUCCESS +} + +fi # ! $_USERMGMT_USER_INPUT_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/useradd b/usr.sbin/bsdconfig/usermgmt/useradd new file mode 100755 index 0000000000000..8dc1934d077cd --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/useradd @@ -0,0 +1,64 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr + +APP_DIR="070.usermgmt" + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Chain-load to userinput to centralize code and minimize duplication +# +f_dialog_init +$BSDCFG_LIBE/$APP_DIR/userinput ${USE_XDIALOG:+-X} mode="Add" + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/userdel b/usr.sbin/bsdconfig/usermgmt/userdel new file mode 100755 index 0000000000000..49a54c4de5182 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/userdel @@ -0,0 +1,93 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include $BSDCFG_LIBE/$APP_DIR/include/user_input.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_delete $msg_login" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Loop until the user Exits, Cancels or presses ESC +# +while :; do + f_dialog_menu_user_list + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + + *) # anything else is a userid + $BSDCFG_LIBE/$APP_DIR/userinput \ + ${USE_XDIALOG:+-X} mode="Delete" user="$mtag" + ;; + + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/useredit b/usr.sbin/bsdconfig/usermgmt/useredit new file mode 100755 index 0000000000000..3109d36ae5097 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/useredit @@ -0,0 +1,93 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include $BSDCFG_LIBE/$APP_DIR/include/user_input.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_edit_view $msg_login" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Loop until the user Exits, Cancels or presses ESC +# +while :; do + f_dialog_menu_user_list + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + "X $msg_exit") break ;; + + *) # anything else is a userid + $BSDCFG_LIBE/$APP_DIR/userinput \ + ${USE_XDIALOG:+-X} mode="Edit/View" user="$mtag" + ;; + + esac +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/userinput b/usr.sbin/bsdconfig/usermgmt/userinput new file mode 100755 index 0000000000000..43a750e3857f7 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/userinput @@ -0,0 +1,515 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include $BSDCFG_LIBE/$APP_DIR/include/user_input.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ CONFIGURATION + +# set some reasonable defaults if /etc/adduser.conf does not exist. +[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf +: ${passwdtype:="yes"} +: ${homeprefix:="/home"} +: ${defaultshell:="/bin/sh"} +: ${udotdir:="/usr/share/skel"} + +############################################################ FUNCTIONS + +# copy_dotfiles +# +# Copy `skel' dot-files to a new home directory. +# +copy_dotfiles() +{ + ( # Operate within sub-shell to protect CWD/glob of parent + cd "$udotdir" || exit $? + set +f # glob + for file in dot.*; do + cp -n "$file" "$pw_home_dir/${file#dot}" || exit $? + done + ) +} + +# save_changes +# +# Save any/all settings (actions performed depend on $mode value). +# +save_changes() +{ + local err retval=$SUCCESS + + case "$mode" in + Delete) + err=$( pw userdel -u "$pw_uid" 2>&1 ) + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_error" "$err" + return $retval + fi + f_show_msg "$msg_login_deleted" + + if [ "$pw_group_delete" = "$msg_yes" ] && + f_quietly pw groupshow -g "$pw_gid" + then + err=$( pw groupdel -g "$pw_gid" 2>&1 ) || + f_show_msg "%s %s\n" "$msg_warning" "$err" + fi + + if [ "$pw_home_delete" = "$msg_yes" ]; then + f_dialog_info "$msg_deleting_home_directory" + err=$( rm -Rf "$pw_home_dir" 2>&1 ) || + f_show_msg "%s %s\n" "$msg_warning" "$err" + fi + ;; + Add) + local cmd="pw useradd -n '$pw_name'" + [ "$pw_member_groups" ] && cmd="$cmd -G '$pw_member_groups'" + [ "$pw_class" ] && cmd="$cmd -L '$pw_class'" + [ "$pw_gecos" ] && cmd="$cmd -c '$pw_gecos'" + [ "$pw_home_dir" ] && cmd="$cmd -d '$pw_home_dir'" + [ "$pw_account_expire" ] && cmd="$cmd -e '$pw_account_expire'" + [ "$pw_gid" ] && cmd="$cmd -g '$pw_gid'" + [ "$pw_password_expire" ] && cmd="$cmd -p '$pw_password_expire'" + [ "$pw_shell" ] && cmd="$cmd -s '$pw_shell'" + [ "$pw_uid" ] && cmd="$cmd -u '$pw_uid'" + if [ "$pw_password_disable" ]; then + cmd="$cmd -h -" + elif [ "$pw_password" ]; then + cmd="echo \"\$pw_password\" | $cmd -h 0" + fi + f_dprintf "cmd=$cmd" + err=$( eval $cmd 2>&1 ) + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_error" "$err" + return $retval + fi + f_show_msg "$msg_login_added" + + if [ "$pw_home_create" = "$msg_yes" ]; then + err=$( mkdir -p "$pw_home_dir" 2>&1 ) + if [ $? -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_warning" "$err" + elif [ -e "$pw_home_dir" ]; then + err=$( chown -R "$pw_uid:$pw_gid" \ + "$pw_home_dir" 2>&1 ) + [ $? -eq $SUCCESS ] || f_show_msg \ + "%s %s\n" "$msg_warning" "$err" + fi + fi + + if [ "$pw_dotfiles_create" = "$msg_yes" ]; then + err=$( copy_dotfiles 2>&1 ) || + f_show_msg "%s %s\n" "$msg_warning" "$err" + fi + + user="$pw_name" + f_quietly pw usershow -n "$pw_name" && + mode="Edit/View" # Change mode + ;; + Edit/View) + local cmd="pw usermod -n '$pw_name'" + [ "$pw_member_groups" ] && cmd="$cmd -G '$pw_member_groups'" + [ "$pw_class" ] && cmd="$cmd -L '$pw_class'" + [ "$pw_gecos" ] && cmd="$cmd -c '$pw_gecos'" + [ "$pw_home_dir" ] && cmd="$cmd -d '$pw_home_dir'" + [ "$pw_account_expire" ] && cmd="$cmd -e '$pw_account_expire'" + [ "$pw_gid" ] && cmd="$cmd -g '$pw_gid'" + [ "$pw_password_expire" ] && cmd="$cmd -p '$pw_password_expire'" + [ "$pw_shell" ] && cmd="$cmd -s '$pw_shell'" + [ "$pw_uid" ] && cmd="$cmd -u '$pw_uid'" + if [ "$pw_password_disable" ]; then + cmd="$cmd -h -" + elif [ "$pw_password" ]; then + cmd="echo \"\$pw_password\" | $cmd -h 0" + fi + f_dprintf "cmd=$cmd" + err=$( eval $cmd 2>&1 ) + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_error" "$err" + return $retval + fi + f_show_msg "$msg_login_updated" + + if [ "$pw_home_create" = "$msg_yes" ]; then + err=$( mkdir -p "$pw_home_dir" ) + if [ $? -ne $SUCCESS ]; then + f_show_msg "%s %s\n" "$msg_warning" "$err" + elif [ -e "$pw_home_dir" ]; then + err=$( chown -R "$pw_uid:$pw_gid" \ + "$pw_home_dir" 2>&1 ) + [ $? -eq $SUCCESS ] || f_show_msg \ + "%s %s\n" "$msg_warning" "$err" + fi + fi + + if [ "$pw_dotfiles_create" = "$msg_yes" ]; then + err=$( copy_dotfiles 2>&1 ) || + f_show_msg "%s %s\n" "$msg_warning" "$err" + fi + ;; + esac + + save_flag= + return $SUCCESS +} + +# dialog_title_update $mode +# +# Set the title based on the given $mode. +# +dialog_title_update() +{ + local mode="$1" + case "$mode" in + Add) f_dialog_title "$msg_add $msg_user" ;; + Edit/View) f_dialog_title "$msg_edit_view $msg_user: $user" ;; + Delete) f_dialog_title "$msg_delete $msg_user: $user" ;; + esac +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while [ $# -gt 0 ]; do + key="${1%%=*}" + value="${1#*=}" + f_dprintf "key=[$key] value=[$value]" + case "$key" in + mode) mode="$value";; + user) user="$value";; + esac + shift +done +f_dprintf "mode=[$mode] user=[$user]" + +# +# Initialize +# +f_dialog_init +dialog_title_update "$mode" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init +menu_text= +save_flag= +hline="$hline_arrows_tab_enter" + +if [ "$mode" = "Add" ]; then + # + # Ask a series of questions to pre-fill the editor screen. + # + # The defaults used in each dialog should allow the user to simply + # hit ENTER to proceed, because cancelling a single dialog will + # cause them to be returned to the main usermenu. + # + + f_dialog_input_name || exit 0 + f_dialog_input_gecos "$pw_name" || exit 0 + [ "$passwdtype" = "yes" ] && + { f_dialog_input_password || exit 0; } + f_dialog_input_uid || exit 0 + f_dialog_input_gid || exit 0 + f_dialog_input_member_groups || exit 0 + f_dialog_input_class || exit 0 + f_dialog_input_change || exit 0 + f_dialog_input_expire || exit 0 + f_dialog_input_home_dir "$homeprefix/$pw_name" || exit 0 + pw_dotfiles_create="$msg_no" + if [ ! -d "$homeprefix/$pw_name" ]; then + f_dialog_input_home_create || exit 0 + [ "$pw_home_create" = "$msg_yes" ] && + { f_dialog_input_dotfiles_create || exit 0; } + fi + f_dialog_input_shell "$defaultshell" || exit 0 +fi + +if [ "$mode" = "Edit/View" -o "$mode" = "Delete" ]; then + f_input_user "$user" || f_die 1 "$msg_login_not_found" +fi + +if [ "$mode" = "Edit/View" ]; then + [ -d "$pw_home_dir" ] || pw_home_create="$msg_no" + pw_dotfiles_create="$msg_no" +fi + +if [ "$mode" = "Delete" ]; then + f_dialog_input_group_delete || exit 0 + pw_home_delete="$msg_no" + [ -d "$pw_home_dir" ] && + { f_dialog_input_home_delete || exit 0; } +fi + +cur_pw_name="$pw_name" +cur_pw_password="$pw_password" +cur_pw_uid="$pw_uid" +cur_pw_gid="$pw_gid" +cur_pw_member_groups="$pw_member_groups" +cur_pw_class="$pw_class" +cur_pw_password_expire="$pw_password_expire" +cur_pw_account_expire="$pw_account_expire" +cur_pw_gecos="$pw_gecos" +cur_pw_home_dir="$pw_home_dir" +cur_pw_shell="$pw_shell" +cur_pw_group_delete="$pw_group_delete" +cur_pw_home_create="$pw_home_create" +cur_pw_home_delete="$pw_home_delete" +cur_pw_dotfiles_create="$pw_dotfiles_create" + +[ "$mode" = "Delete" ] && save_flag=1 + +# +# Loop until the user decides to Exit, Cancel, or presses ESC +# +while :; do + dialog_title_update "$mode" + + menu_text= + menu_exit="$msg_exit" + if [ "$save_flag" ]; then + if [ "$mode" = "Delete" ]; then + menu_exit="$msg_delete/$msg_exit" + menu_text="$msg_delete_exit_or_cancel" + else + menu_exit="$msg_save/$msg_exit" + menu_text="$msg_save_exit_or_cancel" + fi + fi + + pw_password_expires_on="$pw_password_expire" + f_isinteger "$pw_password_expire" && [ $pw_password_expire -ne 0 ] && + pw_password_expires_on=$( + date -r "$pw_password_expire" "+%F %T %Z" + ) + pw_account_expires_on="$pw_account_expire" + f_isinteger "$pw_account_expire" && [ "$pw_account_expire" -ne 0 ] && + pw_account_expires_on=$( + date -r "$pw_account_expire" "+%F %T %Z" + ) + + case "$mode" in + Delete) + menu_items=" + 'X' '$menu_exit' + '1' '$msg_login: $pw_name' + '-' '$msg_full_name: $pw_gecos' + '-' '$msg_password: -----' + '-' '$msg_user_id: $pw_uid' + '-' '$msg_group_id: $pw_gid' + '-' '$msg_member_of_groups: $pw_member_groups' + '-' '$msg_login_class: $pw_class' + '-' '$msg_password_expires_on: $pw_password_expires_on' + '-' '$msg_account_expires_on: $pw_account_expires_on' + '-' '$msg_home_directory: $pw_home_dir' + '-' '$msg_shell: $pw_shell' + " # END-QUOTE + ;; + *) + menu_items=" + 'X' '$menu_exit' + '1' '$msg_login: $pw_name' + '2' '$msg_full_name: $pw_gecos' + '3' '$msg_password: -----' + '4' '$msg_user_id: $pw_uid' + '5' '$msg_group_id: $pw_gid' + '6' '$msg_member_of_groups: $pw_member_groups' + '7' '$msg_login_class: $pw_class' + '8' '$msg_password_expires_on: $pw_password_expires_on' + '9' '$msg_account_expires_on: $pw_account_expires_on' + 'A' '$msg_home_directory: $pw_home_dir' + 'B' '$msg_shell: $pw_shell' + " # END-QUOTE + esac + + case "$mode" in + Add|Edit/View) + if [ -d "$pw_home_dir" ]; then menu_items="$menu_items + '-' '$msg_create_home_directory: $msg_n_a' + 'D' '$msg_create_dotfiles: $pw_dotfiles_create' + "; else menu_items="$menu_items + 'C' '$msg_create_home_directory: $pw_home_create' + 'D' '$msg_create_dotfiles: $pw_dotfiles_create' + "; fi + ;; + Delete) + if [ -d "$pw_home_dir" ]; then menu_items="$menu_items + 'C' '$msg_delete_primary_group: $pw_group_delete' + 'D' '$msg_delete_home_directory: $pw_home_delete' + "; else menu_items="$menu_items + 'C' '$msg_delete_primary_group: $pw_group_delete' + '-' '$msg_delete_home_directory: $msg_n_a' + "; fi + ;; + esac + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$menu_text\" \ + \"\$hline\" \ + $menu_items ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$menu_text\" $size \ + $menu_items \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + # Exit if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || f_die + + case "$mtag" in + X) # Exit + if [ "$save_flag" ]; then + save_changes || continue + fi + break + ;; + 1) # Login + case "$mode" in + Add) f_dialog_input_name "$pw_name" ;; + Edit/View|Delete) + f_dialog_menu_user_list + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + # Loop if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || continue + + [ "$mtag" = "X $msg_exit" ] && continue + + user="$mtag" + f_input_user "$user" || f_die 1 "$msg_login_not_found" + cur_pw_name="$pw_name" + cur_pw_password="$pw_password" + cur_pw_uid="$pw_uid" + cur_pw_gid="$pw_gid" + cur_pw_member_groups="$pw_member_groups" + cur_pw_class="$pw_class" + cur_pw_password_expire="$pw_password_expire" + cur_pw_account_expire="$pw_account_expire" + cur_pw_gecos="$pw_gecos" + cur_pw_home_dir="$pw_home_dir" + cur_pw_shell="$pw_shell" + cur_pw_group_delete="$pw_group_delete" + cur_pw_home_create="$pw_home_create" + cur_pw_home_delete="$pw_home_delete" + cur_pw_dotfiles_create="$pw_dotfiles_create" + [ "$mode" != "Delete" ] && save_flag= + esac + ;; + 2) # Full Name + f_dialog_input_gecos "$pw_gecos" + ;; + 3) # Password + f_dialog_input_password + ;; + 4) # UID + f_dialog_input_uid "$pw_uid" + ;; + 5) # Default Group + f_dialog_input_gid "$pw_gid" + ;; + 6) # Member of Groups + f_dialog_input_member_groups "$pw_member_groups" + ;; + 7) # Login Class + f_dialog_input_class "$pw_class" + ;; + 8) # Password Expire on + f_dialog_input_change "$pw_password_expire" + ;; + 9) # Account Expire on + f_dialog_input_expire "$pw_account_expire" + ;; + A) # Home Directory + f_dialog_input_home_dir "$pw_home_dir" + ;; + B) # Shell + f_dialog_input_shell "$pw_shell" + ;; + esac + + case "$mode" in + Delete) + case "$mtag" in + C) # Delete Primary Group + f_dialog_input_group_delete ;; + D) # Delete Home Directory + f_dialog_input_home_delete ;; + esac + ;; + Add|Edit/View) + case "$mtag" in + C) # Create Home Directory + f_dialog_input_home_create + [ "$pw_home_create" = "$msg_no" ] && + pw_dotfiles_create="$msg_no" + ;; + D) # Create Dotfiles + f_dialog_input_dotfiles_create + [ "$pw_dotfiles_create" = "$msg_yes" ] && + pw_home_create="$msg_yes" + ;; + esac + ;; + esac + +done + +exit $SUCCESS + +################################################################################ +# END +################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/usermgmt b/usr.sbin/bsdconfig/usermgmt/usermgmt new file mode 100755 index 0000000000000..460f46bfd594f --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/usermgmt @@ -0,0 +1,150 @@ +#!/bin/sh +#- +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +. $BSDCFG_LIBE/include/common.subr || exit 1 +f_include $BSDCFG_LIBE/include/dialog.subr +f_include $BSDCFG_LIBE/include/mustberoot.subr + +APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) +[ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" + +############################################################ FUNCTIONS + +# dialog_menu_main +# +# Display the dialog(1)-based application main menu. +# +dialog_menu_main() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + + menu_list=" + 'X' '$msg_exit' + '1' '$msg_add_login' + '2' '$msg_edit_login' + '3' '$msg_delete_login' + '-' '-' + '4' '$msg_add_group' + '5' '$msg_edit_group' + '6' '$msg_delete_group' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +############################################################ MAIN + +# Incorporate rc-file if it exists +[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" + +# +# Process command-line arguments +# +while getopts hSX flag; do + case "$flag" in + h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm";; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Initialize +# +f_dialog_init +f_dialog_title "$msg_login_management" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Launch application main menu +# +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + f_dprintf "retval=$retval mtag=[$mtag]" + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + X) # Exit + exit 0 + ;; + + 1) # Add User + $BSDCFG_LIBE/$APP_DIR/useradd ${USE_XDIALOG:+-X} + ;; + + 2) # Edit/View User + $BSDCFG_LIBE/$APP_DIR/useredit ${USE_XDIALOG:+-X} + ;; + + 3) # Delete User + $BSDCFG_LIBE/$APP_DIR/userdel ${USE_XDIALOG:+-X} + ;; + + 4) # Add Group + $BSDCFG_LIBE/$APP_DIR/groupadd ${USE_XDIALOG:+-X} + ;; + + 5) # Edit/View Group + $BSDCFG_LIBE/$APP_DIR/groupedit ${USE_XDIALOG:+-X} + ;; + + 6) # Delete Group + $BSDCFG_LIBE/$APP_DIR/groupdel ${USE_XDIALOG:+-X} + ;; + + esac +done + +################################################################################ +# END +################################################################################ -- cgit v1.3 From 8a664e6c45b920b459006bd606bbd9387b871f3f Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sat, 14 Jul 2012 06:17:17 +0000 Subject: Mdoc and whitespace fixes. --- usr.sbin/bsdconfig/bsdconfig.8 | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig.8 b/usr.sbin/bsdconfig/bsdconfig.8 index 58cfe002a7055..3222ee7a2c67f 100644 --- a/usr.sbin/bsdconfig/bsdconfig.8 +++ b/usr.sbin/bsdconfig/bsdconfig.8 @@ -98,16 +98,16 @@ Use .Xr Xdialog 1 in place of .Xr dialog 1 . - +.El .Sh COMMANDS The following commands .Pq sorted alphabetically are currently included in the base .Nm program, with more to be added soon. Other commands can be added, as detailed -below in the +below in the .Cm ADDING COMMANDS -section, and once added, will appear in the master menu as well as in the +section, and once added, will appear in the master menu as well as in the .Cm -h listing. .Bl -tag -width ".Cm syscons_screenmap" @@ -119,7 +119,7 @@ Shortcut to the Default Router/Gateway menu under networking. Manage disk partitions and/or labels. Executes .Xr sade 8 . .It Cm docsinstall -Executes the +Executes the .Cm bsdinstall docsinstall sub-utility to allow installation/re-installation of the FreeBSD Documentation set(s). @@ -190,19 +190,17 @@ Shortcut to the Delete Users menu under usermgmt. Shortcut to the Edit/View Users menu under usermgmt. .It Cm usermgmt Utilities to Add/Edit/View/Delete User Accounts. - +.El .Sh INTERNATIONALIZATION -i18n features are built into +i18n features are built into .Nm and language-specific translation files will be added as they become available. In the absence of language-specific translation files, the default .Pq en_US.ISO8859-1 files will be used. - .Sh ADDING COMMANDS To be documented later. Document menu_selection="command|*" syntax of INDEX files. - .Sh ENVIRONMENT VARIABLES The following environment variables affect the execution of .Nm : @@ -215,26 +213,21 @@ messages.$LANG and INDEX.$LANG do not exist. LANG takes precedence over LC_ALL. If LC_ALL is set, messages and index information will be read from files named messages.$LC_ALL and INDEX.$LC_ALL and fall back to files named messages and INDEX if messages.$LC_ALL and INDEX.$LC_ALL do not exist. - +.El .Sh FILES /usr/share/examples/bsdconfig/bsdconfigrc can be copied to $HOME/.bsdconfigrc and customized as needed. - .Sh EXIT STATUS .Ex -std - .Sh SEE ALSO -.Xr host-setup 8 - +.Xr host-setup 8 , +.Xr sade 8 .Sh HISTORY .Nm first appeared in .Fx 10.0 . - .Sh AUTHORS -.\" An -nosplit .An Ron McDowell Aq rcm@FuzzWad.ORG .An Devin Teske Aq devinteske@hotmail.com - .Sh BUGS Undoubtedly. -- cgit v1.3 From e18ad51c15963d08b3ca02931c1d26f1910ac536 Mon Sep 17 00:00:00 2001 From: Alexander Kabaev Date: Sun, 15 Jul 2012 04:15:27 +0000 Subject: Make pkg bootstrap program ask for confirmation before proceeding. The previous behaviour was to silently download and install the pkg package, without ever telling user about what it was doing and why. Discussed with: bapt Reviewed by: kib --- usr.sbin/pkg/pkg.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c index b6c0207ed0613..1849eebca6598 100644 --- a/usr.sbin/pkg/pkg.c +++ b/usr.sbin/pkg/pkg.c @@ -389,6 +389,28 @@ cleanup: return (ret); } +static const char confirmation_message[] = +"The package management tool is not yet installed on your system.\n" +"Do you want to fetch and install it now? [y/N]: "; + +static int +pkg_query_yes_no(void) +{ + int ret, c; + + c = getchar(); + + if (c == 'y' || c == 'Y') + ret = 1; + else + ret = 0; + + while (c != '\n' && c != EOF) + c = getchar(); + + return (ret); +} + int main(__unused int argc, char *argv[]) { @@ -397,9 +419,21 @@ main(__unused int argc, char *argv[]) snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); - if (access(pkgpath, X_OK) == -1) + if (access(pkgpath, X_OK) == -1) { + /* + * Do not ask for confirmation if either of stdin or stdout is + * not tty. Check the environment to see if user has answer + * tucked in there already. + */ + if (getenv("ALWAYS_ASSUME_YES") == NULL && + isatty(fileno(stdin))) { + printf("%s", confirmation_message); + if (pkg_query_yes_no() == 0) + exit(EXIT_FAILURE); + } if (bootstrap_pkg() != 0) exit(EXIT_FAILURE); + } execv(pkgpath, argv); -- cgit v1.3 From f9fbbdf95b22cc0f7493a616d667c3edaf1f65d6 Mon Sep 17 00:00:00 2001 From: Jaakko Heinonen Date: Tue, 17 Jul 2012 09:31:05 +0000 Subject: Remove trailing whitespace. MFC after: 2 weeks --- usr.sbin/lpr/common_source/common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c index 819d7dd1ca875..9b87dfa65dcb4 100644 --- a/usr.sbin/lpr/common_source/common.c +++ b/usr.sbin/lpr/common_source/common.c @@ -136,7 +136,7 @@ getq(const struct printer *pp, struct jobqueue *(*namelist[])) /* * Estimate the array size by taking the size of the directory file - * and dividing it by a multiple of the minimum size entry. + * and dividing it by a multiple of the minimum size entry. */ arraysz = (stbuf.st_size / 24); queue = (struct jobqueue **)malloc(arraysz * sizeof(struct jobqueue *)); @@ -641,7 +641,7 @@ trstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt, * secs= - seconds it took to transfer the file * bytes= - number of bytes transfered (ie, "bytecount") * bps=e - Bytes/sec (if the transfer was "big enough" - * for this to be useful) + * for this to be useful) * ! top= - type of printer (if the type is defined in * printcap, and if this statline is for sending * a file to that ptr) @@ -719,7 +719,7 @@ trstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt, if (remspace > 1) { strcpy(eostat, "\n"); } else { - /* probably should back up to just before the final " x=".. */ + /* probably should back up to just before the final " x=".. */ strcpy(statline+STATLINE_SIZE-2, "\n"); } statfile = open(statfname, O_WRONLY|O_APPEND, 0664); @@ -732,7 +732,7 @@ trstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt, close(statfile); return; -#undef UPD_EOSTAT +#undef UPD_EOSTAT } #include -- cgit v1.3 From e0d20e66159c222104995f137d4114a0b41c88bd Mon Sep 17 00:00:00 2001 From: Jaakko Heinonen Date: Tue, 17 Jul 2012 09:34:52 +0000 Subject: Make sure that arraysz is initialized to a value larger than zero. arraysz could get initialized to zero on ZFS because ZFS reports directory sizes differently compared to UFS. PR: bin/169493 Tested by: swills MFC after: 2 weeks --- usr.sbin/lpr/common_source/common.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c index 9b87dfa65dcb4..c9c276d8c5590 100644 --- a/usr.sbin/lpr/common_source/common.c +++ b/usr.sbin/lpr/common_source/common.c @@ -139,6 +139,8 @@ getq(const struct printer *pp, struct jobqueue *(*namelist[])) * and dividing it by a multiple of the minimum size entry. */ arraysz = (stbuf.st_size / 24); + if (arraysz < 16) + arraysz = 16; queue = (struct jobqueue **)malloc(arraysz * sizeof(struct jobqueue *)); if (queue == NULL) goto errdone; -- cgit v1.3 From abded2bcf255c661c1b1aa8d014b6c337c1d8932 Mon Sep 17 00:00:00 2001 From: Glen Barber Date: Wed, 18 Jul 2012 23:32:12 +0000 Subject: Xref mwl(4) and rum(4) in hostapd(8). MFC after: 3 days --- usr.sbin/wpa/hostapd/hostapd.8 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/wpa/hostapd/hostapd.8 b/usr.sbin/wpa/hostapd/hostapd.8 index 7be2cbf156849..f624cac03e525 100644 --- a/usr.sbin/wpa/hostapd/hostapd.8 +++ b/usr.sbin/wpa/hostapd/hostapd.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 26, 2007 +.Dd July 18, 2012 .Dt HOSTAPD 8 .Os .Sh NAME @@ -111,7 +111,9 @@ Store PID in .Xr ath 4 , .Xr ipw 4 , .Xr iwi 4 , +.Xr mwl 4 , .Xr ral 4 , +.Xr rum 4 , .Xr run 4 , .Xr ural 4 , .Xr wi 4 , -- cgit v1.3 From eb35bc035c8c4f4d5a74b49bd433d55d8a52f575 Mon Sep 17 00:00:00 2001 From: Niclas Zeising Date: Thu, 19 Jul 2012 08:56:30 +0000 Subject: Update my e-mail to my FreeBSD one. Approved by: joel (mentor) --- usr.sbin/ipfwpcap/ipfwpcap.8 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ipfwpcap/ipfwpcap.8 b/usr.sbin/ipfwpcap/ipfwpcap.8 index 53787fcbb5456..523b059a156f3 100644 --- a/usr.sbin/ipfwpcap/ipfwpcap.8 +++ b/usr.sbin/ipfwpcap/ipfwpcap.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2006 Niclas Zeising +.\" Copyright (c) 2006 Niclas Zeising .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -129,4 +129,4 @@ utility first appeared in was written by .An P. Kern Aq pkern@cns.utoronto.ca . This manual page was written by -.An Niclas Zeising Aq niclas.zeising@gmail.com . +.An Niclas Zeising Aq zeising@FreeBSD.org . -- cgit v1.3 From b0c39ce9166480c68292876504eadb9a1628da15 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Sun, 5 Aug 2012 17:23:47 +0000 Subject: In usr.sbin/ctladm/ctladm.c, function cctl_error_inject(), initialize the 'retval' variable to zero, to avoid returning garbage in several cases. This fixes the following clang 3.2 warnings: usr.sbin/ctladm/ctladm.c:1234:6: error: variable 'retval' is used uninitialized whenever 'if' condition is false [-Werror,-Wsometimes-uninitialized] if (ioctl(fd, CTL_ERROR_INJECT, &err_desc) == -1) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ usr.sbin/ctladm/ctladm.c:1243:10: note: uninitialized use occurs here return (retval); ^~~~~~ usr.sbin/ctladm/ctladm.c:1234:2: note: remove the 'if' if its condition is always true if (ioctl(fd, CTL_ERROR_INJECT, &err_desc) == -1) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ usr.sbin/ctladm/ctladm.c:1161:7: error: variable 'retval' is used uninitialized whenever 'if' condition is false [-Werror,-Wsometimes-uninitialized] if (ioctl(fd, CTL_ERROR_INJECT_DELETE, &err_desc) == -1) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ usr.sbin/ctladm/ctladm.c:1243:10: note: uninitialized use occurs here return (retval); ^~~~~~ usr.sbin/ctladm/ctladm.c:1161:3: note: remove the 'if' if its condition is always true if (ioctl(fd, CTL_ERROR_INJECT_DELETE, &err_desc) == -1) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ usr.sbin/ctladm/ctladm.c:1029:12: note: initialize the variable 'retval' to silence this warning int retval; ^ = 0 MFC after: 1 week --- usr.sbin/ctladm/ctladm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 4cdbd42ce4f5d..9497101d03754 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -1026,7 +1026,7 @@ static int cctl_error_inject(int fd, uint32_t target, uint32_t lun, int argc, char **argv, char *combinedopt) { - int retval; + int retval = 0; struct ctl_error_desc err_desc; uint64_t lba = 0; uint32_t len = 0; -- cgit v1.3 From a6c38148a0b6468af4023547efb32749dd35f892 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Sun, 5 Aug 2012 21:13:21 +0000 Subject: Add interactive option to override portsnap's automagical detection of a terminal. Submitted by: Hannes h2+fbsdports@fsfe.org Approved by: cperciva MFC after: 1 week --- usr.sbin/portsnap/portsnap/portsnap.8 | 8 +++++++- usr.sbin/portsnap/portsnap/portsnap.sh | 20 +++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/portsnap/portsnap/portsnap.8 b/usr.sbin/portsnap/portsnap/portsnap.8 index 0d9c2612a1e2a..f0b362803d85c 100644 --- a/usr.sbin/portsnap/portsnap/portsnap.8 +++ b/usr.sbin/portsnap/portsnap/portsnap.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 15, 2008 +.Dd August 5, 2012 .Dt PORTSNAP 8 .Os FreeBSD .Sh NAME @@ -110,6 +110,12 @@ command only, operate only on parts of the ports tree starting with .Ar sysutils/port would extract sysutils/portsman, sysutils/portsnap, sysutils/portupgrade, etc.) +.It Fl Fl interactive +override auto-detection of calling process. +Only use this when calling portsnap from an +.Sy interactive, non-terminal application. +(Cron jobs are particularly bad since they cause +load spikes on the Portsnap mirrors.) .El .Sh COMMANDS The diff --git a/usr.sbin/portsnap/portsnap/portsnap.sh b/usr.sbin/portsnap/portsnap/portsnap.sh index 033bb55fe074f..80718203a8e15 100644 --- a/usr.sbin/portsnap/portsnap/portsnap.sh +++ b/usr.sbin/portsnap/portsnap/portsnap.sh @@ -48,6 +48,9 @@ Options: (default: /usr/ports/) -s server -- Server from which to fetch updates. (default: portsnap.FreeBSD.org) + --interactive -- interactive: override auto-detection of calling process + (use this when calling portsnap from an interactive, non- + terminal application AND NEVER ELSE). path -- Extract only parts of the tree starting with the given string. (extract command only) Commands: @@ -84,6 +87,7 @@ init_params() { SERVERNAME="" REFUSE="" LOCALDESC="" + INTERACTIVE="" } # Parse the command line @@ -103,6 +107,9 @@ parse_cmdline() { XARGST="-t" DDSTATS=".." ;; + --interactive) + INTERACTIVE="YES" + ;; -f) if [ $# -eq 1 ]; then usage; fi if [ ! -z "${CONFFILE}" ]; then usage; fi @@ -228,6 +235,13 @@ default_params() { eval ${X}=${__} fi done + if [ -z "${INTERACTIVE}" ]; then + if [ -t 0 ]; then + INTERACTIVE="YES" + else + INTERACTIVE="NO" + fi + fi } # Perform sanity checks and set some final parameters @@ -1023,10 +1037,10 @@ get_params() { # Fetch command. Make sure that we're being called # interactively, then run fetch_check_params and fetch_run cmd_fetch() { - if [ ! -t 0 ]; then + if [ "${INTERACTIVE}" != "YES" ]; then echo -n "`basename $0` fetch should not " echo "be run non-interactively." - echo "Run `basename $0` cron instead." + echo "Run `basename $0` cron instead" exit 1 fi fetch_check_params @@ -1069,7 +1083,7 @@ cmd_update() { # whether stdin is a terminal; then run 'update' or # 'extract' depending on whether ${PORTSDIR} exists. cmd_alfred() { - if [ -t 0 ]; then + if [ "${INTERACTIVE}" = "YES" ]; then cmd_fetch else cmd_cron -- cgit v1.3 From 44d8663ddb77ef87257d364e8898e57f4bdb4610 Mon Sep 17 00:00:00 2001 From: Warren Block Date: Mon, 13 Aug 2012 15:16:25 +0000 Subject: Correct description of minfree to kilobytes rather than blocks. PR: 125921 Submitted by: Andre Albsmeier MFC after: 3 days --- usr.sbin/lpr/lpd/lpd.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8 index 34b0882b92213..a3d7aa93ed1d5 100644 --- a/usr.sbin/lpr/lpd/lpd.8 +++ b/usr.sbin/lpr/lpd/lpd.8 @@ -152,7 +152,7 @@ machine with the printer. .Pp The file .Em minfree -in each spool directory contains the number of disk blocks to leave free +in each spool directory contains the number of kilobytes to leave free so that the line printer queue will not completely fill the disk. The .Em minfree -- cgit v1.3 From 1ec9bedabe67b4d2ad5f86cd8636cba4a155242e Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Fri, 17 Aug 2012 17:45:27 +0000 Subject: Remove unused member of struct indir (in_exists) from UFS and EXT2 code. Reviewed by: mckusick Approved by: trasz (mentor) MFC after: 1 week --- sys/fs/ext2fs/ext2_bmap.c | 3 --- sys/fs/ext2fs/inode.h | 1 - sys/ufs/ufs/inode.h | 1 - sys/ufs/ufs/ufs_bmap.c | 3 --- usr.sbin/makefs/ffs/ffs_extern.h | 1 - usr.sbin/makefs/ffs/ufs_bmap.c | 2 -- 6 files changed, 11 deletions(-) (limited to 'usr.sbin') diff --git a/sys/fs/ext2fs/ext2_bmap.c b/sys/fs/ext2fs/ext2_bmap.c index ba8eb632101da..a0581155e3a56 100644 --- a/sys/fs/ext2fs/ext2_bmap.c +++ b/sys/fs/ext2fs/ext2_bmap.c @@ -183,7 +183,6 @@ ext2_bmaparray(vp, bn, bnp, runp, runb) if (bp) bqrelse(bp); - ap->in_exists = 1; bp = getblk(vp, metalbn, bsize, 0, 0, 0); if ((bp->b_flags & B_CACHE) == 0) { #ifdef DIAGNOSTIC @@ -310,7 +309,6 @@ ext2_getlbns(vp, bn, ap, nump) */ ap->in_lbn = metalbn; ap->in_off = off = NIADDR - i; - ap->in_exists = 0; ap++; for (++numlevels; i <= NIADDR; i++) { /* If searching for a meta-data block, quit when found. */ @@ -322,7 +320,6 @@ ext2_getlbns(vp, bn, ap, nump) ++numlevels; ap->in_lbn = metalbn; ap->in_off = off; - ap->in_exists = 0; ++ap; metalbn -= -1 + off * blockcnt; diff --git a/sys/fs/ext2fs/inode.h b/sys/fs/ext2fs/inode.h index 100a07687db72..d8aaac9c41d01 100644 --- a/sys/fs/ext2fs/inode.h +++ b/sys/fs/ext2fs/inode.h @@ -151,7 +151,6 @@ struct inode { struct indir { int32_t in_lbn; /* Logical block number. */ int in_off; /* Offset in buffer. */ - int in_exists; /* Flag if the block exists. */ }; /* Convert between inode pointers and vnode pointers. */ diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h index 2b020007f8352..51f0197974d45 100644 --- a/sys/ufs/ufs/inode.h +++ b/sys/ufs/ufs/inode.h @@ -167,7 +167,6 @@ struct inode { struct indir { ufs2_daddr_t in_lbn; /* Logical block number. */ int in_off; /* Offset in buffer. */ - int in_exists; /* Flag if the block exists. */ }; /* Convert between inode pointers and vnode pointers. */ diff --git a/sys/ufs/ufs/ufs_bmap.c b/sys/ufs/ufs/ufs_bmap.c index e0fb30761f36c..22887c8ad9bea 100644 --- a/sys/ufs/ufs/ufs_bmap.c +++ b/sys/ufs/ufs/ufs_bmap.c @@ -212,7 +212,6 @@ ufs_bmaparray(vp, bn, bnp, nbp, runp, runb) if (bp) bqrelse(bp); - ap->in_exists = 1; bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0, 0); if ((bp->b_flags & B_CACHE) == 0) { #ifdef INVARIANTS @@ -357,7 +356,6 @@ ufs_getlbns(vp, bn, ap, nump) */ ap->in_lbn = metalbn; ap->in_off = off = NIADDR - i; - ap->in_exists = 0; ap++; for (++numlevels; i <= NIADDR; i++) { /* If searching for a meta-data block, quit when found. */ @@ -370,7 +368,6 @@ ufs_getlbns(vp, bn, ap, nump) ++numlevels; ap->in_lbn = metalbn; ap->in_off = off; - ap->in_exists = 0; ++ap; metalbn -= -1 + off * blockcnt; diff --git a/usr.sbin/makefs/ffs/ffs_extern.h b/usr.sbin/makefs/ffs/ffs_extern.h index 1c3442f59795c..7755823c17030 100644 --- a/usr.sbin/makefs/ffs/ffs_extern.h +++ b/usr.sbin/makefs/ffs/ffs_extern.h @@ -44,7 +44,6 @@ struct inode; struct indir { daddr_t in_lbn; /* Logical block number. */ int in_off; /* Offset in buffer. */ - int in_exists; /* Flag if the block exists. */ }; /* ffs.c */ diff --git a/usr.sbin/makefs/ffs/ufs_bmap.c b/usr.sbin/makefs/ffs/ufs_bmap.c index 85c1d87597439..b65b416361507 100644 --- a/usr.sbin/makefs/ffs/ufs_bmap.c +++ b/usr.sbin/makefs/ffs/ufs_bmap.c @@ -117,7 +117,6 @@ ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump) */ ap->in_lbn = metalbn; ap->in_off = off = NIADDR - i; - ap->in_exists = 0; ap++; for (++numlevels; i <= NIADDR; i++) { /* If searching for a meta-data block, quit when found. */ @@ -131,7 +130,6 @@ ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump) ++numlevels; ap->in_lbn = metalbn; ap->in_off = off; - ap->in_exists = 0; ++ap; metalbn -= -1 + (off << lbc); -- cgit v1.3 From 442646f5c181cd8257b70820910d80982c051cca Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Tue, 21 Aug 2012 13:46:46 +0000 Subject: Fix "unexpected operator" error when passed multi-word first-argument containing whitespace. Also make other changes to support multi-word arguments. PR: bin/170759 Submitted by: dteske Reviewed by: emaste (mentor) Approved by: emaste (mentor) MFC after: 3 days --- usr.sbin/bsdinstall/bsdinstall | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdinstall/bsdinstall b/usr.sbin/bsdinstall/bsdinstall index 236dbac95e7a1..8225daffda77e 100755 --- a/usr.sbin/bsdinstall/bsdinstall +++ b/usr.sbin/bsdinstall/bsdinstall @@ -34,11 +34,10 @@ VERB=$1; shift -if [ -z $VERB ]; then +if [ -z "$VERB" ]; then VERB=auto fi test -d "$BSDINSTALL_TMPETC" || mkdir "$BSDINSTALL_TMPETC" -echo Running installation step: $VERB $@ >> "$BSDINSTALL_LOG" -exec /usr/libexec/bsdinstall/$VERB $@ 2>>"$BSDINSTALL_LOG" - +echo "Running installation step: $VERB $@" >> "$BSDINSTALL_LOG" +exec "/usr/libexec/bsdinstall/$VERB" "$@" 2>>"$BSDINSTALL_LOG" -- cgit v1.3 From 99c841b11eb1609ae1dedb9b5da6869fea65189b Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Wed, 22 Aug 2012 06:37:30 +0000 Subject: Add -p flag to create the image as a sparse file. Submitted by: Shesha Sreenivasamurthy PR: bin/167779 --- usr.sbin/makefs/ffs.c | 29 +++++++++++++++++++++-------- usr.sbin/makefs/makefs.8 | 5 ++++- usr.sbin/makefs/makefs.c | 7 +++++-- usr.sbin/makefs/makefs.h | 1 + 4 files changed, 31 insertions(+), 11 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c index 13f00cf7099cf..0417d86095ec3 100644 --- a/usr.sbin/makefs/ffs.c +++ b/usr.sbin/makefs/ffs.c @@ -493,13 +493,25 @@ ffs_create_image(const char *image, fsinfo_t *fsopts) bufsize = sfs.f_iosize; #endif bufrem = fsopts->size; - if (debug & DEBUG_FS_CREATE_IMAGE) - printf( - "zero-ing image `%s', %lld sectors, using %d byte chunks\n", - image, (long long)bufrem, bufsize); - if ((buf = calloc(1, bufsize)) == NULL) { - warn("Can't create buffer for sector"); - return (-1); + if (fsopts->sparse) { + if (ftruncate(fsopts->fd, bufrem) == -1) { + warn("sparse option disabled.\n"); + fsopts->sparse = 0; + } + } + if (fsopts->sparse) { + /* File truncated at bufrem. Remaining is 0 */ + bufrem = 0; + buf = NULL; + } else { + if (debug & DEBUG_FS_CREATE_IMAGE) + printf("zero-ing image `%s', %lld sectors, " + "using %d byte chunks\n", image, (long long)bufrem, + bufsize); + if ((buf = calloc(1, bufsize)) == NULL) { + warn("Can't create buffer for sector"); + return (-1); + } } while (bufrem > 0) { i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); @@ -511,7 +523,8 @@ ffs_create_image(const char *image, fsinfo_t *fsopts) } bufrem -= i; } - free(buf); + if (buf) + free(buf); /* make the file system */ if (debug & DEBUG_FS_CREATE_IMAGE) diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8 index cc6e389289324..7c5f37332bf09 100644 --- a/usr.sbin/makefs/makefs.8 +++ b/usr.sbin/makefs/makefs.8 @@ -35,7 +35,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 30, 2012 +.Dd August 22, 2012 .Dt MAKEFS 8 .Os .Sh NAME @@ -43,6 +43,7 @@ .Nd create a file system image from a directory tree or a mtree manifest .Sh SYNOPSIS .Nm +.Op Fl p .Op Fl x .Op Fl B Ar byte-order .Op Fl b Ar free-blocks @@ -188,6 +189,8 @@ Set file system specific options. .Ar fs-options is a comma separated list of options. Valid file system specific options are detailed below. +.It Fl p +Create the image as a sparse file. .It Fl S Ar sector-size Set the file system sector size to .Ar sector-size . diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c index 623ca8ac4e97e..7aebbbbbc510f 100644 --- a/usr.sbin/makefs/makefs.c +++ b/usr.sbin/makefs/makefs.c @@ -112,7 +112,7 @@ main(int argc, char *argv[]) start_time.tv_sec = start.tv_sec; start_time.tv_nsec = start.tv_usec * 1000; - while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:o:s:S:t:x")) != -1) { + while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:o:ps:S:t:x")) != -1) { switch (ch) { case 'B': @@ -199,6 +199,9 @@ main(int argc, char *argv[]) } break; } + case 'p': + fsoptions.sparse = 1; + break; case 's': fsoptions.minsize = fsoptions.maxsize = @@ -346,7 +349,7 @@ usage(void) fprintf(stderr, "usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n" "\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n" -"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x]\n" +"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x] [-p]\n" "\t[-N userdb-dir] image-file directory | manifest [extra-directory ...]\n", prog); exit(1); diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h index f6cadeb98c4ff..483ccff961418 100644 --- a/usr.sbin/makefs/makefs.h +++ b/usr.sbin/makefs/makefs.h @@ -129,6 +129,7 @@ typedef struct { int freeblockpc; /* free block % */ int needswap; /* non-zero if byte swapping needed */ int sectorsize; /* sector size */ + int sparse; /* sparse image, don't fill it with zeros */ void *fs_specific; /* File system specific additions. */ } fsinfo_t; -- cgit v1.3 From 33b12a8557471643334fb1633673e890fbce376b Mon Sep 17 00:00:00 2001 From: Jim Harris Date: Wed, 22 Aug 2012 19:02:07 +0000 Subject: Add -m option (for printing sampled PCs to a file) to pmcstat usage message. Sponsored by: Intel MFC after: 3 days --- usr.sbin/pmcstat/pmcstat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'usr.sbin') diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c index 0133119e221dc..6808eedab2024 100644 --- a/usr.sbin/pmcstat/pmcstat.c +++ b/usr.sbin/pmcstat/pmcstat.c @@ -509,6 +509,7 @@ pmcstat_show_usage(void) "\t -f spec\t pass \"spec\" to as plugin option\n" "\t -g\t\t produce gprof(1) compatible profiles\n" "\t -k dir\t\t set the path to the kernel\n" + "\t -m file\t print sampled PCs to \"file\"\n" "\t -n rate\t set sampling rate\n" "\t -o file\t send print output to \"file\"\n" "\t -p spec\t allocate a process-private counting PMC\n" -- cgit v1.3 From 0f065c85a83fc9a70ecd8e95b41a83bb6c91bb06 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Wed, 22 Aug 2012 19:27:42 +0000 Subject: Sort options. --- usr.sbin/makefs/makefs.8 | 3 +-- usr.sbin/makefs/makefs.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8 index 7c5f37332bf09..68d6ca6652525 100644 --- a/usr.sbin/makefs/makefs.8 +++ b/usr.sbin/makefs/makefs.8 @@ -43,8 +43,7 @@ .Nd create a file system image from a directory tree or a mtree manifest .Sh SYNOPSIS .Nm -.Op Fl p -.Op Fl x +.Op Fl px .Op Fl B Ar byte-order .Op Fl b Ar free-blocks .Op Fl d Ar debug-mask diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c index 7aebbbbbc510f..b2da82b426dfa 100644 --- a/usr.sbin/makefs/makefs.c +++ b/usr.sbin/makefs/makefs.c @@ -349,7 +349,7 @@ usage(void) fprintf(stderr, "usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n" "\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n" -"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x] [-p]\n" +"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-px]\n" "\t[-N userdb-dir] image-file directory | manifest [extra-directory ...]\n", prog); exit(1); -- cgit v1.3 From 2b4f1090dec7472aec701d23bf5544ce8b85cb1b Mon Sep 17 00:00:00 2001 From: Jamie Gritton Date: Thu, 23 Aug 2012 01:43:01 +0000 Subject: Remember that I'm using length-defined strings in parameters: Remove a bogus null terminator when stripping the netmask from IP addresses. This was causing later addresses in a comma-separated string to disappear. Use memcpy instead of strcpy. This could just cause Bad Things. PR: 170832 MFC after: 1 week --- usr.sbin/jail/config.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c index 7a7917d2a5cf1..508f28b4b4232 100644 --- a/usr.sbin/jail/config.c +++ b/usr.sbin/jail/config.c @@ -597,8 +597,7 @@ check_intparams(struct cfjail *j) "ip4.addr: bad netmask \"%s\"", cs); error = -1; } - *cs = '\0'; - s->len = cs - s->s + 1; + s->len = cs - s->s; } } } @@ -621,8 +620,7 @@ check_intparams(struct cfjail *j) cs); error = -1; } - *cs = '\0'; - s->len = cs - s->s + 1; + s->len = cs - s->s; } } } @@ -714,7 +712,7 @@ import_params(struct cfjail *j) value = alloca(vallen); cs = value; TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { - strcpy(cs, s->s); + memcpy(cs, s->s, s->len); if (ts != NULL) { cs += s->len + 1; cs[-1] = ','; -- cgit v1.3 From b01d717742bff92d8dbc1f47ff575f69d5f645ed Mon Sep 17 00:00:00 2001 From: Jamie Gritton Date: Thu, 23 Aug 2012 01:43:22 +0000 Subject: Pre-separate IP addresses passed on the command line, so they can be properly parsed for interface prefixes and netmask suffixes. This was already done for the old-style (fixed) command line, but missed for the new-style. MFC after: 1 week --- usr.sbin/jail/jail.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c index 78160c007539a..6970ae176287a 100644 --- a/usr.sbin/jail/jail.c +++ b/usr.sbin/jail/jail.c @@ -304,9 +304,33 @@ main(int argc, char **argv) for (i++; i < argc; i++) add_param(NULL, NULL, IP_COMMAND, argv[i]); - break; } - add_param(NULL, NULL, 0, argv[i]); +#ifdef INET + else if (!strncmp(argv[i], "ip4.addr=", 9)) { + for (cs = argv[i] + 9;; cs = ncs + 1) { + ncs = strchr(cs, ','); + if (ncs) + *ncs = '\0'; + add_param(NULL, NULL, KP_IP4_ADDR, cs); + if (!ncs) + break; + } + } +#endif +#ifdef INET6 + else if (!strncmp(argv[i], "ip6.addr=", 9)) { + for (cs = argv[i] + 9;; cs = ncs + 1) { + ncs = strchr(cs, ','); + if (ncs) + *ncs = '\0'; + add_param(NULL, NULL, KP_IP6_ADDR, cs); + if (!ncs) + break; + } + } +#endif + else + add_param(NULL, NULL, 0, argv[i]); } } else { /* From the config file, perhaps with a specified jail */ -- cgit v1.3 From e9322de834debf91d35c9a0fc67296a280ce2bb3 Mon Sep 17 00:00:00 2001 From: Jamie Gritton Date: Thu, 23 Aug 2012 19:39:23 +0000 Subject: Partially roll back r239601 - keep parameter strings both length-delimited and null-terminated at the same time, because they're later passed to libjail as null-terminated. That means I also need to add a nul byte when comma-combining array parameters. MFC after: 6 days --- usr.sbin/jail/config.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c index 508f28b4b4232..ec96c0c95e909 100644 --- a/usr.sbin/jail/config.c +++ b/usr.sbin/jail/config.c @@ -597,6 +597,7 @@ check_intparams(struct cfjail *j) "ip4.addr: bad netmask \"%s\"", cs); error = -1; } + *cs = '\0'; s->len = cs - s->s; } } @@ -620,6 +621,7 @@ check_intparams(struct cfjail *j) cs); error = -1; } + *cs = '\0'; s->len = cs - s->s; } } @@ -713,11 +715,10 @@ import_params(struct cfjail *j) cs = value; TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { memcpy(cs, s->s, s->len); - if (ts != NULL) { - cs += s->len + 1; - cs[-1] = ','; - } + cs += s->len + 1; + cs[-1] = ','; } + value[vallen - 1] = '\0'; } if (jailparam_import(jp, value) < 0) { error = -1; -- cgit v1.3 From 3a480126c466f6621a2fbcb811b4bebbdf6a2c82 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Fri, 24 Aug 2012 21:08:56 +0000 Subject: - change ALWAYS_ASSUME_YES to ASSUME_ALWAYS_YES for consistency with pkg(8) - if not on a tty prompt about the missing pkg(8) but default on 'no' except if ASSUME_ALWAYS_YES is set MFC after: 2 days --- usr.sbin/pkg/pkg.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c index 1849eebca6598..50e71cff0b1e1 100644 --- a/usr.sbin/pkg/pkg.c +++ b/usr.sbin/pkg/pkg.c @@ -425,10 +425,12 @@ main(__unused int argc, char *argv[]) * not tty. Check the environment to see if user has answer * tucked in there already. */ - if (getenv("ALWAYS_ASSUME_YES") == NULL && - isatty(fileno(stdin))) { + if (getenv("ASSUME_ALWAYS_YES") == NULL) { printf("%s", confirmation_message); - if (pkg_query_yes_no() == 0) + if (isatty(fileno(stdin)) && + pkg_query_yes_no() == 0) + exit(EXIT_FAILURE); + else exit(EXIT_FAILURE); } if (bootstrap_pkg() != 0) -- cgit v1.3 From 204ea792b39cba7209d39172f04f67342f39afe3 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Fri, 24 Aug 2012 21:45:52 +0000 Subject: Fix confirmation logic when detecting a tty Reported by: mjg --- usr.sbin/pkg/pkg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c index 50e71cff0b1e1..8dc7bcfd9a1ac 100644 --- a/usr.sbin/pkg/pkg.c +++ b/usr.sbin/pkg/pkg.c @@ -427,10 +427,10 @@ main(__unused int argc, char *argv[]) */ if (getenv("ASSUME_ALWAYS_YES") == NULL) { printf("%s", confirmation_message); - if (isatty(fileno(stdin)) && - pkg_query_yes_no() == 0) + if (!isatty(fileno(stdin))) exit(EXIT_FAILURE); - else + + if (pkg_query_yes_no() == 0) exit(EXIT_FAILURE); } if (bootstrap_pkg() != 0) -- cgit v1.3 From 087d31736a5fe7e8acdbbf2242bea830e328f618 Mon Sep 17 00:00:00 2001 From: Brooks Davis Date: Sat, 25 Aug 2012 18:08:20 +0000 Subject: Add isf(4), a driver for the Intel StrataFlash family of NOR flash parts. The driver attempts to support all documented parts, but has only been tested with the 512Mbit part on the Terasic DE4 FPGA board. It should be trivial to adapt the driver's attach routine to other embedded boards using with any parts in the family. Also import isfctl(8) which can be used to erase sections of the flash. Sponsored by: DARPA, AFRL --- share/man/man4/Makefile | 1 + share/man/man4/isf.4 | 135 ++++++++ sys/conf/files | 2 + sys/dev/isf/isf.c | 740 ++++++++++++++++++++++++++++++++++++++++++ sys/dev/isf/isf.h | 94 ++++++ sys/dev/isf/isf_nexus.c | 120 +++++++ sys/mips/conf/BERI_DE4.hints | 10 + sys/mips/conf/BERI_DE4_MDROOT | 1 + sys/mips/conf/BERI_DE4_SDROOT | 1 + usr.sbin/Makefile | 1 + usr.sbin/isfctl/Makefile | 7 + usr.sbin/isfctl/isfctl.8 | 89 +++++ usr.sbin/isfctl/isfctl.c | 115 +++++++ 13 files changed, 1316 insertions(+) create mode 100644 share/man/man4/isf.4 create mode 100644 sys/dev/isf/isf.c create mode 100644 sys/dev/isf/isf.h create mode 100644 sys/dev/isf/isf_nexus.c create mode 100644 usr.sbin/isfctl/Makefile create mode 100644 usr.sbin/isfctl/isfctl.8 create mode 100644 usr.sbin/isfctl/isfctl.c (limited to 'usr.sbin') diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index e0124faeb1ad6..1863c550b6d6f 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -192,6 +192,7 @@ MAN= aac.4 \ ipwfw.4 \ isci.4 \ iscsi_initiator.4 \ + isf.4 \ isp.4 \ ispfw.4 \ iwi.4 \ diff --git a/share/man/man4/isf.4 b/share/man/man4/isf.4 new file mode 100644 index 0000000000000..f8637203b47c9 --- /dev/null +++ b/share/man/man4/isf.4 @@ -0,0 +1,135 @@ +.\"- +.\" Copyright (c) 2012 SRI International +.\" All rights reserved. +.\" +.\" This software was developed by SRI International and the University of +.\" Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) +.\" ("CTSRD"), as part of the DARPA CRASH research programme. +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd August 3, 2012 +.Dt ISF 4 +.Os +.Sh NAME +.Nm isf +.Nd driver for Intel StrataFlash NOR flash devices +.Sh SYNOPSIS +.Cd "device isf" +.Pp +In +.Pa /boot/device.hints : +.Cd hint.isf.0.at="nexus0" +.Cd hint.isf.0.maddr=0x74000000 +.Cd hint.isf.0.msize=0x2000000 +.Sh DESCRIPTION +The +.Nm +device driver provides support for Intel StrataFlash NOR flash devices. +Devices are presented as +.Xr disk 9 +devices and read access is supported along with limited write support. +Erasing blocks is supported the +.Dv ISF_ERASE +ioctl. +.Pp +The erase block size of +.Nm +devices is 128K. +NOR flash blocks contains all 1's after an erase cycle. +Writes to +.Nm +devices are allowed to succeed if and only if they all bits in the write +block (512-bytes) remain the same or transition from 1 to 0. +.Sh HARDWARE +The current version of the +.Nm +driver is known to support the 64MB part found on the Altera DE4 board. +It attempts to support other StrataFlash parts documented in the +datasheet, but those are untested. +.Sh IOCTLS +The +.Nm device +supports the +.Xr ioctl 2 +command codes: +.Bl -tag -width ISF_ERASE +.It Dv ISF_ERASE +Erase one or more blocks. +.Dv ISF_ERASE is defined as follows: +.Bd -literal + struct isf_range { + off_t ir_off; + size_t ir_size; + }; + + #define ISF_ERASE _IOW('I', 1, struct isf_range) +.Ed +.Pp +The +.Li ir_off +member marks the beginning of the area to be erased and must fall on at 128K +boundary. +The +.Li ir_size +member indicates the size of the area to be erased and must a multiple +of 128K. +.El +.Sh SEE ALSO +.Xr isfctl 4 , +.Xr disk 9 +.Rs +.%T Intel StrataFlash Embedded Memory (P30) +.%D November 1, 2005 +.%I Intel Corporation +.%U http://www.xilinx.com/products/boards/ml505/datasheets/30666604.pdf +.Re +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 10.0 . +.Sh AUTHORS +The +.Nm +device driver and this manual page were +developed by SRI International and the University of Cambridge Computer +Laboratory under DARPA/AFRL contract +.Pq FA8750-10-C-0237 +.Pq Do CTSRD Dc , +as part of the DARPA CRASH research programme. +.Sh BUGS +While an erase is in progress, all read and write operations return +.Er EBUSY . +In principle, reads could be allowed outside the programming region the +blocked currently being erased resides in and writes could be allowed by +suspending the erase, but neither of these is currently implemented. +.Pp +Depending on the flash part ether the top or bottom 128K of the flash +address space is divided into 4 32K erase blocks. +The +.Nm +driver hides this from the user requiring that all erase requests be +multiples of 128K in size and erasing the individual blocks as needed at +the top or bottom. diff --git a/sys/conf/files b/sys/conf/files index 54100daf094bb..a95e04564f00e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1380,6 +1380,8 @@ dev/iscsi/initiator/isc_cam.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_soc.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_sm.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_subr.c optional iscsi_initiator scbus +dev/isf/isf.c optional isf +dev/isf/isf_nexus.c optional isf dev/isp/isp.c optional isp dev/isp/isp_freebsd.c optional isp dev/isp/isp_library.c optional isp diff --git a/sys/dev/isf/isf.c b/sys/dev/isf/isf.c new file mode 100644 index 0000000000000..b3ac080a381f3 --- /dev/null +++ b/sys/dev/isf/isf.c @@ -0,0 +1,740 @@ +/*- + * Copyright (c) 2012 Robert N. M. Watson + * Copyright (c) 2012 SRI International + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* Read Mode */ +#define ISF_CMD_RA 0xFF /* Read Array mode */ +#define ISF_CMD_RSR 0x70 /* Read Status Register mode */ +#define ISF_CMD_RDI 0x90 /* Read Device ID/Config Reg mode */ +#define ISF_CMD_RQ 0x98 /* Read Query mode */ +#define ISF_CMD_CSR 0x50 /* Clear Status Register */ + +/* Write Mode */ +#define ISF_CMD_WPS 0x40 /* Word Program Setup */ +#define ISF_CMD_BPS 0xE8 /* Buffered Program Setup */ +#define ISF_CMD_BPC 0xD0 /* Buffered Program Confirm */ + +/* Erase Mode */ +#define ISF_CMD_BES 0x20 /* Block Erase Setup */ +#define ISF_CMD_BEC 0xD0 /* Block Erase Confirm */ + +/* Block Locking/Unlocking */ +#define ISF_CMD_LBS 0x60 /* Lock Block Setup */ +#define ISF_CMD_LB 0x01 /* Lock Block */ +#define ISF_CMD_UB 0xD0 /* Unlock Block */ + +/* + * Read Device Identifier registers. + * + * NOTE: ISF_RDIR_BLC is relative to the block base address. + */ +#define ISF_REG_MC 0x00 /* Manufacture Code */ +#define ISF_REG_ID 0x01 /* Device ID Code */ +#define ISF_REG_BLC 0x02 /* Block Lock Configuration */ +#define ISF_REG_RCR 0x05 /* Read Configuration Register */ + +/* + * Protection Registers + */ +#define ISF_REG_L0 0x80 /* Lock Register 0 */ +#define ISF_REG_FPP 0x81 /* 64-bit Factory Protection Register */ +#define ISF_REG_UPP 0x85 /* 64-bit User Protection Register */ +#define ISF_REG_L1 0x89 /* Lock Register 1 */ +#define ISF_REG_PP1 0x8A /* 128-bit Protection Register 1 */ +#define ISF_REG_PP2 0x92 /* 128-bit Protection Register 2 */ +#define ISF_REG_PP3 0x9A /* 128-bit Protection Register 3 */ +#define ISF_REG_PP4 0xA2 /* 128-bit Protection Register 4 */ +#define ISF_REG_PP5 0xAA /* 128-bit Protection Register 5 */ +#define ISF_REG_PP6 0xB2 /* 128-bit Protection Register 6 */ +#define ISF_REG_PP7 0xBA /* 128-bit Protection Register 7 */ +#define ISF_REG_PP8 0xC2 /* 128-bit Protection Register 8 */ +#define ISF_REG_PP9 0xCA /* 128-bit Protection Register 9 */ +#define ISF_REG_PP10 0xD2 /* 128-bit Protection Register 10 */ +#define ISF_REG_PP11 0xDA /* 128-bit Protection Register 11 */ +#define ISF_REG_PP12 0xE2 /* 128-bit Protection Register 12 */ +#define ISF_REG_PP13 0xEA /* 128-bit Protection Register 13 */ +#define ISF_REG_PP14 0xF2 /* 128-bit Protection Register 14 */ +#define ISF_REG_PP15 0xFA /* 128-bit Protection Register 15 */ +#define ISF_REG_PP16 0x102 /* 128-bit Protection Register 16 */ + +#define ISF_SR_BWS (1 << 0) /* BEFP Status */ +#define ISF_SR_BLS (1 << 1) /* Block-Locked Status */ +#define ISF_SR_PSS (1 << 2) /* Program Suspend Status */ +#define ISF_SR_VPPS (1 << 3) /* Vpp Status */ +#define ISF_SR_PS (1 << 4) /* Program Status */ +#define ISF_SR_ES (1 << 5) /* Erase Status */ +#define ISF_SR_ESS (1 << 6) /* Erase Suspend Status */ +#define ISF_SR_DWS (1 << 7) /* Device Write Status */ +#define ISF_SR_FSC_MASK (ISF_SR_VPPS | ISF_SR_PS | ISF_SR_BLS) + +#define ISF_BUFFER_PROGRAM + +MALLOC_DEFINE(M_ISF, "isf_data", "Intel StrateFlash driver"); +static int isf_debug = 0; + +static struct isf_chips { + uint16_t chip_id; + size_t chip_size; + const char *chip_desc; +} chip_ids[] = { + { 0x8817, 0x0800000, "64-Mbit Top Parameter" }, + { 0x881A, 0x0800000, "64-Mbit Bottom Parameter" }, + { 0x8818, 0x1000000, "128-Mbit Top Parameter" }, + { 0x881B, 0x1000000, "128-Mbit Bottom Parameter" }, + { 0x8919, 0x2000000, "256-Mbit Top Parameter" }, + { 0x891C, 0x2000000, "256-Mbit Bottom Parameter" }, + { 0x8961, 0x2000000, "512-Mbit package (half)" }, + { 0x0000, 0x0000000, NULL } +}; + +static void isf_task(void *arg); + +/* + * Device driver for the Intel StrataFlash NOR flash device. This + * implementation is known to work with 256Mb instances of the device, but may + * also work with other 64/128/512Mb parts without much work. Multiple + * device instances should be used when multiple parts are in the same + * physical package, due to variable block size support in the StrataFlash + * part. + */ + +static uint16_t +isf_read_reg(struct isf_softc *sc, uint16_t reg) +{ + + if (isf_debug) + device_printf(sc->isf_dev, "isf_read_reg(0x%02x)\n", reg); + return (le16toh(bus_read_2(sc->isf_res, reg * 2))); +} + +static uint64_t +isf_read_reg64(struct isf_softc *sc, uint16_t reg) +{ + uint64_t val; + uint16_t *val16 = (uint16_t *)&val; + + if (isf_debug) + device_printf(sc->isf_dev, "isf_read_reg64(0x%02x)\n", reg); + val16[0] = bus_read_2(sc->isf_res, reg * 2); + val16[1] = bus_read_2(sc->isf_res, (reg+1) * 2); + val16[2] = bus_read_2(sc->isf_res, (reg+2) * 2); + val16[3] = bus_read_2(sc->isf_res, (reg+3) * 2); + + return(le64toh(val)); +} + +static uint16_t +isf_read_off(struct isf_softc *sc, off_t off) +{ + + KASSERT(off >= 0, ("%s: negative offset\n", __func__)); + KASSERT(off < sc->isf_disk->d_mediasize, + ("%s: offset out side address space 0x%08jx \n", __func__, + (intmax_t)off)); + + if (isf_debug) + device_printf(sc->isf_dev, "isf_read_off(0x%08jx)\n", + (intmax_t)off); + return (le16toh(bus_read_2(sc->isf_res, off))); +} + +static void +isf_write_cmd(struct isf_softc *sc, off_t off, uint16_t cmd) +{ + + if (isf_debug) + device_printf(sc->isf_dev, "isf_write_cmd(0x%08jx, 0x%02x)\n", + off, cmd); + bus_write_2(sc->isf_res, off, htole16(cmd)); +} + +static uint16_t +isf_read_status(struct isf_softc *sc, off_t off) +{ + + isf_write_cmd(sc, off/2, ISF_CMD_RSR); + return isf_read_off(sc, off); +} + +static void +isf_clear_status(struct isf_softc *sc) +{ + + isf_write_cmd(sc, 0, ISF_CMD_CSR); +} + +static int +isf_full_status_check(struct isf_softc *sc, off_t off) +{ + int error = 0; + uint16_t status; + + status = isf_read_status(sc, off); + if (status & ISF_SR_VPPS) { + device_printf(sc->isf_dev, "Vpp Range Error\n"); + error = EIO; + } else if (status & ISF_SR_PS) { + device_printf(sc->isf_dev, "Program Error\n"); + error = EIO; + } else if (status & ISF_SR_BLS) { + device_printf(sc->isf_dev, "Device Protect Error\n"); + error = EIO; + } + isf_clear_status(sc); + + return(error); +} + +static int +isf_full_erase_status_check(struct isf_softc *sc, off_t off) +{ + int error = 0; + uint16_t status; + + status = isf_read_status(sc, off); + if (status & ISF_SR_VPPS) { + device_printf(sc->isf_dev, "Vpp Range Error\n"); + error = EIO; + } else if (status & (ISF_SR_PS|ISF_SR_ES)) { + device_printf(sc->isf_dev, "Command Sequence Error\n"); + error = EIO; + } else if (status & ISF_SR_ES) { + device_printf(sc->isf_dev, "Block Erase Error\n"); + error = EIO; + } else if (status & ISF_SR_BLS) { + device_printf(sc->isf_dev, "Block Locked Error\n"); + error = EIO; + } + isf_clear_status(sc); + + return(error); +} + +static void +isf_unlock_block(struct isf_softc *sc, off_t off) +{ + + isf_write_cmd(sc, off, ISF_CMD_LBS); + isf_write_cmd(sc, off, ISF_CMD_UB); + isf_write_cmd(sc, off, ISF_CMD_RA); +} + +static void +isf_lock_block(struct isf_softc *sc, off_t off) +{ + + isf_write_cmd(sc, off, ISF_CMD_LBS); + isf_write_cmd(sc, off, ISF_CMD_LB); + isf_write_cmd(sc, off, ISF_CMD_RA); +} + +static void +isf_read(struct isf_softc *sc, off_t off, void *data, size_t len) +{ + + KASSERT((uintptr_t)data % 2 == 0, + ("%s: unaligned data %p", __func__, data)); + KASSERT((len <= ISF_SECTORSIZE) && (len % 2 == 0), + ("%s: invalid length %ju", __func__, len)); + KASSERT(off % ISF_SECTORSIZE == 0, + ("%s: invalid offset %ju\n", __func__, off)); + + /* + * It is not permitted to read blocks that are in the process of + * being erased, but we know they will be all 1's after the + * erase so just report that value if asked about a block that + * is being erased. + */ + if (sc->isf_bstate[off / ISF_ERASE_BLOCK] == BS_ERASING) + memset(data, 0xFF, len); + else + bus_read_region_2(sc->isf_res, off, (uint16_t *)data, len / 2); +} + +static int +isf_write(struct isf_softc *sc, off_t off, void *data, size_t len) +{ + int cycles, error = 0; + uint16_t *dp; + uint16_t status; + off_t coff; + + KASSERT((uintptr_t)data % 2 == 0, + ("%s: unaligned data %p", __func__, data)); + KASSERT((len <= ISF_SECTORSIZE) && (len % 2 == 0), + ("%s: invalid length %ju", __func__, len)); + KASSERT(off % ISF_SECTORSIZE == 0, + ("%s: invalid offset %ju\n", __func__, off)); + KASSERT(!sc->isf_erasing, + ("%s: trying to write while erasing\n", __func__)); + KASSERT(sc->isf_bstate[off / ISF_ERASE_BLOCK] != BS_ERASING, + ("%s: block being erased at %ju\n", __func__, off)); + + isf_unlock_block(sc, off); + +#ifdef ISF_BUFFER_PROGRAM + for (dp = data, coff = off; dp - (uint16_t *)data < len / 2; + dp += 32, coff += 64) { + isf_clear_status(sc); + isf_write_cmd(sc, coff, ISF_CMD_BPS); + cycles = 0xFFFF; + while ( !(isf_read_off(sc, coff) & ISF_SR_DWS) ) { + if (cycles-- == 0) { + device_printf(sc->isf_dev, "timeout waiting" + " for write to start at 0x08%jx\n", + (intmax_t)coff); + return (EIO); + } + isf_write_cmd(sc, coff, ISF_CMD_BPS); + } + + /* When writing N blocks, send N-1 as the count */ + isf_write_cmd(sc, coff, 31); + bus_write_region_2(sc->isf_res, coff, dp, 32); + + isf_write_cmd(sc, coff, ISF_CMD_BPC); + + status = isf_read_off(sc, coff); + cycles = 0xFFFFF; + while ( !(status & ISF_SR_DWS) ) { + if (cycles-- == 0) { + device_printf(sc->isf_dev, "timeout waiting" + " for write to complete at 0x08%jx\n", + (intmax_t)coff); + error = EIO; + break; + } + status = isf_read_off(sc, coff); + } + isf_full_status_check(sc, off); + + isf_write_cmd(sc, coff, ISF_CMD_RA); + } +#else + for (dp = data, coff = off; dp - (uint16_t *)data < len / 2; + dp++, coff += 2) { + isf_write_cmd(sc, coff, ISF_CMD_WPS); + bus_write_2(sc->isf_res, coff, *dp); + status = isf_read_off(sc, coff); + cycles=0xFFFFF; + while ( !(status & ISF_SR_DWS) ) { + if (cycles-- == 0) { + device_printf(sc->isf_dev, "timeout waiting" + " for write to complete at 0x08%jx\n", + (intmax_t)coff); + error = EIO; + break; + } + status = isf_read_off(sc, coff); + } + + } + isf_full_status_check(sc, off); + isf_write_cmd(sc, coff, ISF_CMD_RA); +#endif + + isf_lock_block(sc, off); + + return error; +} + +static void +isf_erase_at(struct isf_softc *sc, off_t off) +{ + int cycles; + uint16_t status; + + isf_unlock_block(sc, off); + isf_clear_status(sc); + + isf_write_cmd(sc, off, ISF_CMD_BES); + isf_write_cmd(sc, off, ISF_CMD_BEC); + + cycles=0xFFFFFF; + status = isf_read_off(sc, off); + while ( !(status & ISF_SR_DWS) ) { +#ifdef NOTYET + ISF_SLEEP(sc, sc, hz); +#endif + if (cycles-- == 0) { + device_printf(sc->isf_dev, + "Giving up on erase\n"); + break; + } + status = isf_read_off(sc, off); + } + + isf_full_erase_status_check(sc, off); + + isf_lock_block(sc, off); + + isf_write_cmd(sc, off, ISF_CMD_RA); +} + +static void +isf_erase_range(struct isf_softc *sc, off_t blk_off, size_t size) +{ + off_t off; + off_t ms = sc->isf_disk->d_mediasize; + + KASSERT(blk_off % ISF_ERASE_BLOCK == 0, + ("%s: invalid offset %ju\n", __func__, blk_off)); + + ISF_LOCK_ASSERT(sc); + + for (off = blk_off; off < blk_off + size; off += ISF_ERASE_BLOCK) { + sc->isf_bstate[off / ISF_ERASE_BLOCK] = BS_ERASING; + + /* + * The first or last 128K is four blocks depending which + * part this is. For now, just assume both are and + * erase four times. + */ + if (off == 0 || ms - off == ISF_ERASE_BLOCK) { + isf_erase_at(sc, off); + isf_erase_at(sc, off + 0x08000); + isf_erase_at(sc, off + 0x10000); + isf_erase_at(sc, off + 0x18000); + } else + isf_erase_at(sc, off); + + sc->isf_bstate[off / ISF_ERASE_BLOCK] = BS_STEADY; + } +} + +/* + * disk(9) methods. + */ +static int +isf_disk_ioctl(struct disk *disk, u_long cmd, void *data, int fflag, + struct thread *td) +{ + int error = 0; + struct isf_softc *sc = disk->d_drv1; + struct isf_range *ir; + + switch (cmd) { + case ISF_ERASE: + ir = data; + if (ir->ir_off % ISF_ERASE_BLOCK != 0 || + ir->ir_off >= disk->d_mediasize || + ir->ir_size == 0 || + ir->ir_size % ISF_ERASE_BLOCK != 0 || + ir->ir_off + ir->ir_size > disk->d_mediasize) { + error = EINVAL; + break; + } + ISF_LOCK(sc); + if (sc->isf_erasing) { + ISF_UNLOCK(sc); + error = EBUSY; + break; + } + sc->isf_erasing = 1; + isf_erase_range(sc, ir->ir_off, ir->ir_size); + sc->isf_erasing = 0; + ISF_UNLOCK(sc); + break; + default: + error = EINVAL; + } + + return (error); +} + +static void +isf_disk_strategy(struct bio *bp) +{ + struct isf_softc *sc = bp->bio_disk->d_drv1;; + + /* + * We advertise a block size and maximum I/O size up the stack; catch + * any attempts to not follow the rules. + */ + KASSERT(bp->bio_bcount == ISF_SECTORSIZE, + ("%s: I/O size not %d", __func__, ISF_SECTORSIZE)); + + ISF_LOCK(sc); + bioq_disksort(&sc->isf_bioq, bp); + ISF_WAKEUP(sc); + ISF_UNLOCK(sc); +} + +static void +isf_task(void *arg) +{ + struct isf_softc *sc = arg; + struct bio *bp; + int ss = sc->isf_disk->d_sectorsize; + int error, i; + + for (;;) { + ISF_LOCK(sc); + do { + bp = bioq_first(&sc->isf_bioq); + if (bp == NULL) { + if (sc->isf_doomed) + kproc_exit(0); + else + ISF_SLEEP(sc, sc, 0); + } + } while (bp == NULL); + bioq_remove(&sc->isf_bioq, bp); + + error = 0; + switch (bp->bio_cmd) { + case BIO_READ: + isf_read(sc, bp->bio_pblkno * ss, bp->bio_data, + bp->bio_bcount); + break; + + case BIO_WRITE: + /* + * In principle one could suspend the in-progress + * erase, process any pending writes to other + * blocks and then proceed, but that seems + * overly complex for the likely usage modes. + */ + if (sc->isf_erasing) { + error = EBUSY; + break; + } + + /* + * Read in the block we want to write and check that + * we're only setting bits to 0. If an erase would + * be required return an I/O error. + */ + isf_read(sc, bp->bio_pblkno * ss, sc->isf_rbuf, + bp->bio_bcount); + for (i = 0; i < bp->bio_bcount / 2; i++) + if ((sc->isf_rbuf[i] & + ((uint16_t *)bp->bio_data)[i]) != + ((uint16_t *)bp->bio_data)[i]) { + device_printf(sc->isf_dev, "write" + " requires erase at 0x%08jx\n", + bp->bio_pblkno * ss); + error = EIO; + break; + } + if (error != 0) + break; + + error = isf_write(sc, bp->bio_pblkno * ss, + bp->bio_data, bp->bio_bcount); + break; + + default: + panic("%s: unsupported I/O operation %d", __func__, + bp->bio_cmd); + } + if (error == 0) + biodone(bp); + else + biofinish(bp, NULL, error); + ISF_UNLOCK(sc); + } +} + +static void +isf_dump_info(struct isf_softc *sc) +{ + int i; + int32_t reg; + + isf_write_cmd(sc, 0, ISF_CMD_RDI); + device_printf(sc->isf_dev, "manufacturer code: 0x%04x\n", + isf_read_reg(sc, ISF_REG_MC)); + device_printf(sc->isf_dev, "device id code: 0x%04x\n", + isf_read_reg(sc, ISF_REG_ID)); + device_printf(sc->isf_dev, "read config register: 0x%04x\n", + isf_read_reg(sc, ISF_REG_RCR)); + + device_printf(sc->isf_dev, "lock register 0: 0x%04x\n", + isf_read_reg(sc, ISF_REG_L0)); + device_printf(sc->isf_dev, "lock register 1: 0x%04x\n", + isf_read_reg(sc, ISF_REG_L1)); + + device_printf(sc->isf_dev, "factory PPR: 0x%016jx\n", + (uintmax_t)isf_read_reg64(sc, ISF_REG_FPP)); + device_printf(sc->isf_dev, "user PPR (64-bit): 0x%016jx\n", + (uintmax_t)isf_read_reg64(sc, ISF_REG_UPP)); + + for (reg = ISF_REG_PP1, i = 1; reg <= ISF_REG_PP16; reg += 8, i++) { + /* XXX: big-endian ordering of uint64_t's */ + device_printf(sc->isf_dev, + "user PPR [%02d]: 0x%016jx%016jx\n", i, + (uintmax_t)isf_read_reg64(sc, reg+4), + (uintmax_t)isf_read_reg64(sc, reg)); + } + + isf_write_cmd(sc, 0, ISF_CMD_RA); +} + +static void +isf_disk_insert(struct isf_softc *sc, off_t mediasize) +{ + struct disk *disk; + + sc->isf_doomed = 0; + sc->isf_erasing = 0; + sc->isf_bstate = malloc(sizeof(*sc->isf_bstate) * + (mediasize / ISF_ERASE_BLOCK), M_ISF, M_ZERO | M_WAITOK); + kproc_create(&isf_task, sc, &sc->isf_proc, 0, 0, "isf"); + + disk = disk_alloc(); + disk->d_drv1 = sc; + disk->d_name = "isf"; + disk->d_unit = sc->isf_unit; + disk->d_strategy = isf_disk_strategy; + disk->d_ioctl = isf_disk_ioctl; + disk->d_sectorsize = ISF_SECTORSIZE; + disk->d_mediasize = mediasize; + disk->d_maxsize = ISF_SECTORSIZE; + sc->isf_disk = disk; + + if (bootverbose) + isf_dump_info(sc); + + disk_create(disk, DISK_VERSION); + device_printf(sc->isf_dev, "%juM flash device\n", + (uintmax_t)disk->d_mediasize / (1024 * 1024)); + +} + +static void +isf_disk_remove(struct isf_softc *sc) +{ + struct disk *disk; + + ISF_LOCK_ASSERT(sc); + KASSERT(sc->isf_disk != NULL, ("%s: isf_disk NULL", __func__)); + + sc->isf_doomed = 1; + ISF_WAKEUP(sc); + ISF_SLEEP(sc, sc->isf_proc, 0); + + /* + * XXXRW: Is it OK to call disk_destroy() under the mutex, or should + * we be deferring that to the calling context once it is released? + */ + disk = sc->isf_disk; + disk_gone(disk); + disk_destroy(disk); + sc->isf_disk = NULL; + free(sc->isf_bstate, M_ISF); + device_printf(sc->isf_dev, "flash device removed\n"); +} + +int +isf_attach(struct isf_softc *sc) +{ + uint16_t id; + u_long start, size; + struct isf_chips *cp = chip_ids; + + start = rman_get_start(sc->isf_res); + if (start % 2 != 0) { + device_printf(sc->isf_dev, + "Unsupported flash start alignment %lu\n", + start); + return (ENXIO); + } + + isf_write_cmd(sc, 0, ISF_CMD_RDI); + id = isf_read_reg(sc, ISF_REG_ID); + while (cp->chip_id != id) + cp++; + if (cp->chip_desc == NULL) { + device_printf(sc->isf_dev, + "Unsupported device ID 0x%04x\n", id); + return (ENXIO); + } + isf_write_cmd(sc, 0, ISF_CMD_RA); + + size = rman_get_size(sc->isf_res); + if (size != cp->chip_size) { + device_printf(sc->isf_dev, + "Unsupported flash size %lu\n", size); + return (ENXIO); + } + + bioq_init(&sc->isf_bioq); + ISF_LOCK_INIT(sc); + sc->isf_disk = NULL; + isf_disk_insert(sc, size); + return(0); +} + +void +isf_detach(struct isf_softc *sc) +{ + + /* + * Simulate a disk removal if one is present to deal with any pending + * or queued I/O. This will occur as a result of a device driver + * detach -- the Intel StrataFlash has no notion of removal itself. + * + * XXXRW: Is the locking here right? + */ + ISF_LOCK(sc); + isf_disk_remove(sc); + bioq_flush(&sc->isf_bioq, NULL, ENXIO); + KASSERT(bioq_first(&sc->isf_bioq) == NULL, + ("%s: non-empty bioq", __func__)); + ISF_UNLOCK(sc); + ISF_LOCK_DESTROY(sc); +} diff --git a/sys/dev/isf/isf.h b/sys/dev/isf/isf.h new file mode 100644 index 0000000000000..f5cdc08fc31e0 --- /dev/null +++ b/sys/dev/isf/isf.h @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2012 Robert N. M. Watson + * Copyright (c) 2012 SRI International + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_ISF_H_ +#define _DEV_ISF_H_ + +struct isf_range { + off_t ir_off; /* Offset of range to delete (set to 0xFF) */ + size_t ir_size; /* Size of range */ +}; + +#define ISF_ERASE _IOW('I', 1, struct isf_range) + +/* + * Ordinary read and write operations are limited to 512 bytes. + * We support erasing 128K blocks and ignore the fact that portions of the + * flash are in fact divided into 32K blocks. + */ +#define ISF_SECTORSIZE (512) +#define ISF_ERASE_BLOCK (128 * 1024) + +#ifdef _KERNEL +MALLOC_DECLARE(M_ISF); + +enum bstate { + BS_STEADY = 0, + BS_ERASING +}; + +struct isf_softc { + device_t isf_dev; + int isf_unit; + struct resource *isf_res; + int isf_rid; + struct mtx isf_lock; + struct disk *isf_disk; + struct proc *isf_proc; + int isf_doomed; + + /* + * Fields relating to in-progress and pending I/O, if any. + */ + struct bio_queue_head isf_bioq; + uint16_t isf_rbuf[ISF_SECTORSIZE / 2]; + int isf_erasing; + enum bstate *isf_bstate; +}; + +#define ISF_LOCK(sc) mtx_lock(&(sc)->isf_lock) +#define ISF_LOCK_ASSERT(sc) mtx_assert(&(sc)->isf_lock, MA_OWNED) +#define ISF_LOCK_DESTROY(sc) mtx_destroy(&(sc)->isf_lock) +#define ISF_LOCK_INIT(sc) mtx_init(&(sc)->isf_lock, "isf", NULL, \ + MTX_DEF) +#define ISF_SLEEP(sc, wait, timo) mtx_sleep((wait), \ + &(sc)->isf_lock, PRIBIO, \ + "isf", (timo)) +#define ISF_UNLOCK(sc) mtx_unlock(&(sc)->isf_lock) +#define ISF_WAKEUP(sc) wakeup((sc)) + +int isf_attach(struct isf_softc *sc); +void isf_detach(struct isf_softc *sc); +#endif /* _KERNEL */ + +#endif /* _DEV_ISF_H_ */ diff --git a/sys/dev/isf/isf_nexus.c b/sys/dev/isf/isf_nexus.c new file mode 100644 index 0000000000000..af2f874411c36 --- /dev/null +++ b/sys/dev/isf/isf_nexus.c @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 2012 Robert N. M. Watson + * Copyright (c) 2012 SRI International + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* + * Nexus bus attachment for the Intel Strata Flash devices. Appropriate for + * most Altera FPGA SoC-style configurations in which the part will be exposed + * to the processor via a memory-mapped Avalon bus. + */ +static int +isf_nexus_probe(device_t dev) +{ + + device_set_desc(dev, "Intel StrataFlash NOR flash device"); + return (BUS_PROBE_DEFAULT); +} + +static int +isf_nexus_attach(device_t dev) +{ + int error; + struct isf_softc *sc; + + sc = device_get_softc(dev); + sc->isf_dev = dev; + sc->isf_unit = device_get_unit(dev); + sc->isf_rid = 0; + sc->isf_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->isf_rid, RF_ACTIVE); + if (sc->isf_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + return (ENXIO); + } + error = isf_attach(sc); + if (error) + bus_release_resource(dev, SYS_RES_MEMORY, sc->isf_rid, + sc->isf_res); + return (error); +} + +static int +isf_nexus_detach(device_t dev) +{ + struct isf_softc *sc; + + sc = device_get_softc(dev); + KASSERT(sc->isf_res != NULL, ("%s: resources not allocated", + __func__)); + isf_detach(sc); + bus_release_resource(dev, SYS_RES_MEMORY, sc->isf_rid, sc->isf_res); + return (0); +} + +static device_method_t isf_nexus_methods[] = { + DEVMETHOD(device_probe, isf_nexus_probe), + DEVMETHOD(device_attach, isf_nexus_attach), + DEVMETHOD(device_detach, isf_nexus_detach), + { 0, 0 } +}; + +static driver_t isf_nexus_driver = { + "isf", + isf_nexus_methods, + sizeof(struct isf_softc), +}; + +static devclass_t isf_devclass; + +DRIVER_MODULE(isf, nexus, isf_nexus_driver, isf_devclass, 0, 0); diff --git a/sys/mips/conf/BERI_DE4.hints b/sys/mips/conf/BERI_DE4.hints index 319d0c2aa1489..4ffe6c2f95727 100644 --- a/sys/mips/conf/BERI_DE4.hints +++ b/sys/mips/conf/BERI_DE4.hints @@ -46,6 +46,16 @@ hint.altera_avgen.0.devname="berirom" #hint.altera_avgen.0.mmapio="rwx" #hint.altera_avgen.0.devname="de4flash" +# +# General Intel StrataFlash driver +# +hint.isf.0.at="nexus0" +hint.isf.0.maddr=0x74000000 +hint.isf.0.msize=0x2000000 +hint.isf.1.at="nexus0" +hint.isf.1.maddr=0x76000000 +hint.isf.1.msize=0x2000000 + # Reserved configuration blocks. Don't touch. hint.map.0.at="isf0" hint.map.0.start=0x00000000 diff --git a/sys/mips/conf/BERI_DE4_MDROOT b/sys/mips/conf/BERI_DE4_MDROOT index 8903179a220fa..658c925f78c7a 100644 --- a/sys/mips/conf/BERI_DE4_MDROOT +++ b/sys/mips/conf/BERI_DE4_MDROOT @@ -25,4 +25,5 @@ device altera_avgen device altera_jtag_uart device altera_sdcard +device isf #device sc diff --git a/sys/mips/conf/BERI_DE4_SDROOT b/sys/mips/conf/BERI_DE4_SDROOT index dcd177b4ae090..b41e6ffca9d6d 100644 --- a/sys/mips/conf/BERI_DE4_SDROOT +++ b/sys/mips/conf/BERI_DE4_SDROOT @@ -18,4 +18,5 @@ device altera_avgen device altera_jtag_uart device altera_sdcard +device isf #device sc diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 95eb7b8f0d736..19c19c9631c3f 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -35,6 +35,7 @@ SUBDIR= adduser \ ifmcstat \ inetd \ iostat \ + isfctl \ kldxref \ mailwrapper \ makefs \ diff --git a/usr.sbin/isfctl/Makefile b/usr.sbin/isfctl/Makefile new file mode 100644 index 0000000000000..30f8e5b800d7a --- /dev/null +++ b/usr.sbin/isfctl/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= isfctl + +MAN= isfctl.8 + +.include diff --git a/usr.sbin/isfctl/isfctl.8 b/usr.sbin/isfctl/isfctl.8 new file mode 100644 index 0000000000000..06b2f3a1a25a4 --- /dev/null +++ b/usr.sbin/isfctl/isfctl.8 @@ -0,0 +1,89 @@ +.\"- +.\" Copyright (c) 2012 SRI International +.\" All rights reserved. +.\" +.\" This software was developed by SRI International and the University of +.\" Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) +.\" ("CTSRD"), as part of the DARPA CRASH research programme. +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd August 3, 2012 +.Dt ISFCTL 8 +.Os +.Sh NAME +.Nm isfctl +.Nd Intel StrataFlash device control program +.Sh SYNOPSIS +.Nm +.Ar device +.Ic erase +.Ar offset +.Ar size +.Sh DESCRIPTION +The +.Nm +utility provides a way for administrators to control aspects of +.Xr isf 4 +devices that can not be managed though the +.Xr disk 9 +interface. +.Pp +The +.Nm +utility takes a device name as its first argument followed by a command. +Currently supported commands are: +.Bl -tag -width erase +.It Ic erase +Erase blocks beginning at +.Ar offset +covering a total of +.Ar size +bytes. +The +.Ar offset +argument must be a multiple of 128K. +The +.Ar size +argument must either be a multiple of 128K or a number less than 32K in +which case it is treated as a number of 128K blocks to erase. +.El +.Sh EXAMPLES +.Dl isfctl isf0 erase 0 0x20000 +.Pp +Erase the first block of the isf0 device. +.Sh SEE ALSO +.Xr isf 4 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 10.0 . +.Sh AUTHORS +This software and this manual page were +developed by SRI International and the University of Cambridge Computer +Laboratory under DARPA/AFRL contract +.Pq FA8750-10-C-0237 +.Pq Do CTSRD Dc , +as part of the DARPA CRASH research programme. diff --git a/usr.sbin/isfctl/isfctl.c b/usr.sbin/isfctl/isfctl.c new file mode 100644 index 0000000000000..af99093e19a7c --- /dev/null +++ b/usr.sbin/isfctl/isfctl.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2012 SRI International + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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. + * + * $FreeBSD$ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* XXXBED: should install and include sys/dev/isf.h */ +struct isf_range { + off_t ir_off; /* Offset of range to delete (set to 0xFF) */ + size_t ir_size; /* Size of range */ +}; + +#define ISF_ERASE _IOW('I', 1, struct isf_range) + +#define ISF_ERASE_BLOCK (128 * 1024) + +static enum {UNSET, ERASE} action = UNSET; + +static void +usage(void) +{ + fprintf(stderr, "usage: isfctl erase \n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + struct isf_range ir; + int fd, i; + char *p, *dev; + + if (argc < 2) + usage(); + argc--; argv++; + + if (*argv[0] == '/') + dev = argv[0]; + else + asprintf(&dev, "/dev/%s", argv[0]); + argc--; argv++; + fd = open(dev, O_RDWR); + if (fd < 0) + err(1, "unable to open device -- %s", dev); + + if (strcmp(argv[0], "erase") == 0) { + if (argc != 3) + usage(); + action = ERASE; + ir.ir_off = strtol(argv[1], &p, 0); + if (*p) + errx(1, "invalid offset -- %s", argv[2]); + ir.ir_size = strtol(argv[2], &p, 0); + if (*p) + errx(1, "invalid size -- %s", argv[3]); + /* + * If the user requests to delete less than 32K of space + * then assume that they want to delete a number of 128K + * blocks. + */ + if (ir.ir_size < 32 * 1024) + ir.ir_size *= 128 * 1024; + } + + switch (action) { + case ERASE: + i = ioctl(fd, ISF_ERASE, &ir); + if (i < 0) + err(1, "ioctl(%s, %jx, %zx)", dev, + (intmax_t)ir.ir_off, ir.ir_size); + break; + default: + usage(); + } + + close(fd); + return (0); +} -- cgit v1.3 From b1fcaf5f95107aebf2ec6152633460399f11808d Mon Sep 17 00:00:00 2001 From: Roman Divacky Date: Mon, 27 Aug 2012 14:51:26 +0000 Subject: Dont cast from char* to struct chrp_header* which has a bigger alignment requirements. Copy it via union instead. Fixes a clang warning about alignment. Reviewed by: sobomax --- usr.sbin/nvram/nvram.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/nvram/nvram.c b/usr.sbin/nvram/nvram.c index 9d56f9d537b1c..a286ee3e60ea9 100644 --- a/usr.sbin/nvram/nvram.c +++ b/usr.sbin/nvram/nvram.c @@ -51,12 +51,16 @@ struct deletelist { struct deletelist *last; }; +union { + uint8_t buf[sizeof(struct chrp_header)]; + struct chrp_header header; +} conv; + int main(int argc, char **argv) { int opt, dump, fd, res, i, size; uint8_t buf[NVRAM_SIZE], *cp, *common; - struct chrp_header *header; struct deletelist *dl; dump = 0; @@ -116,9 +120,9 @@ main(int argc, char **argv) /* Locate common block */ size = 0; for (cp = buf; cp < buf + sizeof(buf); cp += size) { - header = (struct chrp_header *)cp; - size = header->length * 0x10; - if (strncmp(header->name, "common", 7) == 0) + memcpy(conv.buf, cp, sizeof(struct chrp_header)); + size = conv.header.length * 0x10; + if (strncmp(conv.header.name, "common", 7) == 0) break; } if (cp >= buf + sizeof(buf) || size <= (int)sizeof(struct chrp_header)) -- cgit v1.3 From 7790916244e0382ba65fb179e7eb9ddc576eb5bb Mon Sep 17 00:00:00 2001 From: Xin LI Date: Mon, 27 Aug 2012 19:08:47 +0000 Subject: Show error messages if nmount() failed. MFC after: 1 month --- usr.sbin/mountd/mountd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c index 1e56e10cc850d..ffc0a09e6d715 100644 --- a/usr.sbin/mountd/mountd.c +++ b/usr.sbin/mountd/mountd.c @@ -2461,11 +2461,11 @@ do_mount(struct exportlist *ep, struct grouplist *grp, int exflags, } if (errno == EPERM) { if (debug) - warnx("can't change attributes for %s", - dirp); + warnx("can't change attributes for %s: %s", + dirp, errmsg); syslog(LOG_ERR, - "can't change attributes for %s", - dirp); + "can't change attributes for %s: %s", + dirp, errmsg); ret = 1; goto error_exit; } -- cgit v1.3 From e489ac6c53db8c961e3a9889d7afe8719b1bc627 Mon Sep 17 00:00:00 2001 From: Andrey Zonov Date: Tue, 28 Aug 2012 08:38:53 +0000 Subject: - Don't allow watchdogd(8) to be swapped out. On machines with huge amount of swap and high IO activity, watchdogd(8) may wait for a swap memory longer than timeout and sometimes fires. Approved by: kib (mentor) MFC after: 1 week --- usr.sbin/watchdogd/watchdogd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c index 7bae4ffc3ee4a..f0896a92f329e 100644 --- a/usr.sbin/watchdogd/watchdogd.c +++ b/usr.sbin/watchdogd/watchdogd.c @@ -118,6 +118,8 @@ main(int argc, char *argv[]) pidfile_write(pfh); if (madvise(0, 0, MADV_PROTECT) != 0) warn("madvise failed"); + if (mlockall(MCL_FUTURE) != 0) + warn("mlockall failed"); watchdog_loop(); -- cgit v1.3 From 57bc16315ff247ee70223c59f3caf9f5d5920bb1 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Wed, 29 Aug 2012 16:15:22 +0000 Subject: Fix whitespace. --- usr.sbin/ac/ac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c index 5dc827165bd49..bcbf53474f7b4 100644 --- a/usr.sbin/ac/ac.c +++ b/usr.sbin/ac/ac.c @@ -60,7 +60,7 @@ struct tty_list { * globals - yes yuk */ #ifdef CONSOLE_TTY -static char *Console = CONSOLE_TTY; +static char *Console = CONSOLE_TTY; #endif static time_t Total = 0; static time_t FirstTime = 0; @@ -406,7 +406,6 @@ log_out(struct utmp_list *head, struct utmpx *up) return head; } - /* * if do_tty says ok, login a user */ -- cgit v1.3 From 193e2b55462f518c2027427195e6eed5735cd357 Mon Sep 17 00:00:00 2001 From: Andrey Zonov Date: Thu, 30 Aug 2012 08:07:37 +0000 Subject: - It's also need to lock current memory. Approved by: kib (mentor) MFC after: 1 week --- usr.sbin/watchdogd/watchdogd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c index f0896a92f329e..cce84e1d9a1ef 100644 --- a/usr.sbin/watchdogd/watchdogd.c +++ b/usr.sbin/watchdogd/watchdogd.c @@ -118,7 +118,7 @@ main(int argc, char *argv[]) pidfile_write(pfh); if (madvise(0, 0, MADV_PROTECT) != 0) warn("madvise failed"); - if (mlockall(MCL_FUTURE) != 0) + if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) warn("mlockall failed"); watchdog_loop(); -- cgit v1.3 From b42c08d3c4e60c9104118690267238aff9286d9b Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Thu, 30 Aug 2012 16:45:27 +0000 Subject: Massively refactor ac(8). - Use queue(3) -- not some homegrown implementation of linked lists. - Rename structures to _entry, as they are entries in the linked list -- not the lists themselves. - Don't store entire copies of struct utmpx in utmpx_entry, but only the members we're interested in. Large fields such as hostnames are not needed during the execution of the program. - Give structure members useful names, instead of `name'. - While there, use struct timevals instead of time_t's internally. This is not strictly useful, but while we're at it... - Mark stuff static. - Add missing const keywords. - Remove unneeded prototypes. - Remove workaround for sparc64-specific utmp problems. These don't apply to utmpx. - Don't discard entries when timestamps are not monotone. This shouldn't ever happen with utmpx, but discarding them is a bit too harsh. - Remove debug code. We nowadays have `getent utmpx', which can be used to analyze logfiles in depth. - Use proper uppercasing/periods in comments. - Print output of `ac -p' sorted alphabetically, instead of first occurrence. - Properly check against pts/* instead of tty[PQRSpqrs]* to determine whether a TTY is a pseudo-terminal. MFC after: 1 month --- usr.sbin/ac/Makefile | 5 - usr.sbin/ac/ac.c | 500 +++++++++++++++++++-------------------------------- 2 files changed, 189 insertions(+), 316 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ac/Makefile b/usr.sbin/ac/Makefile index 576dbb3323ce1..0fac2fbdbab99 100644 --- a/usr.sbin/ac/Makefile +++ b/usr.sbin/ac/Makefile @@ -3,11 +3,6 @@ PROG= ac MAN= ac.8 -# Temporary, while tracking down problem wrt 64-bit time_t's on sparc64 -.if ${MACHINE_CPUARCH} == "sparc64" -CFLAGS+=-DDEBUG -.endif - # If "CONSOLE_TTY" is not defined, this program is compatible with the # traditional implementation (using SunOS 4.x as the sample traditional # implementation). This is the default. diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c index bcbf53474f7b4..1e2c54b86f48b 100644 --- a/usr.sbin/ac/ac.c +++ b/usr.sbin/ac/ac.c @@ -16,8 +16,9 @@ #include __FBSDID("$FreeBSD$"); -#include +#include #include + #include #include #include @@ -32,43 +33,47 @@ __FBSDID("$FreeBSD$"); /* * this is for our list of currently logged in sessions */ -struct utmp_list { - struct utmp_list *next; - struct utmpx usr; +struct utmpx_entry { + SLIST_ENTRY(utmpx_entry) next; + char user[sizeof(((struct utmpx *)0)->ut_user)]; + char id[sizeof(((struct utmpx *)0)->ut_id)]; +#ifdef CONSOLE_TTY + char line[sizeof(((struct utmpx *)0)->ut_line)]; +#endif + struct timeval time; }; /* * this is for our list of users that are accumulating time. */ -struct user_list { - struct user_list *next; - char name[sizeof(((struct utmpx *)0)->ut_user)]; - time_t secs; +struct user_entry { + SLIST_ENTRY(user_entry) next; + char user[sizeof(((struct utmpx *)0)->ut_user)]; + struct timeval time; }; /* * this is for chosing whether to ignore a login */ -struct tty_list { - struct tty_list *next; - char name[sizeof(((struct utmpx *)0)->ut_host) + 2]; - size_t len; - int ret; +struct tty_entry { + SLIST_ENTRY(tty_entry) next; + char line[sizeof(((struct utmpx *)0)->ut_line) + 2]; + size_t len; + int ret; }; /* * globals - yes yuk */ #ifdef CONSOLE_TTY -static char *Console = CONSOLE_TTY; +static const char *Console = CONSOLE_TTY; #endif -static time_t Total = 0; -static time_t FirstTime = 0; +static struct timeval Total = { 0, 0 }; +static struct timeval FirstTime = { 0, 0 }; static int Flags = 0; -static struct user_list *Users = NULL; -static struct tty_list *Ttys = NULL; - -#define NEW(type) (type *)malloc(sizeof (type)) +static SLIST_HEAD(, utmpx_entry) CurUtmpx = SLIST_HEAD_INITIALIZER(CurUtmpx); +static SLIST_HEAD(, user_entry) Users = SLIST_HEAD_INITIALIZER(Users); +static SLIST_HEAD(, tty_entry) Ttys = SLIST_HEAD_INITIALIZER(Ttys); #define AC_W 1 /* not _PATH_WTMP */ #define AC_D 2 /* daily totals (ignore -p) */ @@ -76,182 +81,112 @@ static struct tty_list *Ttys = NULL; #define AC_U 8 /* specified users only */ #define AC_T 16 /* specified ttys only */ -#ifdef DEBUG -static int Debug = 0; -#endif +static void ac(const char *); +static void usage(void); -int main(int, char **); -int ac(const char *); -struct tty_list *add_tty(char *); -#ifdef DEBUG -const char *debug_pfx(const struct utmpx *, const struct utmpx *); -#endif -int do_tty(char *); -struct utmp_list *log_in(struct utmp_list *, struct utmpx *); -struct utmp_list *log_out(struct utmp_list *, struct utmpx *); -int on_console(struct utmp_list *); -void show(const char *, time_t); -void show_today(struct user_list *, struct utmp_list *, - time_t); -void show_users(struct user_list *); -struct user_list *update_user(struct user_list *, char *, time_t); -void usage(void); - -struct tty_list * -add_tty(char *name) +static void +add_tty(const char *line) { - struct tty_list *tp; + struct tty_entry *tp; char *rcp; Flags |= AC_T; - if ((tp = NEW(struct tty_list)) == NULL) + if ((tp = malloc(sizeof(*tp))) == NULL) errx(1, "malloc failed"); tp->len = 0; /* full match */ tp->ret = 1; /* do if match */ - if (*name == '!') { /* don't do if match */ + if (*line == '!') { /* don't do if match */ tp->ret = 0; - name++; + line++; } - strlcpy(tp->name, name, sizeof (tp->name)); - if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */ + strlcpy(tp->line, line, sizeof(tp->line)); + /* Wildcard. */ + if ((rcp = strchr(tp->line, '*')) != NULL) { *rcp = '\0'; - tp->len = strlen(tp->name); /* match len bytes only */ + /* Match len bytes only. */ + tp->len = strlen(tp->line); } - tp->next = Ttys; - Ttys = tp; - return Ttys; + SLIST_INSERT_HEAD(&Ttys, tp, next); } /* * should we process the named tty? */ -int -do_tty(char *name) +static int +do_tty(const char *line) { - struct tty_list *tp; + struct tty_entry *tp; int def_ret = 0; - for (tp = Ttys; tp != NULL; tp = tp->next) { + SLIST_FOREACH(tp, &Ttys, next) { if (tp->ret == 0) /* specific don't */ def_ret = 1; /* default do */ if (tp->len != 0) { - if (strncmp(name, tp->name, tp->len) == 0) + if (strncmp(line, tp->line, tp->len) == 0) return tp->ret; } else { - if (strncmp(name, tp->name, sizeof (tp->name)) == 0) + if (strncmp(line, tp->line, sizeof(tp->line)) == 0) return tp->ret; } } - return def_ret; + return (def_ret); } #ifdef CONSOLE_TTY /* * is someone logged in on Console? */ -int -on_console(struct utmp_list *head) +static int +on_console(void) { - struct utmp_list *up; + struct utmpx_entry *up; - for (up = head; up; up = up->next) { - if (strcmp(up->usr.ut_line, Console) == 0) - return 1; - } - return 0; + SLIST_FOREACH(up, &CurUtmpx, next) + if (strcmp(up->line, Console) == 0) + return (1); + return (0); } #endif /* - * update user's login time + * Update user's login time. + * If no entry for this user is found, a new entry is inserted into the + * list alphabetically. */ -struct user_list * -update_user(struct user_list *head, char *name, time_t secs) +static void +update_user(const char *user, struct timeval secs) { - struct user_list *up; + struct user_entry *up, *aup; + int c; - for (up = head; up != NULL; up = up->next) { - if (strcmp(up->name, name) == 0) { - up->secs += secs; - Total += secs; - return head; - } + aup = NULL; + SLIST_FOREACH(up, &Users, next) { + c = strcmp(up->user, user); + if (c == 0) { + timeradd(&up->time, &secs, &up->time); + timeradd(&Total, &secs, &Total); + return; + } else if (c > 0) + break; + aup = up; } /* * not found so add new user unless specified users only */ if (Flags & AC_U) - return head; + return; - if ((up = NEW(struct user_list)) == NULL) + if ((up = malloc(sizeof(*up))) == NULL) errx(1, "malloc failed"); - up->next = head; - strlcpy(up->name, name, sizeof (up->name)); - up->secs = secs; - Total += secs; - return up; -} - -#ifdef DEBUG -/* - * Create a string which is the standard prefix for a debug line. It - * includes a timestamp (perhaps with year), device-name, and user-name. - */ -const char * -debug_pfx(const struct utmpx *event_up, const struct utmpx *userinf_up) -{ - static char str_result[40 + sizeof(userinf_up->ut_line) + - sizeof(userinf_up->ut_user)]; - static char thisyear[5]; - size_t maxcopy; - time_t ut_timecopy; - - if (thisyear[0] == '\0') { - /* Figure out what "this year" is. */ - time(&ut_timecopy); - strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result)); - strlcpy(thisyear, &str_result[20], sizeof(thisyear)); - } - - if (event_up->ut_tv.tv_sec == 0) - strlcpy(str_result, "*ZeroTime* --:--:-- ", sizeof(str_result)); - else { - ut_timecopy = event_up->ut_tv.tv_sec; - strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result)); - /* - * Include the year, if it is not the same year as "now". - */ - if (strncmp(&str_result[20], thisyear, 4) == 0) - str_result[20] = '\0'; - else { - str_result[24] = ' '; /* Replace a '\n' */ - str_result[25] = '\0'; - } - } - - if (userinf_up->ut_line[0] == '\0') - strlcat(str_result, "NoDev", sizeof(str_result)); - else { - maxcopy = strlen(str_result) + sizeof(userinf_up->ut_line); - if (maxcopy > sizeof(str_result)) - maxcopy = sizeof(str_result); - strlcat(str_result, userinf_up->ut_line, maxcopy); - } - strlcat(str_result, ": ", sizeof(str_result)); - - if (userinf_up->ut_user[0] == '\0') - strlcat(str_result, "LogOff", sizeof(str_result)); - else { - maxcopy = strlen(str_result) + sizeof(userinf_up->ut_user); - if (maxcopy > sizeof(str_result)) - maxcopy = sizeof(str_result); - strlcat(str_result, userinf_up->ut_user, maxcopy); - } - - return (str_result); + if (aup == NULL) + SLIST_INSERT_HEAD(&Users, up, next); + else + SLIST_INSERT_AFTER(aup, up, next); + strlcpy(up->user, user, sizeof(up->user)); + up->time = secs; + timeradd(&Total, &secs, &Total); } -#endif int main(int argc, char *argv[]) @@ -261,13 +196,8 @@ main(int argc, char *argv[]) (void) setlocale(LC_TIME, ""); - while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) { + while ((c = getopt(argc, argv, "c:dpt:w:")) != -1) { switch (c) { -#ifdef DEBUG - case 'D': - Debug++; - break; -#endif case 'c': #ifdef CONSOLE_TTY Console = optarg; @@ -299,7 +229,7 @@ main(int argc, char *argv[]) * initialize user list */ for (; optind < argc; optind++) { - Users = update_user(Users, argv[optind], (time_t)0); + update_user(argv[optind], (struct timeval){ 0, 0 }); } Flags |= AC_U; /* freeze user list */ } @@ -307,112 +237,108 @@ main(int argc, char *argv[]) Flags &= ~AC_P; ac(wtmpf); - return 0; + return (0); } /* * print login time in decimal hours */ -void -show(const char *name, time_t secs) +static void +show(const char *user, struct timeval secs) { (void)printf("\t%-*s %8.2f\n", - (int)sizeof(((struct utmpx *)0)->ut_user), name, - ((double)secs / 3600)); + (int)sizeof(((struct user_entry *)0)->user), user, + (double)secs.tv_sec / 3600); } -void -show_users(struct user_list *list) +static void +show_users(void) { - struct user_list *lp; + struct user_entry *lp; - for (lp = list; lp; lp = lp->next) - show(lp->name, lp->secs); + SLIST_FOREACH(lp, &Users, next) + show(lp->user, lp->time); } /* * print total login time for 24hr period in decimal hours */ -void -show_today(struct user_list *users, struct utmp_list *logins, time_t secs) +static void +show_today(struct timeval today) { - struct user_list *up; - struct utmp_list *lp; + struct user_entry *up; + struct utmpx_entry *lp; char date[64]; - time_t yesterday = secs - 1; + struct timeval usec = { 0, 1 }; + struct timeval yesterday; static int d_first = -1; if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); - (void)strftime(date, sizeof (date), + timersub(&today, &usec, &yesterday); + (void)strftime(date, sizeof(date), d_first ? "%e %b total" : "%b %e total", - localtime(&yesterday)); + localtime(&yesterday.tv_sec)); /* restore the missing second */ - yesterday++; + timeradd(&today, &usec, &yesterday); - for (lp = logins; lp != NULL; lp = lp->next) { - secs = yesterday - lp->usr.ut_tv.tv_sec; - Users = update_user(Users, lp->usr.ut_user, secs); - lp->usr.ut_tv.tv_sec = yesterday; /* as if they just logged in */ + SLIST_FOREACH(lp, &CurUtmpx, next) { + timersub(&yesterday, &lp->time, &today); + update_user(lp->user, today); + /* As if they just logged in. */ + lp->time = yesterday; } - secs = 0; - for (up = users; up != NULL; up = up->next) { - secs += up->secs; - up->secs = 0; /* for next day */ + timerclear(&today); + SLIST_FOREACH(up, &Users, next) { + timeradd(&today, &up->time, &today); + /* For next day. */ + timerclear(&up->time); } - if (secs) - (void)printf("%s %11.2f\n", date, ((double)secs / 3600)); + if (timerisset(&today)) + (void)printf("%s %11.2f\n", date, (double)today.tv_sec / 3600); } /* - * log a user out and update their times. - * if ut_line is "~", we log all users out as the system has - * been shut down. + * Log a user out and update their times. + * If ut_type is BOOT_TIME or SHUTDOWN_TIME, we log all users out as the + * system has been shut down. */ -struct utmp_list * -log_out(struct utmp_list *head, struct utmpx *up) +static void +log_out(const struct utmpx *up) { - struct utmp_list *lp, *lp2, *tlp; - time_t secs; + struct utmpx_entry *lp, *lp2, *tlp; + struct timeval secs; - for (lp = head, lp2 = NULL; lp != NULL; ) + for (lp = SLIST_FIRST(&CurUtmpx), lp2 = NULL; lp != NULL;) if (up->ut_type == BOOT_TIME || up->ut_type == SHUTDOWN_TIME || (up->ut_type == DEAD_PROCESS && - memcmp(lp->usr.ut_id, up->ut_id, sizeof up->ut_id) == 0)) { - secs = up->ut_tv.tv_sec - lp->usr.ut_tv.tv_sec; - Users = update_user(Users, lp->usr.ut_user, secs); -#ifdef DEBUG - if (Debug) - printf("%s logged out (%2d:%02d:%02d)\n", - debug_pfx(up, &lp->usr), (int)(secs / 3600), - (int)((secs % 3600) / 60), - (int)(secs % 60)); -#endif + memcmp(lp->id, up->ut_id, sizeof(up->ut_id)) == 0)) { + timersub(&up->ut_tv, &lp->time, &secs); + update_user(lp->user, secs); /* * now lose it */ tlp = lp; - lp = lp->next; - if (tlp == head) - head = lp; - else if (lp2 != NULL) - lp2->next = lp; + lp = SLIST_NEXT(lp, next); + if (lp2 == NULL) + SLIST_REMOVE_HEAD(&CurUtmpx, next); + else + SLIST_REMOVE_AFTER(lp2, next); free(tlp); } else { lp2 = lp; - lp = lp->next; + lp = SLIST_NEXT(lp, next); } - return head; } /* * if do_tty says ok, login a user */ -struct utmp_list * -log_in(struct utmp_list *head, struct utmpx *up) +static void +log_in(struct utmpx *up) { - struct utmp_list *lp; + struct utmpx_entry *lp; /* * this could be a login. if we're not dealing with @@ -431,198 +357,150 @@ log_in(struct utmp_list *head, struct utmpx *up) * SunOS 4.0.2 does not treat ":0.0" as special but we * do. */ - if (on_console(head)) - return head; + if (on_console()) + return; /* * ok, no recorded login, so they were here when wtmp * started! Adjust ut_time! */ - up->ut_time = FirstTime; + up->ut_tv = FirstTime; /* * this allows us to pick the right logout */ - strlcpy(up->ut_line, Console, sizeof (up->ut_line)); + strlcpy(up->ut_line, Console, sizeof(up->ut_line)); } #endif /* * If we are doing specified ttys only, we ignore * anything else. */ - if (Flags & AC_T) - if (!do_tty(up->ut_line)) - return head; + if (Flags & AC_T && !do_tty(up->ut_line)) + return; /* * go ahead and log them in */ - if ((lp = NEW(struct utmp_list)) == NULL) + if ((lp = malloc(sizeof(*lp))) == NULL) errx(1, "malloc failed"); - lp->next = head; - head = lp; - memmove(&lp->usr, up, sizeof *up); -#ifdef DEBUG - if (Debug) { - printf("%s logged in", debug_pfx(&lp->usr, up)); - if (*up->ut_host) - printf(" (%-.*s)", (int)sizeof(up->ut_host), - up->ut_host); - putchar('\n'); - } + SLIST_INSERT_HEAD(&CurUtmpx, lp, next); + strlcpy(lp->user, up->ut_user, sizeof(lp->user)); + memcpy(lp->id, up->ut_id, sizeof(lp->id)); +#ifdef CONSOLE_TTY + memcpy(lp->line, up->ut_line, sizeof(lp->line)); #endif - return head; + lp->time = up->ut_tv; } -int +static void ac(const char *file) { - struct utmp_list *lp, *head = NULL; + struct utmpx_entry *lp; struct utmpx *usr, usht; struct tm *ltm; - time_t prev_secs, secs, ut_timecopy; - int day, rfound, tchanged, tskipped; + struct timeval prev_secs, ut_timecopy, secs, clock_shift, now; + int day, rfound; day = -1; - prev_secs = 1; /* Minimum acceptable date == 1970 */ - rfound = tchanged = tskipped = 0; - secs = 0; + timerclear(&prev_secs); /* Minimum acceptable date == 1970. */ + timerclear(&secs); + timerclear(&clock_shift); + rfound = 0; if (setutxdb(UTXDB_LOG, file) != 0) err(1, "%s", file); while ((usr = getutxent()) != NULL) { rfound++; - ut_timecopy = usr->ut_tv.tv_sec; - /* - * With sparc64 using 64-bit time_t's, there is some system - * routine which sets ut_time==0 (the high-order word of a - * 64-bit time) instead of a 32-bit time value. For those - * wtmp files, it is "more-accurate" to substitute the most- - * recent time found, instead of throwing away the entire - * record. While it is still just a guess, it is a better - * guess than throwing away a log-off record and therefore - * counting a session as if it continued to the end of the - * month, or the next system-reboot. - */ - if (ut_timecopy == 0 && prev_secs > 1) { -#ifdef DEBUG - if (Debug) - printf("%s - date changed to: %s", - debug_pfx(usr, usr), ctime(&prev_secs)); -#endif - tchanged++; - usr->ut_tv.tv_sec = ut_timecopy = prev_secs; - } - /* - * Skip records where the time goes backwards. - */ - if (ut_timecopy < prev_secs) { -#ifdef DEBUG - if (Debug) - printf("%s - bad date, record skipped\n", - debug_pfx(usr, usr)); -#endif - tskipped++; - continue; /* Skip this invalid record. */ - } + ut_timecopy = usr->ut_tv; + /* Don't let the time run backwards. */ + if (timercmp(&ut_timecopy, &prev_secs, <)) + ut_timecopy = prev_secs; prev_secs = ut_timecopy; - if (!FirstTime) + if (!timerisset(&FirstTime)) FirstTime = ut_timecopy; if (Flags & AC_D) { - ltm = localtime(&ut_timecopy); + ltm = localtime(&ut_timecopy.tv_sec); if (day >= 0 && day != ltm->tm_yday) { day = ltm->tm_yday; /* * print yesterday's total */ secs = ut_timecopy; - secs -= ltm->tm_sec; - secs -= 60 * ltm->tm_min; - secs -= 3600 * ltm->tm_hour; - show_today(Users, head, secs); + secs.tv_sec -= ltm->tm_sec; + secs.tv_sec -= 60 * ltm->tm_min; + secs.tv_sec -= 3600 * ltm->tm_hour; + secs.tv_usec = 0; + show_today(secs); } else day = ltm->tm_yday; } switch(usr->ut_type) { case OLD_TIME: - secs = ut_timecopy; + clock_shift = ut_timecopy; break; case NEW_TIME: - secs -= ut_timecopy; + timersub(&clock_shift, &ut_timecopy, &clock_shift); /* * adjust time for those logged in */ - for (lp = head; lp != NULL; lp = lp->next) - lp->usr.ut_tv.tv_sec -= secs; + SLIST_FOREACH(lp, &CurUtmpx, next) + timersub(&lp->time, &clock_shift, &lp->time); break; case BOOT_TIME: case SHUTDOWN_TIME: - head = log_out(head, usr); + log_out(usr); FirstTime = ut_timecopy; /* shouldn't be needed */ break; case USER_PROCESS: /* - * if they came in on tty[p-sP-S]*, then it is only - * a login session if the ut_host field is non-empty + * If they came in on pts/..., then it is only + * a login session if the ut_host field is non-empty. */ - if (strncmp(usr->ut_line, "tty", 3) != 0 || - strchr("pqrsPQRS", usr->ut_line[3]) == NULL || + if (strncmp(usr->ut_line, "pts/", 4) != 0 || *usr->ut_host != '\0') - head = log_in(head, usr); -#ifdef DEBUG - else if (Debug > 1) - /* Things such as 'screen' sessions. */ - printf("%s - record ignored\n", - debug_pfx(usr, usr)); -#endif + log_in(usr); break; case DEAD_PROCESS: - head = log_out(head, usr); + log_out(usr); break; } } endutxent(); - if (!(Flags & AC_W)) - usht.ut_tv.tv_sec = time(NULL); + (void)gettimeofday(&now, NULL); + if (Flags & AC_W) + usht.ut_tv = ut_timecopy; else - usht.ut_tv.tv_sec = ut_timecopy; + usht.ut_tv = now; usht.ut_type = SHUTDOWN_TIME; if (Flags & AC_D) { - ltm = localtime(&ut_timecopy); + ltm = localtime(&ut_timecopy.tv_sec); if (day >= 0 && day != ltm->tm_yday) { /* * print yesterday's total */ secs = ut_timecopy; - secs -= ltm->tm_sec; - secs -= 60 * ltm->tm_min; - secs -= 3600 * ltm->tm_hour; - show_today(Users, head, secs); + secs.tv_sec -= ltm->tm_sec; + secs.tv_sec -= 60 * ltm->tm_min; + secs.tv_sec -= 3600 * ltm->tm_hour; + secs.tv_usec = 0; + show_today(secs); } } /* * anyone still logged in gets time up to now */ - head = log_out(head, &usht); + log_out(&usht); if (Flags & AC_D) - show_today(Users, head, time((time_t *)0)); + show_today(now); else { if (Flags & AC_P) - show_users(Users); + show_users(); show("total", Total); } - - if (tskipped > 0) - printf("(Skipped %d of %d records due to invalid time values)\n", - tskipped, rfound); - if (tchanged > 0) - printf("(Changed %d of %d records to have a more likely time value)\n", - tchanged, rfound); - - return 0; } -void +static void usage(void) { (void)fprintf(stderr, -- cgit v1.3 From 07df15dd9e6db9065f63cf2ae6bf941b63bf2680 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 31 Aug 2012 08:48:53 +0000 Subject: Use better variable naming. MFC after: 1 month --- usr.sbin/ac/ac.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c index 1e2c54b86f48b..db275e0c081c4 100644 --- a/usr.sbin/ac/ac.c +++ b/usr.sbin/ac/ac.c @@ -269,8 +269,7 @@ show_today(struct timeval today) struct user_entry *up; struct utmpx_entry *lp; char date[64]; - struct timeval usec = { 0, 1 }; - struct timeval yesterday; + struct timeval diff, total = { 0, 0 }, usec = { 0, 1 }, yesterday; static int d_first = -1; if (d_first < 0) @@ -280,23 +279,19 @@ show_today(struct timeval today) d_first ? "%e %b total" : "%b %e total", localtime(&yesterday.tv_sec)); - /* restore the missing second */ - timeradd(&today, &usec, &yesterday); - SLIST_FOREACH(lp, &CurUtmpx, next) { - timersub(&yesterday, &lp->time, &today); - update_user(lp->user, today); + timersub(&today, &lp->time, &diff); + update_user(lp->user, diff); /* As if they just logged in. */ - lp->time = yesterday; + lp->time = today; } - timerclear(&today); SLIST_FOREACH(up, &Users, next) { - timeradd(&today, &up->time, &today); + timeradd(&total, &up->time, &total); /* For next day. */ timerclear(&up->time); } - if (timerisset(&today)) - (void)printf("%s %11.2f\n", date, (double)today.tv_sec / 3600); + if (timerisset(&total)) + (void)printf("%s %11.2f\n", date, (double)total.tv_sec / 3600); } /* -- cgit v1.3 From b47c28f3bad875d9f8422afaf6b37c0c98e16e4d Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Fri, 31 Aug 2012 16:57:16 +0000 Subject: Remove trailing whitespace. --- usr.sbin/makefs/makefs.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8 index 68d6ca6652525..b1a5751386228 100644 --- a/usr.sbin/makefs/makefs.8 +++ b/usr.sbin/makefs/makefs.8 @@ -188,7 +188,7 @@ Set file system specific options. .Ar fs-options is a comma separated list of options. Valid file system specific options are detailed below. -.It Fl p +.It Fl p Create the image as a sparse file. .It Fl S Ar sector-size Set the file system sector size to -- cgit v1.3 From 8f67c5bc870e1e201a34c10d1ff7d471aa711845 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 31 Aug 2012 18:26:39 +0000 Subject: Relicense ac(8). Though the license of the original ac(8) source code provides many liberties, we are already somewhat violating it. The license requires us to clearly comment any modifications to the source code, as the original authors of course do not want to get bug reports for modified versions of ac(8). This is something we have not done consistently. The need for such a license clause has become less over time. It is very unlikely that end users will contact the original authors, as the copyright is from 1994. I contacted both the copyright holders. They responded in a timely fashion and were more than willing to relicense it to a 2-clause BSD license. To address any concerns about bug reports going to the original authors instead of me, add my own name and email address to the copyright statements as well. MFC after: 1 month Special thanks to: Christopher Demetriou and Simon Gerraty --- usr.sbin/ac/ac.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c index db275e0c081c4..f5d4bda650bdc 100644 --- a/usr.sbin/ac/ac.c +++ b/usr.sbin/ac/ac.c @@ -1,16 +1,29 @@ -/* - * Copyright (c) 1994 Christopher G. Demetriou. - * @(#)Copyright (c) 1994, Simon J. Gerraty. +/*- + * Copyright (c) 1994 Christopher G. Demetriou + * Copyright (c) 1994 Simon J. Gerraty + * Copyright (c) 2012 Ed Schouten + * 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 is free software. It comes with NO WARRANTY. - * Permission to use, modify and distribute this source code - * is granted subject to the following conditions. - * 1/ that the above copyright notice and this notice - * are preserved in all copies and that due credit be given - * to the author. - * 2/ that any changes to this code are clearly commented - * as such so that the author does not get blamed for bugs - * other than his own. + * 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 -- cgit v1.3 From f66dc62abfcff95eee4849fe414b993e6e28dc0e Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 31 Aug 2012 22:37:08 +0000 Subject: Also relicense the ac(8) man page. MFC after: 1 month Discussed with: Simon Gerraty and Chris Demetriou --- usr.sbin/ac/ac.8 | 5 ----- 1 file changed, 5 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ac/ac.8 b/usr.sbin/ac/ac.8 index 63b5618b920e7..cc6fb9392bf4a 100644 --- a/usr.sbin/ac/ac.8 +++ b/usr.sbin/ac/ac.8 @@ -11,11 +11,6 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Christopher G. Demetriou. -.\" 3. The name of the author may not be used to endorse or promote products -.\" derived from this software without specific prior written permission .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -- cgit v1.3 From 21fe0af7aaca9ffe690669fdde507ace25bff8bd Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sat, 1 Sep 2012 10:48:38 +0000 Subject: Remove unneeded struct timezone. We're only interested in a timestamp -- not the timezone. --- usr.sbin/lmcconfig/lmcconfig.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/lmcconfig/lmcconfig.c b/usr.sbin/lmcconfig/lmcconfig.c index ece19f75aa617..e820a91770211 100644 --- a/usr.sbin/lmcconfig/lmcconfig.c +++ b/usr.sbin/lmcconfig/lmcconfig.c @@ -1076,9 +1076,8 @@ print_events(void) { char *time; struct timeval tv; - struct timezone tz; - gettimeofday(&tv, &tz); + gettimeofday(&tv, NULL); time = (char *)ctime((time_t *)&tv); printf("Current time:\t\t%s", time); if (status.cntrs.reset_time.tv_sec < 1000) -- cgit v1.3 From 98e695d7fd39f6b174702db0adf86697761660c2 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sat, 1 Sep 2012 10:56:15 +0000 Subject: Rework time handling. After I made the previous commit, I noticed the code does some things it shouldn't. It casts a struct timeval to a time_t, assuming tv_sec is the first member. Also, we are not interested in microseconds, so it is better to just call time(NULL). MFC after: 1 month --- usr.sbin/lmcconfig/lmcconfig.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/lmcconfig/lmcconfig.c b/usr.sbin/lmcconfig/lmcconfig.c index e820a91770211..7fbc2168e1ecc 100644 --- a/usr.sbin/lmcconfig/lmcconfig.c +++ b/usr.sbin/lmcconfig/lmcconfig.c @@ -1074,17 +1074,16 @@ print_hssi_sigs(void) static void print_events(void) { - char *time; - struct timeval tv; + const char *reset_time; + time_t now; - gettimeofday(&tv, NULL); - time = (char *)ctime((time_t *)&tv); - printf("Current time:\t\t%s", time); + now = time(NULL); + printf("Current time:\t\t%s", ctime(&now)); if (status.cntrs.reset_time.tv_sec < 1000) - time = "Never\n"; + reset_time = "Never\n"; else - time = (char *)ctime((time_t *)&status.cntrs.reset_time.tv_sec); - printf("Cntrs reset:\t\t%s", time); + reset_time = ctime(&status.cntrs.reset_time.tv_sec); + printf("Cntrs reset:\t\t%s", reset_time); if (status.cntrs.ibytes) printf("Rx bytes:\t\t%ju\n", (uintmax_t)status.cntrs.ibytes); if (status.cntrs.obytes) printf("Tx bytes:\t\t%ju\n", (uintmax_t)status.cntrs.obytes); -- cgit v1.3 From 902d9eafbf66c52f8513bc38f4d0c8063c0b8a03 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sat, 1 Sep 2012 14:45:15 +0000 Subject: Rework all non-contributed files that use `struct timezone'. This structure is not part of POSIX. According to POSIX, gettimeofday() has the following prototype: int gettimeofday(struct timeval *restrict tp, void *restrict tzp); Also, POSIX states that gettimeofday() shall return 0 (as long as tzp is not used). Remove dead error handling code. Also use NULL for a nul-pointer instead of integer 0. While there, change all pieces of code that only use tv_sec to use time(3), as this provides less overhead. --- bin/date/date.c | 8 +++---- bin/dd/dd.c | 4 ++-- bin/dd/misc.c | 2 +- lib/libc/rpc/auth_des.c | 2 +- lib/libc/rpc/svc_auth_des.c | 2 +- libexec/rbootd/rbootd.c | 7 +++--- libexec/rbootd/rmpproto.c | 2 +- libexec/rpc.rstatd/rstat_proc.c | 7 +++--- release/picobsd/tinyware/login/pico-login.c | 14 ++++++------ sbin/adjkerntz/adjkerntz.c | 5 +---- usr.bin/lock/lock.c | 33 +++++++++++++---------------- usr.bin/mail/util.c | 2 +- usr.bin/systat/ifstat.c | 3 +-- usr.bin/time/time.c | 6 +++--- usr.sbin/cron/crontab/crontab.c | 3 +-- usr.sbin/inetd/builtins.c | 8 +------ usr.sbin/keyserv/keyserv.c | 4 ++-- usr.sbin/timed/timed/master.c | 32 ++++++++++++++-------------- usr.sbin/timed/timed/measure.c | 6 +++--- usr.sbin/timed/timed/readmsg.c | 6 +++--- usr.sbin/timed/timed/slave.c | 28 ++++++++++++------------ usr.sbin/timed/timed/timed.c | 6 ++---- usr.sbin/timed/timedc/cmds.c | 2 +- 23 files changed, 87 insertions(+), 105 deletions(-) (limited to 'usr.sbin') diff --git a/bin/date/date.c b/bin/date/date.c index 1e9f281bc8a78..58a9afb0d0fcb 100644 --- a/bin/date/date.c +++ b/bin/date/date.c @@ -137,7 +137,7 @@ main(int argc, char *argv[]) * If -d or -t, set the timezone or daylight savings time; this * doesn't belong here; the kernel should not know about either. */ - if (set_timezone && settimeofday((struct timeval *)NULL, &tz)) + if (set_timezone && settimeofday(NULL, &tz) != 0) err(1, "settimeofday (timezone)"); if (!rflag && time(&tval) == -1) @@ -273,14 +273,14 @@ setthetime(const char *fmt, const char *p, int jflag, int nflag) /* set the time */ if (nflag || netsettime(tval)) { utx.ut_type = OLD_TIME; - gettimeofday(&utx.ut_tv, NULL); + (void)gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); tv.tv_sec = tval; tv.tv_usec = 0; - if (settimeofday(&tv, (struct timezone *)NULL)) + if (settimeofday(&tv, NULL) != 0) err(1, "settimeofday (timeval)"); utx.ut_type = NEW_TIME; - gettimeofday(&utx.ut_tv, NULL); + (void)gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); } diff --git a/bin/dd/dd.c b/bin/dd/dd.c index 0bb43f789b1ae..637e0cc6cdcfc 100644 --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -232,8 +232,8 @@ setup(void) ctab = casetab; } - (void)gettimeofday(&tv, (struct timezone *)NULL); - st.start = tv.tv_sec + tv.tv_usec * 1e-6; + (void)gettimeofday(&tv, NULL); + st.start = tv.tv_sec + tv.tv_usec * 1e-6; } static void diff --git a/bin/dd/misc.c b/bin/dd/misc.c index 4f538bc4bfbda..8edc5a534c631 100644 --- a/bin/dd/misc.c +++ b/bin/dd/misc.c @@ -59,7 +59,7 @@ summary(void) double secs; char buf[100]; - (void)gettimeofday(&tv, (struct timezone *)NULL); + (void)gettimeofday(&tv, NULL); secs = tv.tv_sec + tv.tv_usec * 1e-6 - st.start; if (secs < 1e-6) secs = 1e-6; diff --git a/lib/libc/rpc/auth_des.c b/lib/libc/rpc/auth_des.c index 8f363e91a64f8..1b45c3bcbe956 100644 --- a/lib/libc/rpc/auth_des.c +++ b/lib/libc/rpc/auth_des.c @@ -286,7 +286,7 @@ authdes_marshal(AUTH *auth, XDR *xdrs) * Figure out the "time", accounting for any time difference * with the server if necessary. */ - (void) gettimeofday(&ad->ad_timestamp, (struct timezone *)NULL); + (void)gettimeofday(&ad->ad_timestamp, NULL); ad->ad_timestamp.tv_sec += ad->ad_timediff.tv_sec; ad->ad_timestamp.tv_usec += ad->ad_timediff.tv_usec; while (ad->ad_timestamp.tv_usec >= USEC_PER_SEC) { diff --git a/lib/libc/rpc/svc_auth_des.c b/lib/libc/rpc/svc_auth_des.c index de4d1b4069f2a..bd45f20bf42b4 100644 --- a/lib/libc/rpc/svc_auth_des.c +++ b/lib/libc/rpc/svc_auth_des.c @@ -271,7 +271,7 @@ _svcauth_des(rqst, msg) debug("timestamp before last seen"); return (AUTH_REJECTEDVERF); /* replay */ } - (void) gettimeofday(¤t, (struct timezone *)NULL); + (void)gettimeofday(¤t, NULL); current.tv_sec -= window; /* allow for expiration */ if (!BEFORE(¤t, ×tamp)) { debug("timestamp expired"); diff --git a/libexec/rbootd/rbootd.c b/libexec/rbootd/rbootd.c index e56a3e77a428f..1d35bd1e2eeba 100644 --- a/libexec/rbootd/rbootd.c +++ b/libexec/rbootd/rbootd.c @@ -310,16 +310,15 @@ void DoTimeout(void) { RMPCONN *rtmp; - struct timeval now; - - (void) gettimeofday(&now, (struct timezone *)0); + time_t now; /* * For each active connection, if RMP_TIMEOUT seconds have passed * since the last packet was sent, delete the connection. */ + now = time(NULL); for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) - if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) { + if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now) { syslog(LOG_WARNING, "%s: connection timed out (%u)", EnetStr(rtmp), rtmp->rmp.r_type); RemoveConn(rtmp); diff --git a/libexec/rbootd/rmpproto.c b/libexec/rbootd/rmpproto.c index 8d51445165ade..d37ef852415ae 100644 --- a/libexec/rbootd/rmpproto.c +++ b/libexec/rbootd/rmpproto.c @@ -575,7 +575,7 @@ SendPacket(RMPCONN *rconn) /* * Last time this connection was active. */ - (void) gettimeofday(&rconn->tstamp, (struct timezone *)0); + (void)gettimeofday(&rconn->tstamp, NULL); if (DbgFp != NULL) /* display packet */ DispPkt(rconn,DIR_SENT); diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c index 76911c8bd370f..1b66c644623f6 100644 --- a/libexec/rpc.rstatd/rstat_proc.c +++ b/libexec/rpc.rstatd/rstat_proc.c @@ -244,7 +244,7 @@ updatestat(void) FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout); FETCH_CNT(stats_all.s1.v_intr, sys.v_intr); FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch); - gettimeofday(&tm, (struct timezone *) 0); + (void)gettimeofday(&tm, NULL); stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + hz*(tm.tv_usec - btm.tv_usec)/1000000; @@ -287,8 +287,9 @@ updatestat(void) stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors; stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions; } - gettimeofday((struct timeval *)&stats_all.s3.curtime, - (struct timezone *) 0); + (void)gettimeofday(&tm, NULL); + stats_all.s3.curtime.tv_sec = tm.tv_sec; + stats_all.s3.curtime.tv_usec = tm.tv_usec; alarm(1); } diff --git a/release/picobsd/tinyware/login/pico-login.c b/release/picobsd/tinyware/login/pico-login.c index 78346238a8d88..e540ca861a519 100644 --- a/release/picobsd/tinyware/login/pico-login.c +++ b/release/picobsd/tinyware/login/pico-login.c @@ -150,12 +150,11 @@ main(argc, argv) extern char **environ; struct group *gr; struct stat st; - struct timeval tp; struct utmpx utmp; int rootok, retries, backoff; int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval; int changepass; - time_t warntime; + time_t now, warntime; uid_t uid, euid; gid_t egid; char *p, *ttyn; @@ -430,8 +429,7 @@ main(argc, argv) if (!quietlog) quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; - if (pwd->pw_change || pwd->pw_expire) - (void)gettimeofday(&tp, (struct timezone *)NULL); + now = time(NULL); #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ @@ -439,10 +437,10 @@ main(argc, argv) DEFAULT_WARN); if (pwd->pw_expire) { - if (tp.tv_sec >= pwd->pw_expire) { + if (now >= pwd->pw_expire) { refused("Sorry -- your account has expired", "EXPIRED", 1); - } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog) + } else if (pwd->pw_expire - now < warntime && !quietlog) (void)printf("Warning: your account expires on %s", ctime(&pwd->pw_expire)); } @@ -452,12 +450,12 @@ main(argc, argv) changepass = 0; if (pwd->pw_change) { - if (tp.tv_sec >= pwd->pw_change) { + if (now >= pwd->pw_change) { (void)printf("Sorry -- your password has expired.\n"); changepass = 1; syslog(LOG_INFO, "%s Password expired - forcing change", pwd->pw_name); - } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog) + } else if (pwd->pw_change - now < warntime && !quietlog) (void)printf("Warning: your password expires on %s", ctime(&pwd->pw_change)); } diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c index 35d8da9d6c518..c42379d71915d 100644 --- a/sbin/adjkerntz/adjkerntz.c +++ b/sbin/adjkerntz/adjkerntz.c @@ -315,10 +315,7 @@ recalculate: * restoring disrtcset, since we don't clean up * anything. */ - if (gettimeofday(&tv, (struct timezone *)NULL)) { - syslog(LOG_ERR, "gettimeofday: %m"); - return 1; - } + (void)gettimeofday(&tv, NULL); tv.tv_sec += diff; stv = &tv; } diff --git a/usr.bin/lock/lock.c b/usr.bin/lock/lock.c index 3f23a981a0b10..0382e831862c6 100644 --- a/usr.bin/lock/lock.c +++ b/usr.bin/lock/lock.c @@ -54,9 +54,9 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include #include + #include #include #include @@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #define TIMEOUT 15 @@ -89,10 +90,9 @@ int main(int argc, char **argv) { struct passwd *pw; - struct timeval timval; - time_t timval_sec; struct itimerval ntimer, otimer; struct tm *timp; + time_t timval; int ch, failures, sectimeout, usemine, vtylock; char *ap, *cryptpw, *mypw, *ttynam, *tzn; char hostname[MAXHOSTNAMELEN], s[BUFSIZ], s1[BUFSIZ]; @@ -138,11 +138,9 @@ main(int argc, char **argv) errx(1, "not a terminal?"); if (strncmp(ttynam, _PATH_DEV, strlen(_PATH_DEV)) == 0) ttynam += strlen(_PATH_DEV); - if (gettimeofday(&timval, (struct timezone *)NULL)) - err(1, "gettimeofday"); - nexttime = timval.tv_sec + (sectimeout * 60); - timval_sec = timval.tv_sec; - timp = localtime(&timval_sec); + timval = time(NULL); + nexttime = timval + (sectimeout * 60); + timp = localtime(&timval); ap = asctime(timp); tzn = timp->tm_zone; @@ -256,17 +254,16 @@ usage(void) static void hi(int signo __unused) { - struct timeval timval; + time_t timval; - if (!gettimeofday(&timval, (struct timezone *)NULL)) { - (void)printf("lock: type in the unlock key. "); - if (no_timeout) { - (void)putchar('\n'); - } else { - (void)printf("timeout in %jd:%jd minutes\n", - (intmax_t)(nexttime - timval.tv_sec) / 60, - (intmax_t)(nexttime - timval.tv_sec) % 60); - } + timval = time(NULL); + (void)printf("lock: type in the unlock key. "); + if (no_timeout) { + (void)putchar('\n'); + } else { + (void)printf("timeout in %jd:%jd minutes\n", + (intmax_t)(nexttime - timval) / 60, + (intmax_t)(nexttime - timval) % 60); } } diff --git a/usr.bin/mail/util.c b/usr.bin/mail/util.c index c90fe905effde..4e3a0f6dde934 100644 --- a/usr.bin/mail/util.c +++ b/usr.bin/mail/util.c @@ -324,7 +324,7 @@ alter(char *name) if (stat(name, &sb)) return; - (void)gettimeofday(&tv[0], (struct timezone *)NULL); + (void)gettimeofday(&tv[0], NULL); tv[0].tv_sec++; TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtim); (void)utimes(name, tv); diff --git a/usr.bin/systat/ifstat.c b/usr.bin/systat/ifstat.c index fc853b0c87b49..3e4549921c038 100644 --- a/usr.bin/systat/ifstat.c +++ b/usr.bin/systat/ifstat.c @@ -247,8 +247,7 @@ fetchifstat(void) old_outb = ifp->if_mib.ifmd_data.ifi_obytes; ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange; - if (gettimeofday(&new_tv, (struct timezone *)0) != 0) - IFSTAT_ERR(2, "error getting time of day"); + (void)gettimeofday(&new_tv, NULL); (void)getifmibdata(ifp->if_row, &ifp->if_mib); new_inb = ifp->if_mib.ifmd_data.ifi_ibytes; diff --git a/usr.bin/time/time.c b/usr.bin/time/time.c index 2cb7f13d9e764..ebee54225a151 100644 --- a/usr.bin/time/time.c +++ b/usr.bin/time/time.c @@ -117,7 +117,7 @@ main(int argc, char **argv) setvbuf(out, (char *)NULL, _IONBF, (size_t)0); } - gettimeofday(&before_tv, (struct timezone *)NULL); + (void)gettimeofday(&before_tv, NULL); switch(pid = fork()) { case -1: /* error */ err(1, "time"); @@ -134,7 +134,7 @@ main(int argc, char **argv) (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGINFO, siginfo); while (wait4(pid, &status, 0, &ru) != pid); - gettimeofday(&after, (struct timezone *)NULL); + (void)gettimeofday(&after, NULL); if ( ! WIFEXITED(status)) warnx("command terminated abnormally"); exitonsig = WIFSIGNALED(status) ? WTERMSIG(status) : 0; @@ -297,7 +297,7 @@ siginfo(int sig __unused) struct timeval after; struct rusage ru; - gettimeofday(&after, (struct timezone *)NULL); + (void)gettimeofday(&after, NULL); getrusage(RUSAGE_CHILDREN, &ru); showtime(stdout, &before_tv, &after, &ru); } diff --git a/usr.sbin/cron/crontab/crontab.c b/usr.sbin/cron/crontab/crontab.c index 69c4606334437..1ac81b0295457 100644 --- a/usr.sbin/cron/crontab/crontab.c +++ b/usr.sbin/cron/crontab/crontab.c @@ -627,9 +627,8 @@ static void poke_daemon() { #ifdef USE_UTIMES struct timeval tvs[2]; - struct timezone tz; - (void) gettimeofday(&tvs[0], &tz); + (void)gettimeofday(&tvs[0], NULL); tvs[1] = tvs[0]; if (utimes(SPOOL_DIR, tvs) < OK) { warn("can't update mtime on spooldir %s", SPOOL_DIR); diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c index 974e54f46d1b9..3ccdab04362cf 100644 --- a/usr.sbin/inetd/builtins.c +++ b/usr.sbin/inetd/builtins.c @@ -691,15 +691,9 @@ printit: uint32_t machtime(void) { - struct timeval tv; - if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { - if (debug) - warnx("unable to get time of day"); - return (0L); - } #define OFFSET ((uint32_t)25567 * 24*60*60) - return (htonl((uint32_t)(tv.tv_sec + OFFSET))); + return (htonl((uint32_t)(time(NULL) + OFFSET))); #undef OFFSET } diff --git a/usr.sbin/keyserv/keyserv.c b/usr.sbin/keyserv/keyserv.c index 2366ba07fe273..79bf90d0f1df6 100644 --- a/usr.sbin/keyserv/keyserv.c +++ b/usr.sbin/keyserv/keyserv.c @@ -232,7 +232,7 @@ randomize(master) seed = 0; for (i = 0; i < 1024; i++) { - (void) gettimeofday(&tv, (struct timezone *) NULL); + (void)gettimeofday(&tv, NULL); shift = i % 8 * sizeof (int); seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift)); } @@ -575,7 +575,7 @@ key_gen_1_svc_prog(v, s) static des_block keygen; static des_block key; - (void) gettimeofday(&time, (struct timezone *) NULL); + (void)gettimeofday(&time, NULL); keygen.key.high += (time.tv_sec ^ time.tv_usec); keygen.key.low += (time.tv_sec ^ time.tv_usec); ecb_crypt((char *)&masterkey, (char *)&keygen, sizeof (keygen), diff --git a/usr.sbin/timed/timed/master.c b/usr.sbin/timed/timed/master.c index a7d74ee533557..b64259bcfdb5c 100644 --- a/usr.sbin/timed/timed/master.c +++ b/usr.sbin/timed/timed/master.c @@ -85,7 +85,7 @@ master() if (ntp->status == MASTER) masterup(ntp); } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); pollingtime = ntime.tv_sec+3; if (justquit) polls = 0; @@ -96,14 +96,14 @@ master() * to update all timers. */ loop: - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); wait.tv_sec = pollingtime - ntime.tv_sec; if (wait.tv_sec < 0) wait.tv_sec = 0; wait.tv_usec = 0; msg = readmsg(TSP_ANY, ANYADDR, &wait, 0); if (!msg) { - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); if (ntime.tv_sec >= pollingtime) { pollingtime = ntime.tv_sec + SAMPLEINTVL; get_goodgroup(0); @@ -175,7 +175,7 @@ loop: } mchgdate(msg); - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); pollingtime = ntime.tv_sec + SAMPLEINTVL; break; @@ -203,7 +203,7 @@ loop: } mchgdate(msg); - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); pollingtime = ntime.tv_sec + SAMPLEINTVL; break; @@ -284,7 +284,7 @@ loop: * do not want to call synch() while waiting * to be killed! */ - (void)gettimeofday(&ntime, (struct timezone *)0); + (void)gettimeofday(&ntime, NULL); pollingtime = ntime.tv_sec + SAMPLEINTVL; break; @@ -354,7 +354,7 @@ mchgdate(msg) (void)strcpy(olddate, date()); /* adjust time for residence on the queue */ - (void)gettimeofday(&otime, 0); + (void)gettimeofday(&otime, NULL); adj_msg_time(msg,&otime); tmptv.tv_sec = msg->tsp_time.tv_sec; @@ -368,11 +368,11 @@ mchgdate(msg) synch(tvtomsround(ntime)); } else { utx.ut_type = OLD_TIME; - gettimeofday(&utx.ut_tv, NULL); + (void)gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); (void)settimeofday(&tmptv, 0); utx.ut_type = NEW_TIME; - gettimeofday(&utx.ut_tv, NULL); + (void)gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); spreadtime(); } @@ -396,7 +396,7 @@ synch(mydelta) if (slvcount > 0) { if (trace) fprintf(fd, "measurements starting at %s\n", date()); - (void)gettimeofday(&check, 0); + (void)gettimeofday(&check, NULL); for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { if (htp->noanswer != 0) { measure_status = measure(500, 100, @@ -424,7 +424,7 @@ synch(mydelta) } else { htp->delta = measure_delta; } - (void)gettimeofday(&stop, 0); + (void)gettimeofday(&stop, NULL); timevalsub(&stop, &stop, &check); if (stop.tv_sec >= 1) { if (trace) @@ -437,7 +437,7 @@ synch(mydelta) if (0 != readmsg(TSP_TRACEON,ANYADDR, &wait,0)) traceon(); - (void)gettimeofday(&check, 0); + (void)gettimeofday(&check, NULL); } } if (trace) @@ -474,7 +474,7 @@ spreadtime() for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { to.tsp_type = TSP_SETTIME; (void)strcpy(to.tsp_name, hostname); - (void)gettimeofday(&tmptv, 0); + (void)gettimeofday(&tmptv, NULL); to.tsp_time.tv_sec = tmptv.tv_sec; to.tsp_time.tv_usec = tmptv.tv_usec; answer = acksend(&to, &htp->addr, htp->name, @@ -732,7 +732,7 @@ masterup(net) * we do not tell them to start using our time, before we have * found a good master. */ - (void)gettimeofday(&net->slvwait, 0); + (void)gettimeofday(&net->slvwait, NULL); } void @@ -755,12 +755,12 @@ newslave(msg) * If we are stable, send our time to the slave. * Do not go crazy if the date has been changed. */ - (void)gettimeofday(&now, 0); + (void)gettimeofday(&now, NULL); if (now.tv_sec >= fromnet->slvwait.tv_sec+3 || now.tv_sec < fromnet->slvwait.tv_sec) { to.tsp_type = TSP_SETTIME; (void)strcpy(to.tsp_name, hostname); - (void)gettimeofday(&tmptv, 0); + (void)gettimeofday(&tmptv, NULL); to.tsp_time.tv_sec = tmptv.tv_sec; to.tsp_time.tv_usec = tmptv.tv_usec; answer = acksend(&to, &htp->addr, diff --git a/usr.sbin/timed/timed/measure.c b/usr.sbin/timed/timed/measure.c index 7f14e79f685b7..81ab7c4cad9cb 100644 --- a/usr.sbin/timed/timed/measure.c +++ b/usr.sbin/timed/timed/measure.c @@ -128,7 +128,7 @@ measure(maxmsec, wmsec, hname, addr, print) FD_ZERO(&ready); - (void)gettimeofday(&tdone, 0); + (void)gettimeofday(&tdone, NULL); mstotvround(&tout, maxmsec); timevaladd(&tdone, &tout); /* when we give up */ @@ -136,7 +136,7 @@ measure(maxmsec, wmsec, hname, addr, print) rcvcount = 0; while (rcvcount < MSGS) { - (void)gettimeofday(&tcur, 0); + (void)gettimeofday(&tcur, NULL); /* * keep sending until we have sent the max @@ -173,7 +173,7 @@ measure(maxmsec, wmsec, hname, addr, print) FD_SET(sock_raw, &ready); count = select(sock_raw+1, &ready, (fd_set *)0, (fd_set *)0, &tout); - (void)gettimeofday(&tcur, (struct timezone *)0); + (void)gettimeofday(&tcur, NULL); if (count <= 0) break; diff --git a/usr.sbin/timed/timed/readmsg.c b/usr.sbin/timed/timed/readmsg.c index 9bbb3d784d519..aca2dbe6403f4 100644 --- a/usr.sbin/timed/timed/readmsg.c +++ b/usr.sbin/timed/timed/readmsg.c @@ -168,11 +168,11 @@ again: * right one, return it, otherwise insert it in the linked list. */ - (void)gettimeofday(&rtout, 0); + (void)gettimeofday(&rtout, NULL); timevaladd(&rtout, intvl); FD_ZERO(&ready); for (;;) { - (void)gettimeofday(&rtime, 0); + (void)gettimeofday(&rtime, NULL); timevalsub(&rwait, &rtout, &rtime); if (rwait.tv_sec < 0) rwait.tv_sec = rwait.tv_usec = 0; @@ -219,7 +219,7 @@ again: inet_ntoa(from.sin_addr)); continue; } - (void)gettimeofday(&from_when, (struct timezone *)0); + (void)gettimeofday(&from_when, NULL); bytehostorder(&msgin); if (msgin.tsp_vers > TSPVERSION) { diff --git a/usr.sbin/timed/timed/slave.c b/usr.sbin/timed/timed/slave.c index 9076d17749272..26e34f79f2a0e 100644 --- a/usr.sbin/timed/timed/slave.c +++ b/usr.sbin/timed/timed/slave.c @@ -83,7 +83,7 @@ slave() refusetime = 0; adjtime = 0; - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; if (justquit) @@ -103,7 +103,7 @@ slave() loop: get_goodgroup(0); - (void)gettimeofday(&ntime, (struct timezone *)0); + (void)gettimeofday(&ntime, NULL); if (ntime.tv_sec > electiontime) { if (trace) fprintf(fd, "election timer expired\n"); @@ -132,7 +132,7 @@ loop: makeslave(slavenet); /* prune extras */ setstatus(); } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); looktime = ntime.tv_sec + delay2; } if (ntime.tv_sec >= looptime) { @@ -153,7 +153,7 @@ loop: } } } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); looptime = ntime.tv_sec + delay2; } @@ -225,7 +225,7 @@ loop: * Otherwise the clocks can race until the loop * is found. */ - (void)gettimeofday(&otime, 0); + (void)gettimeofday(&otime, NULL); if (adjtime < otime.tv_sec) looptime -= (looptime-otime.tv_sec)/2 + 1; @@ -234,7 +234,7 @@ loop: seq = msg->tsp_seq; synch(tvtomsround(msg->tsp_time)); } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; adjtime = ntime.tv_sec + SAMPLEINTVL*2; @@ -248,7 +248,7 @@ loop: seq = msg->tsp_seq; /* adjust time for residence on the queue */ - (void)gettimeofday(&otime, 0); + (void)gettimeofday(&otime, NULL); adj_msg_time(msg,&otime); /* * the following line is necessary due to syslog @@ -289,7 +289,7 @@ loop: if (status & MASTER) spreadtime(); } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; @@ -327,7 +327,7 @@ loop: setstatus(); answerdelay(); xmit(TSP_SLAVEUP, 0, &from); - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; refusetime = 0; @@ -336,7 +336,7 @@ loop: case TSP_MASTERREQ: if (fromnet->status != SLAVE) break; - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; break; @@ -381,7 +381,7 @@ loop: case TSP_ELECTION: if (fromnet->status == SLAVE) { - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; seq = 0; @@ -538,7 +538,7 @@ loop: electiontime = 0; } } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); looptime = ntime.tv_sec + FASTTOUT; } else { if (msg->tsp_hopcnt-- < 1) @@ -590,7 +590,7 @@ loop: (void)remmach(htp); } } - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); looptime = ntime.tv_sec + FASTTOUT; } break; @@ -660,7 +660,7 @@ schgdate(msg, newdate) msg->tsp_name, newdate); /* adjust time for residence on the queue */ - (void)gettimeofday(&otime, 0); + (void)gettimeofday(&otime, NULL); adj_msg_time(msg, &otime); to.tsp_type = TSP_SETDATEREQ; diff --git a/usr.sbin/timed/timed/timed.c b/usr.sbin/timed/timed/timed.c index 63d1a48c789e4..e3397a9da854d 100644 --- a/usr.sbin/timed/timed/timed.c +++ b/usr.sbin/timed/timed/timed.c @@ -232,7 +232,7 @@ main(argc, argv) } /* choose a unique seed for random number generation */ - (void)gettimeofday(&ntime, 0); + (void)gettimeofday(&ntime, NULL); srandom(ntime.tv_sec + ntime.tv_usec); sequence = random(); /* initial seq number */ @@ -701,11 +701,9 @@ casual(inf, sup) char * date() { - struct timeval tv; time_t tv_sec; - (void)gettimeofday(&tv, (struct timezone *)0); - tv_sec = tv.tv_sec; + tv_sec = time(NULL); return (ctime(&tv_sec)); } diff --git a/usr.sbin/timed/timedc/cmds.c b/usr.sbin/timed/timedc/cmds.c index b778b1a5a5e27..84740f6c45e86 100644 --- a/usr.sbin/timed/timedc/cmds.c +++ b/usr.sbin/timed/timedc/cmds.c @@ -131,7 +131,7 @@ daydiff(hostname) } sec -= BU; - (void)gettimeofday(&now, (struct timezone*)0); + (void)gettimeofday(&now, NULL); return (sec - now.tv_sec); } } -- cgit v1.3 From 5610c8a7ac39e055f9d17cb5f1fea10605f4867f Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Mon, 3 Sep 2012 14:16:16 +0000 Subject: Explicitly document all variables in the net.link.ether.inet MIB. --- usr.sbin/arp/arp.4 | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4 index b7273f2a10248..980ca71941504 100644 --- a/usr.sbin/arp/arp.4 +++ b/usr.sbin/arp/arp.4 @@ -28,7 +28,7 @@ .\" @(#)arp4.4 6.5 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd March 28, 2007 +.Dd September 3, 2012 .Dt ARP 4 .Os .Sh NAME @@ -119,20 +119,53 @@ branch of the .Xr sysctl 3 MIB. -.Bl -tag -width "useloopback" +.Bl -tag -width "log_arp_permanent_modify" +.It Va log_arp_movements +Should the kernel log movements of IP addresses from one hardware +address to an other. +See +.Sx DIAGNOSTICS +below. +Turned on by default. +.It Va log_arp_permanent_modify +Should the kernel log attempts of remote host on network to modify a +permanent ARP entry. +See +.Sx DIAGNOSTICS +below. +Turned on by default. +.It Va log_arp_wrong_iface +Should the kernel log attempts to insert an ARP entry on an interface +when the IP network the address belongs to is connected to an other +interface. +See +.Sx DIAGNOSTICS +below. +Turned on by default. .It Va max_age How long an ARP entry is held in the cache until it needs to be refreshed. +Default is 1200 seconds. +.It Va maxhold +How many packets hold in the per-entry output queue while the entry +is being resolved. +Default is one packet. .It Va maxtries Number of retransmits before host is considered down and error is returned. +Default is 5 tries. +.It Va proxyall +Enables ARP proxying for all hosts on net. +Turned off by default. .It Va useloopback If an ARP entry is added for local address, force the traffic to go through the loopback interface. -.It Va proxyall -Enables ARP proxying for all hosts on net. +Turned on by default. +.It Va wait +Lifetime of an incomplete ARP entry. +Default is 20 seconds. .El .Sh DIAGNOSTICS .Bl -diag -.It "arp: %x:%x:%x:%x:%x:%x is using my IP address %d.%d.%d.%d!" +.It "arp: %x:%x:%x:%x:%x:%x is using my IP address %d.%d.%d.%d on %s!" ARP has discovered another host on the local network which responds to mapping requests for its own Internet address with a different Ethernet address, generally indicating that two hosts are attempting to use the -- cgit v1.3 From 478df1d534e9b1ca86261fcef37b46cc7b4c8f17 Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Mon, 3 Sep 2012 14:29:28 +0000 Subject: Provide a sysctl switch that allows to install ARP entries with multicast bit set. FreeBSD refuses to install such entries since 9.0, and this broke installations running Microsoft NLB, which are violating standards. Tested by: Tarasov Oleg --- sys/netinet/if_ether.c | 8 +++++--- usr.sbin/arp/arp.4 | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 8e2daebbe049c..52d7db228dcf1 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -496,6 +496,7 @@ arpintr(struct mbuf *m) static int log_arp_wrong_iface = 1; static int log_arp_movements = 1; static int log_arp_permanent_modify = 1; +static int allow_multicast = 0; SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_wrong_iface, CTLFLAG_RW, &log_arp_wrong_iface, 0, @@ -506,7 +507,8 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_movements, CTLFLAG_RW, SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_permanent_modify, CTLFLAG_RW, &log_arp_permanent_modify, 0, "log arp replies from MACs different than the one in the permanent arp entry"); - +SYSCTL_INT(_net_link_ether_inet, OID_AUTO, allow_multicast, CTLFLAG_RW, + &allow_multicast, 0, "accept multicast addresses"); static void in_arpinput(struct mbuf *m) @@ -551,8 +553,8 @@ in_arpinput(struct mbuf *m) return; } - if (ETHER_IS_MULTICAST(ar_sha(ah))) { - log(LOG_NOTICE, "in_arp: %*D is multicast\n", + if (allow_multicast == 0 && ETHER_IS_MULTICAST(ar_sha(ah))) { + log(LOG_NOTICE, "arp: %*D is multicast\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":"); return; } diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4 index 980ca71941504..0ff0f2c279adf 100644 --- a/usr.sbin/arp/arp.4 +++ b/usr.sbin/arp/arp.4 @@ -120,6 +120,12 @@ of the .Xr sysctl 3 MIB. .Bl -tag -width "log_arp_permanent_modify" +.It Va allow_multicast +Should the kernel install ARP entries with multicast bit set in +the hardware address. +Installing such entries is RFC 1812 violation, but some prorietary +load balancing techniques require routers on network to do so. +Turned off by default. .It Va log_arp_movements Should the kernel log movements of IP addresses from one hardware address to an other. @@ -203,6 +209,11 @@ entry in the local ARP table. This error will only be logged if the sysctl .Va net.link.ether.inet.log_arp_permanent_modify is set to 1, which is the system's default behaviour. +.It "arp: %x:%x:%x:%x:%x:%x is multicast" +Kernel refused to install an entry with multicast hardware address. +If you really want such addresses being installed, set the sysctl +.Va net.link.ether.inet.allow_multicast +to a positive value. .El .Sh SEE ALSO .Xr inet 4 , -- cgit v1.3 From c4edfee458b25a53bd3bcd4d68692b3b4730c8d0 Mon Sep 17 00:00:00 2001 From: Gavin Atkinson Date: Wed, 5 Sep 2012 18:27:45 +0000 Subject: - If the PCIe "SLOT" flag is set, include this in the capability output - Fix printing of PCIe interrupt number, the shift was incorrect. MFC after: 1 week --- usr.sbin/pciconf/cap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index 4eed9ca61964e..51e789ebf4e81 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -403,8 +403,10 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) printf("type %d", (flags & PCIM_EXP_FLAGS_TYPE) >> 4); break; } + if (flags & PCIM_EXP_FLAGS_SLOT) + printf(" slot"); if (flags & PCIM_EXP_FLAGS_IRQ) - printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 8); + printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 9); val = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_DEVICE_CAP, 4); flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_DEVICE_CTL, 2); printf(" max data %d(%d)", -- cgit v1.3 From c53a166889d00118efe78c7d81ee7facf2d842e7 Mon Sep 17 00:00:00 2001 From: Josh Paetzel Date: Thu, 6 Sep 2012 14:59:53 +0000 Subject: Add TRIM support, enabled by default. Fix a bug installing components from a localPath. Allow autosizing of any partition, not just the last partition. Adjust how ZFS is laid out to work with Boot Environments. Submitted by: kmoore Obtained from: PC-BSD MFC after: 3 days --- .../pc-sysinstall/backend/functions-bsdlabel.sh | 43 +++++++++++++++++++++- .../pc-sysinstall/backend/functions-cleanup.sh | 6 +-- usr.sbin/pc-sysinstall/backend/functions-disk.sh | 9 +++++ .../backend/functions-installcomponents.sh | 12 +++++- .../pc-sysinstall/backend/functions-mountdisk.sh | 23 +++++++++++- usr.sbin/pc-sysinstall/backend/functions-newfs.sh | 16 ++------ 6 files changed, 88 insertions(+), 21 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pc-sysinstall/backend/functions-bsdlabel.sh b/usr.sbin/pc-sysinstall/backend/functions-bsdlabel.sh index e76721caee50b..0eedfad02bfe6 100755 --- a/usr.sbin/pc-sysinstall/backend/functions-bsdlabel.sh +++ b/usr.sbin/pc-sysinstall/backend/functions-bsdlabel.sh @@ -164,6 +164,38 @@ gen_glabel_name() export VAL="${NAME}${NUM}" }; +# Function to determine the size we can safely use when 0 is specified +get_autosize() +{ + # Disk tag to look for + dTag="$1" + + # Total MB Avail + get_disk_mediasize_mb "$2" + local _aSize=$VAL + + while read line + do + # Check for data on this slice + echo $line | grep -q "^${_dTag}-part=" 2>/dev/null + if [ $? -ne 0 ] ; then continue ; fi + + get_value_from_string "${line}" + STRING="$VAL" + + # Get the size of this partition + SIZE=`echo $STRING | tr -s '\t' ' ' | cut -d ' ' -f 2` + if [ $SIZE -eq 0 ] ; then continue ; fi + _aSize=`expr $_aSize - $SIZE` + done <${CFGF} + + # Pad the size a bit + _aSize=`expr $_aSize - 2` + + VAL="$_aSize" + export VAL +}; + # Function to setup partitions using gpart setup_gpart_partitions() { @@ -173,6 +205,7 @@ setup_gpart_partitions() local _sNum="$4" local _pType="$5" FOUNDPARTS="1" + USEDAUTOSIZE=0 # Lets read in the config file now and setup our partitions if [ "${_pType}" = "gpt" ] ; then @@ -245,7 +278,15 @@ setup_gpart_partitions() if [ "$SIZE" = "0" ] then - SOUT="" + if [ $USEDAUTOSIZE -eq 1 ] ; then + exit_err "ERROR: You can not have two partitions with a size of 0 specified!" + fi + case ${_pType} in + gpt|apm) get_autosize "${_dTag}" "$_pDisk" ;; + *) get_autosize "${_dTag}" "$_wSlice" ;; + esac + SOUT="-s ${VAL}M" + USEDAUTOSIZE=1 else SOUT="-s ${SIZE}M" fi diff --git a/usr.sbin/pc-sysinstall/backend/functions-cleanup.sh b/usr.sbin/pc-sysinstall/backend/functions-cleanup.sh index 255dbc8aaebba..787f8915b64b3 100755 --- a/usr.sbin/pc-sysinstall/backend/functions-cleanup.sh +++ b/usr.sbin/pc-sysinstall/backend/functions-cleanup.sh @@ -49,7 +49,7 @@ zfs_cleanup_unmount() # Creating a dedicated "/boot" partition cat ${FSMNT}/boot/loader.conf 2>/dev/null | grep -q "vfs.root.mountfrom=" 2>/dev/null if [ $? -ne 0 ] ; then - echo "vfs.root.mountfrom=\"zfs:${ZPOOLNAME}\"" >> ${FSMNT}/boot/loader.conf + echo "vfs.root.mountfrom=\"zfs:${ZPOOLNAME}/ROOT/default\"" >> ${FSMNT}/boot/loader.conf fi export FOUNDZFSROOT="${ZPOOLNAME}" fi @@ -195,8 +195,8 @@ setup_fstab() if [ $? -eq 0 ] ; then if [ "${PARTFS}" = "ZFS" ] ; then ROOTFSTYPE="zfs" - XPOOLNAME=$(get_zpool_name "${PARTDEV}") - ROOTFS="${ZPOOLNAME}" + ZPOOLNAME=$(get_zpool_name "${PARTDEV}") + ROOTFS="${ZPOOLNAME}/ROOT/default" else ROOTFS="${DEVICE}" ROOTFSTYPE="ufs" diff --git a/usr.sbin/pc-sysinstall/backend/functions-disk.sh b/usr.sbin/pc-sysinstall/backend/functions-disk.sh index 8e8cbfdfb73cd..b1b815d7d93d8 100755 --- a/usr.sbin/pc-sysinstall/backend/functions-disk.sh +++ b/usr.sbin/pc-sysinstall/backend/functions-disk.sh @@ -224,6 +224,15 @@ get_disk_mediasize() export VAL="${mediasize}" }; +# Function which returns a target disks mediasize in megabytes +get_disk_mediasize_mb() +{ + mediasize=`diskinfo -v ${1} | grep "# mediasize in bytes" | tr -s ' ' | cut -f 2` + mediasize=`expr $mediasize / 1024` + mediasize=`expr $mediasize / 1024` + export VAL="${mediasize}" +}; + # Function to delete all gparts before starting an install delete_all_gpart() { diff --git a/usr.sbin/pc-sysinstall/backend/functions-installcomponents.sh b/usr.sbin/pc-sysinstall/backend/functions-installcomponents.sh index 05e4d49c36e25..f388dd4e5d802 100755 --- a/usr.sbin/pc-sysinstall/backend/functions-installcomponents.sh +++ b/usr.sbin/pc-sysinstall/backend/functions-installcomponents.sh @@ -71,8 +71,16 @@ copy_component() fetch_file "${FTPPATH}/${COMPFILEDIR}/${SUBDIR}/${CFILE}" "${FSMNT}/${COMPTMPDIR}/${CFILE}" "0" RESULT="$?" ;; - - sftp) ;; + local) + get_value_from_cfg localPath + if [ -z "$VAL" ]; then + exit_err "Install medium was set to local, but no localPath was provided!" + fi + LOCALPATH=$VAL + cp ${LOCALPATH}/${COMPFILEDIR}/${SUBDIR}/${CFILE} \ + ${FSMNT}/${COMPTMPDIR} >>${LOGOUT} 2>>${LOGOUT} + RESULT="$?" + ;; esac if [ "${RESULT}" != "0" ] diff --git a/usr.sbin/pc-sysinstall/backend/functions-mountdisk.sh b/usr.sbin/pc-sysinstall/backend/functions-mountdisk.sh index 6d785b9addeb8..c90c0580d5677 100755 --- a/usr.sbin/pc-sysinstall/backend/functions-mountdisk.sh +++ b/usr.sbin/pc-sysinstall/backend/functions-mountdisk.sh @@ -81,15 +81,34 @@ mount_partition() done if [ "${ZMNT}" = "/" ] ; then - ZNAME="" + # If creating ZFS / dataset, give it name that beadm works with + ZNAME="/ROOT/default" + ZMKMNT="" + echo_log "zfs create $zcopt -p ${ZPOOLNAME}/ROOT" + rc_halt "zfs create $zcopt -p ${ZPOOLNAME}/ROOT" + echo_log "zfs create $zcopt -p ${ZPOOLNAME}${ZNAME}" + rc_halt "zfs create $zcopt -p ${ZPOOLNAME}${ZNAME}" else ZNAME="${ZMNT}" + ZMKMNT="${ZMNT}" echo_log "zfs create $zcopt -p ${ZPOOLNAME}${ZNAME}" rc_halt "zfs create $zcopt -p ${ZPOOLNAME}${ZNAME}" fi sleep 2 if [ -z "$zcopt" ] ; then - rc_halt "zfs set mountpoint=${FSMNT}${ZNAME} ${ZPOOLNAME}${ZNAME}" + rc_halt "zfs set mountpoint=${FSMNT}${ZMKMNT} ${ZPOOLNAME}${ZNAME}" + fi + + # Do we need to make this / zfs dataset bootable? + if [ "$ZMNT" = "/" ] ; then + echo_log "Stamping ${ZPOOLNAME}/ROOT/default as bootfs" + rc_halt "zpool set bootfs=${ZPOOLNAME}/ROOT/default ${ZPOOLNAME}" + fi + + # Do we need to make this /boot zfs dataset bootable? + if [ "$ZMNT" = "/boot" ] ; then + echo_log "Stamping ${ZPOOLNAME}${ZMNT} as bootfs" + rc_halt "zpool set bootfs=${ZPOOLNAME}${ZMNT} ${ZPOOLNAME}" fi # If no ZFS options, we can skip diff --git a/usr.sbin/pc-sysinstall/backend/functions-newfs.sh b/usr.sbin/pc-sysinstall/backend/functions-newfs.sh index 1ebf922cdf743..2ab4a83280220 100755 --- a/usr.sbin/pc-sysinstall/backend/functions-newfs.sh +++ b/usr.sbin/pc-sysinstall/backend/functions-newfs.sh @@ -72,16 +72,6 @@ setup_zfs_filesystem() # Disable atime for this zfs partition, speed increase rc_nohalt "zfs set atime=off ${ZPOOLNAME}" - # Check if we need to set a bootable zpool - for i in `echo ${PARTMNT} | sed 's|,| |g'` - do - if [ "${i}" = "/" -o "${i}" = "/boot" ] ; then - if [ "$HAVEBOOT" = "YES" ] ; then continue ; fi - echo_log "Stamping zpool as bootfs" - rc_halt "zpool set bootfs=${ZPOOLNAME} ${ZPOOLNAME}" - fi - done - }; # Runs newfs on all the partiions which we've setup with bsdlabel @@ -144,7 +134,7 @@ setup_filesystems() UFS) echo_log "NEWFS: ${PARTDEV} - ${PARTFS}" sleep 2 - rc_halt "newfs ${PARTXTRAOPTS} ${PARTDEV}${EXT}" + rc_halt "newfs -t ${PARTXTRAOPTS} ${PARTDEV}${EXT}" sleep 2 rc_halt "sync" rc_halt "glabel label ${PARTLABEL} ${PARTDEV}${EXT}" @@ -160,7 +150,7 @@ setup_filesystems() UFS+S) echo_log "NEWFS: ${PARTDEV} - ${PARTFS}" sleep 2 - rc_halt "newfs ${PARTXTRAOPTS} -U ${PARTDEV}${EXT}" + rc_halt "newfs -t ${PARTXTRAOPTS} -U ${PARTDEV}${EXT}" sleep 2 rc_halt "sync" rc_halt "glabel label ${PARTLABEL} ${PARTDEV}${EXT}" @@ -175,7 +165,7 @@ setup_filesystems() UFS+SUJ) echo_log "NEWFS: ${PARTDEV} - ${PARTFS}" sleep 2 - rc_halt "newfs ${PARTXTRAOPTS} -U ${PARTDEV}${EXT}" + rc_halt "newfs -t ${PARTXTRAOPTS} -U ${PARTDEV}${EXT}" sleep 2 rc_halt "sync" rc_halt "tunefs -j enable ${PARTDEV}${EXT}" -- cgit v1.3 From d6d3f01e0a3395c1fae34a3c4be7b051cb2d7581 Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Sat, 8 Sep 2012 06:41:54 +0000 Subject: Merge the projects/pf/head branch, that was worked on for last six months, into head. The most significant achievements in the new code: o Fine grained locking, thus much better performance. o Fixes to many problems in pf, that were specific to FreeBSD port. New code doesn't have that many ifdefs and much less OpenBSDisms, thus is more attractive to our developers. Those interested in details, can browse through SVN log of the projects/pf/head branch. And for reference, here is exact list of revisions merged: r232043, r232044, r232062, r232148, r232149, r232150, r232298, r232330, r232332, r232340, r232386, r232390, r232391, r232605, r232655, r232656, r232661, r232662, r232663, r232664, r232673, r232691, r233309, r233782, r233829, r233830, r233834, r233835, r233836, r233865, r233866, r233868, r233873, r234056, r234096, r234100, r234108, r234175, r234187, r234223, r234271, r234272, r234282, r234307, r234309, r234382, r234384, r234456, r234486, r234606, r234640, r234641, r234642, r234644, r234651, r235505, r235506, r235535, r235605, r235606, r235826, r235991, r235993, r236168, r236173, r236179, r236180, r236181, r236186, r236223, r236227, r236230, r236252, r236254, r236298, r236299, r236300, r236301, r236397, r236398, r236399, r236499, r236512, r236513, r236525, r236526, r236545, r236548, r236553, r236554, r236556, r236557, r236561, r236570, r236630, r236672, r236673, r236679, r236706, r236710, r236718, r237154, r237155, r237169, r237314, r237363, r237364, r237368, r237369, r237376, r237440, r237442, r237751, r237783, r237784, r237785, r237788, r237791, r238421, r238522, r238523, r238524, r238525, r239173, r239186, r239644, r239652, r239661, r239773, r240125, r240130, r240131, r240136, r240186, r240196, r240212. I'd like to thank people who participated in early testing: Tested by: Florian Smeets Tested by: Chekaluk Vitaly Tested by: Ben Wilber Tested by: Ian FREISLICH --- UPDATING | 4 + contrib/pf/man/pf.4 | 24 +- contrib/pf/man/pf.conf.5 | 40 +- contrib/pf/pfctl/parse.y | 43 +- contrib/pf/pfctl/pf_print_state.c | 5 - contrib/pf/pfctl/pfctl.c | 17 - contrib/pf/pfctl/pfctl_parser.c | 6 - contrib/pf/pfctl/pfctl_table.c | 3 +- sys/contrib/altq/altq/altq_cbq.c | 3 +- sys/contrib/altq/altq/altq_hfsc.c | 3 +- sys/contrib/altq/altq/altq_priq.c | 4 +- sys/contrib/altq/altq/altq_subr.c | 8 +- sys/contrib/pf/net/if_pflog.c | 231 +- sys/contrib/pf/net/if_pflog.h | 39 +- sys/contrib/pf/net/if_pflow.h | 126 - sys/contrib/pf/net/if_pfsync.c | 2357 +++-------- sys/contrib/pf/net/if_pfsync.h | 38 +- sys/contrib/pf/net/pf.c | 4394 +++++++------------- sys/contrib/pf/net/pf_if.c | 753 ++-- sys/contrib/pf/net/pf_ioctl.c | 2846 +++++-------- sys/contrib/pf/net/pf_lb.c | 500 +-- sys/contrib/pf/net/pf_mtag.h | 22 - sys/contrib/pf/net/pf_norm.c | 780 +--- sys/contrib/pf/net/pf_osfp.c | 234 +- sys/contrib/pf/net/pf_ruleset.c | 55 +- sys/contrib/pf/net/pf_table.c | 1059 ++--- sys/contrib/pf/net/pfvar.h | 687 +-- sys/modules/pf/Makefile | 5 +- sys/net/if.c | 15 +- sys/netinet/in_gif.c | 2 - sys/netinet/ip_icmp.c | 2 - sys/netinet/ipfw/ip_fw2.c | 14 +- sys/netinet/raw_ip.c | 3 - sys/netinet/tcp_subr.c | 1 - sys/netinet6/icmp6.c | 4 - sys/netinet6/in6_gif.c | 2 - sys/netipsec/ipsec_input.c | 2 - sys/netipsec/ipsec_output.c | 2 - sys/netipsec/xform_ipip.c | 2 - sys/sys/mbuf.h | 12 +- sys/sys/param.h | 2 +- usr.bin/netstat/if.c | 37 +- usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt | 2 +- usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c | 5 +- usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def | 2 +- 45 files changed, 4673 insertions(+), 9722 deletions(-) delete mode 100644 sys/contrib/pf/net/if_pflow.h (limited to 'usr.sbin') diff --git a/UPDATING b/UPDATING index 73a107c141ed1..e0576490620cd 100644 --- a/UPDATING +++ b/UPDATING @@ -24,6 +24,10 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW: disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20120908: + The pf(4) packet filter ABI has been changed. pfctl(8) and + snmp_pf module need to be recompiled to work with new kernel. + 20120828: A new ZFS feature flag "com.delphix:empty_bpobj" has been merged to -HEAD. Pools that have empty_bpobj in active state can not be diff --git a/contrib/pf/man/pf.4 b/contrib/pf/man/pf.4 index 936a5a834986d..635078dbee249 100644 --- a/contrib/pf/man/pf.4 +++ b/contrib/pf/man/pf.4 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 17 2011 +.Dd June 29 2012 .Dt PF 4 .Os .Sh NAME @@ -75,6 +75,25 @@ separated by characters, similar to how file system hierarchies are laid out. The final component of the anchor path is the anchor under which operations will be performed. +.Sh SYSCTL VARIABLES AND LOADER TUNABLES +The following +.Xr loader 8 +tunables are available. +.Bl -tag -width indent +.It Va net.pf.states_hashsize +Size of hash tables that store states. +Should be power of 2. +Default value is 32768. +.It Va net.pf.source_nodes_hashsize +Size of hash table that store source nodes. +Should be power of 2. +Default value is 8192. +.El +.Pp +Read only +.Xr sysctl 8 +variables with matching names are provided to obtain current values +at runtime. .Sh IOCTL INTERFACE .Nm supports the following @@ -351,7 +370,6 @@ struct pf_status { u_int64_t scounters[SCNT_MAX]; u_int64_t pcounters[2][2][3]; u_int64_t bcounters[2][2]; - u_int64_t stateid; u_int32_t running; u_int32_t states; u_int32_t src_nodes; @@ -493,7 +511,7 @@ struct pfioc_limit { }; enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLES, PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; + PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; .Ed .It Dv DIOCGETLIMIT Fa "struct pfioc_limit *pl" Get the hard diff --git a/contrib/pf/man/pf.conf.5 b/contrib/pf/man/pf.conf.5 index dfec26458fd6b..fc86111372138 100644 --- a/contrib/pf/man/pf.conf.5 +++ b/contrib/pf/man/pf.conf.5 @@ -28,7 +28,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd January 31 2009 +.Dd June 29 2012 .Dt PF.CONF 5 .Os .Sh NAME @@ -1421,7 +1421,7 @@ has the socket open where the packet is sourced from or destined to (depending on which socket is local). This is in addition to the normal information logged. .Pp -Due to the problems described in the BUGS section only the first packet +Only the first packet logged via .Ar log (all, user) will have the user credentials logged when using stateful matching. @@ -1479,13 +1479,6 @@ of the following keywords: .Bl -tag -width xxxxxxxxxxxxxx -compact .It Ar any Any address. -.It Ar route Aq Ar label -Any address whose associated route has label -.Aq Ar label . -See -.Xr route 4 -and -.Xr route 8 . .It Ar no-route Any address which is not currently routable. .It Ar urpf-failed @@ -1594,7 +1587,6 @@ pass in proto tcp from any to any port 25 pass in proto tcp from 10.0.0.0/8 port \*(Gt 1024 \e to ! 10.1.2.3 port != ssh pass in proto tcp from any os "OpenBSD" -pass in proto tcp from route "DTAG" .Ed .It Ar all This is equivalent to "from any to any". @@ -2949,9 +2941,9 @@ proto-list = ( proto-name | proto-number ) [ [ "," ] proto-list ] hosts = "all" | "from" ( "any" | "no-route" | "urpf-failed" | "self" | host | - "{" host-list "}" | "route" string ) [ port ] [ os ] + "{" host-list "}" ) [ port ] [ os ] "to" ( "any" | "no-route" | "self" | host | - "{" host-list "}" | "route" string ) [ port ] + "{" host-list "}" ) [ port ] ipspec = "any" | host | "{" host-list "}" host = [ "!" ] ( address [ "/" mask-bits ] | "\*(Lt" string "\*(Gt" ) @@ -3048,28 +3040,6 @@ Protocol name database. .It Pa /etc/services Service name database. .El -.Sh BUGS -Due to a lock order reversal (LOR) with the socket layer, the use of the -.Ar group -and -.Ar user -filter parameter in conjuction with a Giant-free netstack -can result in a deadlock. -A workaround is available under the -.Va debug.pfugidhack -sysctl which is automatically enabled when a -.Ar user -/ -.Ar group -rule is added or -.Ar log (user) -is specified. -.Pp -Route labels are not supported by the -.Fx -.Xr route 4 -system. -Rules with a route label do not match any traffic. .Sh SEE ALSO .Xr altq 4 , .Xr carp 4 , @@ -3080,7 +3050,6 @@ Rules with a route label do not match any traffic. .Xr pf 4 , .Xr pflow 4 , .Xr pfsync 4 , -.Xr route 4 , .Xr tcp 4 , .Xr udp 4 , .Xr hosts 5 , @@ -3090,7 +3059,6 @@ Rules with a route label do not match any traffic. .Xr ftp-proxy 8 , .Xr pfctl 8 , .Xr pflogd 8 , -.Xr route 8 .Sh HISTORY The .Nm diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y index f798cacc2e567..99c26c0a33dfc 100644 --- a/contrib/pf/pfctl/parse.y +++ b/contrib/pf/pfctl/parse.y @@ -159,8 +159,7 @@ 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_PFLOW }; + PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; @@ -451,7 +450,7 @@ int parseport(char *, struct range *r, int); %token QUEUE PRIORITY QLIMIT RTABLE %token LOAD RULESET_OPTIMIZATION %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE -%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW +%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS %token DIVERTTO DIVERTREPLY %token STRING @@ -2081,15 +2080,6 @@ 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_TIMEOUT: if (o->data.timeout.number == PFTM_ADAPTIVE_START || @@ -2909,26 +2899,6 @@ host : STRING { $$->next = NULL; $$->tail = $$; } - | ROUTE STRING { - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) { - free($2); - err(1, "host: calloc"); - } - $$->addr.type = PF_ADDR_RTLABEL; - if (strlcpy($$->addr.v.rtlabelname, $2, - sizeof($$->addr.v.rtlabelname)) >= - sizeof($$->addr.v.rtlabelname)) { - yyerror("route label too long, max %u chars", - sizeof($$->addr.v.rtlabelname) - 1); - free($2); - free($$); - YYERROR; - } - $$->next = NULL; - $$->tail = $$; - free($2); - } ; number : NUMBER @@ -3597,14 +3567,6 @@ 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 = $$; - } | STRING NUMBER { int i; @@ -5320,7 +5282,6 @@ lookup(char *s) { "out", OUT}, { "overload", OVERLOAD}, { "pass", PASS}, - { "pflow", PFLOW}, { "port", PORT}, { "priority", PRIORITY}, { "priq", PRIQ}, diff --git a/contrib/pf/pfctl/pf_print_state.c b/contrib/pf/pfctl/pf_print_state.c index 069851646049b..d6637b445c350 100644 --- a/contrib/pf/pfctl/pf_print_state.c +++ b/contrib/pf/pfctl/pf_print_state.c @@ -119,9 +119,6 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) case PF_ADDR_URPFFAILED: printf("urpf-failed"); return; - case PF_ADDR_RTLABEL: - printf("route \"%s\"", addr->v.rtlabelname); - return; default: printf("?"); return; @@ -339,8 +336,6 @@ print_state(struct pfsync_state *s, int opts) printf(", rule %u", ntohl(s->rule)); if (s->state_flags & PFSTATE_SLOPPY) printf(", sloppy"); - if (s->state_flags & PFSTATE_PFLOW) - printf(", pflow"); if (s->sync_flags & PFSYNC_FLAG_SRCNODE) printf(", source-track"); if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE) diff --git a/contrib/pf/pfctl/pfctl.c b/contrib/pf/pfctl/pfctl.c index 8b07a2be3bc8b..90a2bb5b7c7dd 100644 --- a/contrib/pf/pfctl/pfctl.c +++ b/contrib/pf/pfctl/pfctl.c @@ -144,7 +144,6 @@ static const struct { { "states", PF_LIMIT_STATES }, { "src-nodes", PF_LIMIT_SRC_NODES }, { "frags", PF_LIMIT_FRAGS }, - { "tables", PF_LIMIT_TABLES }, { "table-entries", PF_LIMIT_TABLE_ENTRIES }, { NULL, 0 } }; @@ -1553,9 +1552,6 @@ pfctl_fopen(const char *name, const char *mode) void pfctl_init_options(struct pfctl *pf) { - int64_t mem; - int mib[2]; - size_t size; pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; @@ -1581,21 +1577,8 @@ 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_TABLES] = PFR_KTABLE_HIWAT; pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT; - mib[0] = CTL_HW; -#ifdef __FreeBSD__ - mib[1] = HW_PHYSMEM; -#else - mib[1] = HW_PHYSMEM64; -#endif - size = sizeof(mem); - if (sysctl(mib, 2, &mem, &size, NULL, 0) == -1) - err(1, "sysctl"); - if (mem <= 100*1024*1024) - pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT_SMALL; - pf->debug = PF_DEBUG_URGENT; } diff --git a/contrib/pf/pfctl/pfctl_parser.c b/contrib/pf/pfctl/pfctl_parser.c index d45b9b78997d8..f2489956daa46 100644 --- a/contrib/pf/pfctl/pfctl_parser.c +++ b/contrib/pf/pfctl/pfctl_parser.c @@ -955,12 +955,6 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) printf("sloppy"); opts = 0; } - if (r->rule_flag & PFRULE_PFLOW) { - if (!opts) - printf(", "); - printf("pflow"); - opts = 0; - } for (i = 0; i < PFTM_MAX; ++i) if (r->timeout[i]) { int j; diff --git a/contrib/pf/pfctl/pfctl_table.c b/contrib/pf/pfctl/pfctl_table.c index 257c014850115..f3a1efdd06ba3 100644 --- a/contrib/pf/pfctl/pfctl_table.c +++ b/contrib/pf/pfctl/pfctl_table.c @@ -621,8 +621,7 @@ print_iface(struct pfi_kif *p, int opts) if (!(opts & PF_OPT_VERBOSE2)) return; printf("\tCleared: %s", ctime(&tzero)); - printf("\tReferences: [ States: %-18d Rules: %-18d ]\n", - p->pfik_states, p->pfik_rules); + printf("\tReferences: %-18d\n", p->pfik_rulerefs); for (i = 0; i < 8; i++) { af = (i>>2) & 1; dir = (i>>1) &1; diff --git a/sys/contrib/altq/altq/altq_cbq.c b/sys/contrib/altq/altq/altq_cbq.c index da12cf81c0438..0a337929fe44b 100644 --- a/sys/contrib/altq/altq/altq_cbq.c +++ b/sys/contrib/altq/altq/altq_cbq.c @@ -271,10 +271,9 @@ cbq_add_altq(struct pf_altq *a) return (ENODEV); /* allocate and initialize cbq_state_t */ - cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK); + cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_NOWAIT | M_ZERO); if (cbqp == NULL) return (ENOMEM); - bzero(cbqp, sizeof(cbq_state_t)); CALLOUT_INIT(&cbqp->cbq_callout); cbqp->cbq_qlen = 0; cbqp->ifnp.ifq_ = &ifp->if_snd; /* keep the ifq */ diff --git a/sys/contrib/altq/altq/altq_hfsc.c b/sys/contrib/altq/altq/altq_hfsc.c index bf4f39ce37f0f..9c91c43f5c103 100644 --- a/sys/contrib/altq/altq/altq_hfsc.c +++ b/sys/contrib/altq/altq/altq_hfsc.c @@ -200,10 +200,9 @@ hfsc_add_altq(struct pf_altq *a) if (!ALTQ_IS_READY(&ifp->if_snd)) return (ENODEV); - hif = malloc(sizeof(struct hfsc_if), M_DEVBUF, M_WAITOK); + hif = malloc(sizeof(struct hfsc_if), M_DEVBUF, M_NOWAIT | M_ZERO); if (hif == NULL) return (ENOMEM); - bzero(hif, sizeof(struct hfsc_if)); hif->hif_eligible = ellist_alloc(); if (hif->hif_eligible == NULL) { diff --git a/sys/contrib/altq/altq/altq_priq.c b/sys/contrib/altq/altq/altq_priq.c index 770d4bf458b55..0ec6534ab4127 100644 --- a/sys/contrib/altq/altq/altq_priq.c +++ b/sys/contrib/altq/altq/altq_priq.c @@ -132,11 +132,9 @@ priq_add_altq(struct pf_altq *a) if (!ALTQ_IS_READY(&ifp->if_snd)) return (ENODEV); - pif = malloc(sizeof(struct priq_if), - M_DEVBUF, M_WAITOK); + pif = malloc(sizeof(struct priq_if), M_DEVBUF, M_NOWAIT | M_ZERO); if (pif == NULL) return (ENOMEM); - bzero(pif, sizeof(struct priq_if)); pif->pif_bandwidth = a->ifbandwidth; pif->pif_maxpri = -1; pif->pif_ifq = &ifp->if_snd; diff --git a/sys/contrib/altq/altq/altq_subr.c b/sys/contrib/altq/altq/altq_subr.c index f5fe990aa16b8..2d7ce758221a2 100644 --- a/sys/contrib/altq/altq/altq_subr.c +++ b/sys/contrib/altq/altq/altq_subr.c @@ -401,14 +401,11 @@ tbr_set(ifq, profile) return (0); } - IFQ_UNLOCK(ifq); - tbr = malloc(sizeof(struct tb_regulator), - M_DEVBUF, M_WAITOK); - if (tbr == NULL) { /* can not happen */ + tbr = malloc(sizeof(struct tb_regulator), M_DEVBUF, M_NOWAIT | M_ZERO); + if (tbr == NULL) { IFQ_UNLOCK(ifq); return (ENOMEM); } - bzero(tbr, sizeof(struct tb_regulator)); tbr->tbr_rate = TBR_SCALE(profile->rate / 8) / machclk_freq; tbr->tbr_depth = TBR_SCALE(profile->depth); @@ -420,7 +417,6 @@ tbr_set(ifq, profile) tbr->tbr_last = read_machclk(); tbr->tbr_lastop = ALTDQ_REMOVE; - IFQ_LOCK(ifq); otbr = ifq->altq_tbr; ifq->altq_tbr = tbr; /* set the new tbr */ diff --git a/sys/contrib/pf/net/if_pflog.c b/sys/contrib/pf/net/if_pflog.c index 349930ba1df00..20feea2330e14 100644 --- a/sys/contrib/pf/net/if_pflog.c +++ b/sys/contrib/pf/net/if_pflog.c @@ -1,10 +1,10 @@ /* $OpenBSD: if_pflog.c,v 1.26 2007/10/18 21:58:18 mpf Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), - * Angelos D. Keromytis (kermit@csd.uch.gr) and + * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, * in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, @@ -20,7 +20,7 @@ * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or - * modification of this software. + * modification of this software. * You may use this code under the GNU public license if you so wish. Please * contribute changes back to the authors under this freer than GPL license * so that we may further the use of strong encryption without limitations to @@ -33,61 +33,34 @@ * PURPOSE. */ -#ifdef __FreeBSD__ +#include +__FBSDID("$FreeBSD$"); + #include "opt_inet.h" #include "opt_inet6.h" #include "opt_bpf.h" #include "opt_pf.h" -#include -__FBSDID("$FreeBSD$"); - -#ifdef DEV_BPF -#define NBPFILTER DEV_BPF -#else -#define NBPFILTER 0 -#endif - -#ifdef DEV_PFLOG -#define NPFLOG DEV_PFLOG -#else -#define NPFLOG 0 -#endif - -#else /* ! __FreeBSD__ */ -#include "bpfilter.h" -#include "pflog.h" -#endif /* __FreeBSD__ */ - #include -#include +#include #include +#include #include #include -#ifdef __FreeBSD__ -#include -#include -#include -#include #include -#else -#include -#endif +#include #include -#ifdef __FreeBSD__ #include -#endif +#include #include -#include -#include +#include #if defined(INET) || defined(INET6) #include #endif #ifdef INET #include -#include #include #endif @@ -96,14 +69,9 @@ __FBSDID("$FreeBSD$"); #include #endif /* INET6 */ -#include -#include - -#ifdef __FreeBSD__ #ifdef INET #include #endif /* INET */ -#endif /* __FreeBSD__ */ #define PFLOGMTU (32768 + MHLEN + MLEN) @@ -113,170 +81,82 @@ __FBSDID("$FreeBSD$"); #define DPRINTF(x) #endif -void pflogattach(int); -int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *, -#ifdef __FreeBSD__ - struct route *); -#else - struct rtentry *); -#endif -int pflogioctl(struct ifnet *, u_long, caddr_t); -void pflogstart(struct ifnet *); -#ifdef __FreeBSD__ -static int pflog_clone_create(struct if_clone *, int, caddr_t); -static void pflog_clone_destroy(struct ifnet *); -#else -int pflog_clone_create(struct if_clone *, int); -int pflog_clone_destroy(struct ifnet *); -#endif +static int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct route *); +static void pflogattach(int); +static int pflogioctl(struct ifnet *, u_long, caddr_t); +static void pflogstart(struct ifnet *); +static int pflog_clone_create(struct if_clone *, int, caddr_t); +static void pflog_clone_destroy(struct ifnet *); -LIST_HEAD(, pflog_softc) pflogif_list; -#ifdef __FreeBSD__ IFC_SIMPLE_DECLARE(pflog, 1); -#else -struct if_clone pflog_cloner = - IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy); -#endif struct ifnet *pflogifs[PFLOGIFS_MAX]; /* for fast access */ -void +static void pflogattach(int npflog) { int i; - LIST_INIT(&pflogif_list); for (i = 0; i < PFLOGIFS_MAX; i++) pflogifs[i] = NULL; if_clone_attach(&pflog_cloner); } -#ifdef __FreeBSD__ static int pflog_clone_create(struct if_clone *ifc, int unit, caddr_t param) -#else -int -pflog_clone_create(struct if_clone *ifc, int unit) -#endif { struct ifnet *ifp; - struct pflog_softc *pflogif; - int s; if (unit >= PFLOGIFS_MAX) return (EINVAL); - if ((pflogif = malloc(sizeof(*pflogif), - M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) - return (ENOMEM); - - pflogif->sc_unit = unit; -#ifdef __FreeBSD__ - ifp = pflogif->sc_ifp = if_alloc(IFT_PFLOG); + ifp = if_alloc(IFT_PFLOG); if (ifp == NULL) { - free(pflogif, M_DEVBUF); return (ENOSPC); } if_initname(ifp, ifc->ifc_name, unit); -#else - ifp = &pflogif->sc_if; - snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit); -#endif - ifp->if_softc = pflogif; ifp->if_mtu = PFLOGMTU; ifp->if_ioctl = pflogioctl; ifp->if_output = pflogoutput; ifp->if_start = pflogstart; -#ifndef __FreeBSD__ - ifp->if_type = IFT_PFLOG; -#endif ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_hdrlen = PFLOG_HDRLEN; if_attach(ifp); -#ifndef __FreeBSD__ - if_alloc_sadl(ifp); -#endif -#if NBPFILTER > 0 -#ifdef __FreeBSD__ bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN); -#else - bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN); -#endif -#endif - s = splnet(); -#ifdef __FreeBSD__ - /* XXX: Why pf(4) lock?! Better add a pflog lock?! */ - PF_LOCK(); -#endif - LIST_INSERT_HEAD(&pflogif_list, pflogif, sc_list); pflogifs[unit] = ifp; -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (0); } -#ifdef __FreeBSD__ static void pflog_clone_destroy(struct ifnet *ifp) -#else -int -pflog_clone_destroy(struct ifnet *ifp) -#endif { - struct pflog_softc *pflogif = ifp->if_softc; - int s; + int i; - s = splnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - pflogifs[pflogif->sc_unit] = NULL; - LIST_REMOVE(pflogif, sc_list); -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); + for (i = 0; i < PFLOGIFS_MAX; i++) + if (pflogifs[i] == ifp) + pflogifs[i] = NULL; -#if NBPFILTER > 0 bpfdetach(ifp); -#endif if_detach(ifp); -#ifdef __FreeBSD__ if_free(ifp); -#endif - free(pflogif, M_DEVBUF); -#ifndef __FreeBSD__ - return (0); -#endif } /* * Start output on the pflog interface. */ -void +static void pflogstart(struct ifnet *ifp) { struct mbuf *m; -#ifndef __FreeBSD__ - int s; -#endif for (;;) { -#ifdef __FreeBSD__ IF_LOCK(&ifp->if_snd); _IF_DROP(&ifp->if_snd); _IF_DEQUEUE(&ifp->if_snd, m); IF_UNLOCK(&ifp->if_snd); -#else - s = splnet(); - IF_DROP(&ifp->if_snd); - IF_DEQUEUE(&ifp->if_snd, m); - splx(s); -#endif if (m == NULL) return; @@ -285,35 +165,24 @@ pflogstart(struct ifnet *ifp) } } -int +static int pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, -#ifdef __FreeBSD__ struct route *rt) -#else - struct rtentry *rt) -#endif { m_freem(m); return (0); } /* ARGSUSED */ -int +static int pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { switch (cmd) { case SIOCSIFFLAGS: -#ifdef __FreeBSD__ if (ifp->if_flags & IFF_UP) ifp->if_drv_flags |= IFF_DRV_RUNNING; else ifp->if_drv_flags &= ~IFF_DRV_RUNNING; -#else - if (ifp->if_flags & IFF_UP) - ifp->if_flags |= IFF_RUNNING; - else - ifp->if_flags &= ~IFF_RUNNING; -#endif break; default: return (ENOTTY); @@ -322,12 +191,11 @@ pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) return (0); } -int +static int pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir, u_int8_t reason, struct pf_rule *rm, struct pf_rule *am, - struct pf_ruleset *ruleset, struct pf_pdesc *pd) + struct pf_ruleset *ruleset, struct pf_pdesc *pd, int lookupsafe) { -#if NBPFILTER > 0 struct ifnet *ifn; struct pfloghdr hdr; @@ -354,23 +222,18 @@ pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir, strlcpy(hdr.ruleset, ruleset->anchor->name, sizeof(hdr.ruleset)); } - if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done) -#ifdef __FreeBSD__ - /* - * XXX: This should not happen as we force an early lookup - * via debug.pfugidhack - */ - ; /* empty */ -#else - pd->lookup.done = pf_socket_lookup(dir, pd); -#endif - if (pd->lookup.done > 0) { + /* + * XXXGL: we avoid pf_socket_lookup() when we are holding + * state lock, since this leads to unsafe LOR. + * These conditions are very very rare, however. + */ + if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done && lookupsafe) + pd->lookup.done = pf_socket_lookup(dir, pd, m); + if (pd->lookup.done > 0) hdr.uid = pd->lookup.uid; - hdr.pid = pd->lookup.pid; - } else { + else hdr.uid = UID_MAX; - hdr.pid = NO_PID; - } + hdr.pid = NO_PID; hdr.rule_uid = rm->cuid; hdr.rule_pid = rm->cpid; hdr.dir = dir; @@ -387,18 +250,11 @@ pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir, ifn->if_opackets++; ifn->if_obytes += m->m_pkthdr.len; -#ifdef __FreeBSD__ BPF_MTAP2(ifn, &hdr, PFLOG_HDRLEN, m); -#else - bpf_mtap_hdr(ifn->if_bpf, (char *)&hdr, PFLOG_HDRLEN, m, - BPF_DIRECTION_OUT); -#endif -#endif return (0); } -#ifdef __FreeBSD__ static int pflog_modevent(module_t mod, int type, void *data) { @@ -407,14 +263,14 @@ pflog_modevent(module_t mod, int type, void *data) switch (type) { case MOD_LOAD: pflogattach(1); - PF_LOCK(); + PF_RULES_WLOCK(); pflog_packet_ptr = pflog_packet; - PF_UNLOCK(); + PF_RULES_WUNLOCK(); break; case MOD_UNLOAD: - PF_LOCK(); + PF_RULES_WLOCK(); pflog_packet_ptr = NULL; - PF_UNLOCK(); + PF_RULES_WUNLOCK(); if_clone_detach(&pflog_cloner); break; default: @@ -432,4 +288,3 @@ static moduledata_t pflog_mod = { "pflog", pflog_modevent, 0 }; DECLARE_MODULE(pflog, pflog_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(pflog, PFLOG_MODVER); MODULE_DEPEND(pflog, pf, PF_MODVER, PF_MODVER, PF_MODVER); -#endif /* __FreeBSD__ */ diff --git a/sys/contrib/pf/net/if_pflog.h b/sys/contrib/pf/net/if_pflog.h index 5f48f6c752469..0faeb7d46322c 100644 --- a/sys/contrib/pf/net/if_pflog.h +++ b/sys/contrib/pf/net/if_pflog.h @@ -29,16 +29,6 @@ #define PFLOGIFS_MAX 16 -struct pflog_softc { -#ifdef __FreeBSD__ - struct ifnet *sc_ifp; /* the interface pointer */ -#else - struct ifnet sc_if; /* the interface */ -#endif - int sc_unit; - LIST_ENTRY(pflog_softc) sc_list; -}; - #define PFLOG_RULESET_NAME_SIZE 16 struct pfloghdr { @@ -62,40 +52,15 @@ struct pfloghdr { /* minus pad, also used as a signature */ #define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad) -/* XXX remove later when old format logs are no longer needed */ -struct old_pfloghdr { - u_int32_t af; - char ifname[IFNAMSIZ]; - short rnr; - u_short reason; - u_short action; - u_short dir; -}; -#define OLD_PFLOG_HDRLEN sizeof(struct old_pfloghdr) - #ifdef _KERNEL -#ifdef __FreeBSD__ struct pf_rule; struct pf_ruleset; struct pfi_kif; struct pf_pdesc; -#if 0 -typedef int pflog_packet_t(struct pfi_kif *, struct mbuf *, sa_family_t, - u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, - struct pf_ruleset *, struct pf_pdesc *); -extern pflog_packet_t *pflog_packet_ptr; -#endif -#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g,h) do { \ +#define PFLOG_PACKET(i,a,b,c,d,e,f,g,h,di) do { \ if (pflog_packet_ptr != NULL) \ - pflog_packet_ptr(i,a,b,c,d,e,f,g,h); \ + pflog_packet_ptr(i,a,b,c,d,e,f,g,h,di); \ } while (0) -#else /* ! __FreeBSD__ */ -#if NPFLOG > 0 -#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g,h) pflog_packet(i,a,b,c,d,e,f,g,h) -#else -#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g,h) ((void)0) -#endif /* NPFLOG > 0 */ -#endif #endif /* _KERNEL */ #endif /* _NET_IF_PFLOG_H_ */ diff --git a/sys/contrib/pf/net/if_pflow.h b/sys/contrib/pf/net/if_pflow.h deleted file mode 100644 index 35ccbeb4fc3f2..0000000000000 --- a/sys/contrib/pf/net/if_pflow.h +++ /dev/null @@ -1,126 +0,0 @@ -/* $OpenBSD: if_pflow.h,v 1.5 2009/02/27 11:09:36 gollo Exp $ */ - -/* - * Copyright (c) 2008 Henning Brauer - * Copyright (c) 2008 Joerg Goltermann - * - * 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 MIND, 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. - * - * $FreeBSD$ - */ - -#ifndef _NET_IF_PFLOW_H_ -#define _NET_IF_PFLOW_H_ - -#define PFLOW_ID_LEN sizeof(u_int64_t) - -#define PFLOW_MAXFLOWS 30 -#define PFLOW_VERSION 5 -#define PFLOW_ENGINE_TYPE 42 -#define PFLOW_ENGINE_ID 42 -#define PFLOW_MAXBYTES 0xffffffff -#define PFLOW_TIMEOUT 30 - -struct pflow_flow { - u_int32_t src_ip; - u_int32_t dest_ip; - u_int32_t nexthop_ip; - u_int16_t if_index_in; - u_int16_t if_index_out; - u_int32_t flow_packets; - u_int32_t flow_octets; - u_int32_t flow_start; - u_int32_t flow_finish; - u_int16_t src_port; - u_int16_t dest_port; - u_int8_t pad1; - u_int8_t tcp_flags; - u_int8_t protocol; - u_int8_t tos; - u_int16_t src_as; - u_int16_t dest_as; - u_int8_t src_mask; - u_int8_t dest_mask; - u_int16_t pad2; -} __packed; - -#ifdef _KERNEL - -extern int pflow_ok; - -struct pflow_softc { - struct ifnet sc_if; - struct ifnet *sc_pflow_ifp; - - unsigned int sc_count; - unsigned int sc_maxcount; - u_int64_t sc_gcounter; - struct ip_moptions sc_imo; -#ifdef __FreeBSD__ - struct callout sc_tmo; -#else - struct timeout sc_tmo; -#endif - struct in_addr sc_sender_ip; - u_int16_t sc_sender_port; - struct in_addr sc_receiver_ip; - u_int16_t sc_receiver_port; - struct mbuf *sc_mbuf; /* current cumulative mbuf */ - SLIST_ENTRY(pflow_softc) sc_next; -}; - -extern struct pflow_softc *pflowif; - -#endif /* _KERNEL */ - -struct pflow_header { - u_int16_t version; - u_int16_t count; - u_int32_t uptime_ms; - u_int32_t time_sec; - u_int32_t time_nanosec; - u_int32_t flow_sequence; - u_int8_t engine_type; - u_int8_t engine_id; - u_int8_t reserved1; - u_int8_t reserved2; -} __packed; - -#define PFLOW_HDRLEN sizeof(struct pflow_header) - -struct pflowstats { - u_int64_t pflow_flows; - u_int64_t pflow_packets; - u_int64_t pflow_onomem; - u_int64_t pflow_oerrors; -}; - -/* - * Configuration structure for SIOCSETPFLOW SIOCGETPFLOW - */ -struct pflowreq { - struct in_addr sender_ip; - struct in_addr receiver_ip; - u_int16_t receiver_port; - u_int16_t addrmask; -#define PFLOW_MASK_SRCIP 0x01 -#define PFLOW_MASK_DSTIP 0x02 -#define PFLOW_MASK_DSTPRT 0x04 -}; - -#ifdef _KERNEL -int export_pflow(struct pf_state *); -int pflow_sysctl(int *, u_int, void *, size_t *, void *, size_t); -#endif /* _KERNEL */ - -#endif /* _NET_IF_PFLOW_H_ */ diff --git a/sys/contrib/pf/net/if_pfsync.c b/sys/contrib/pf/net/if_pfsync.c index 7da6c2e9c2b8e..28af641ad6fab 100644 --- a/sys/contrib/pf/net/if_pfsync.c +++ b/sys/contrib/pf/net/if_pfsync.c @@ -54,91 +54,44 @@ * 1.173 - correct expire time processing */ -#ifdef __FreeBSD__ -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_pf.h" - #include __FBSDID("$FreeBSD$"); -#define NBPFILTER 1 -#endif /* __FreeBSD__ */ +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_pf.h" #include -#include -#ifdef __FreeBSD__ #include +#include #include -#include -#endif -#include -#include -#include +#include +#include #include -#include -#ifdef __FreeBSD__ -#include -#include #include -#include -#include -#include #include +#include #include -#else -#include -#include -#endif +#include +#include #include -#ifndef __FreeBSD__ -#include -#endif +#include #include -#ifdef __FreeBSD__ #include -#endif #include -#include -#include -#include -#ifdef __FreeBSD__ -#include -#endif +#include +#include -#include #include -#include -#include - -#ifdef INET -#include +#include #include #include -#include -#endif - -#ifdef INET6 -#include -#endif /* INET6 */ - -#ifdef __FreeBSD__ -#include -#else -#include "carp.h" -#if NCARP > 0 #include -#endif -#endif - -#include -#include - -#ifndef __FreeBSD__ -#include "bpfilter.h" -#include "pfsync.h" -#endif +#include +#include +#include +#include #define PFSYNC_MINPKT ( \ sizeof(struct ip) + \ @@ -152,26 +105,22 @@ struct pfsync_pkt { u_int8_t flags; }; -int pfsync_input_hmac(struct mbuf *, int); - -int pfsync_upd_tcp(struct pf_state *, struct pfsync_state_peer *, - struct pfsync_state_peer *); - -int pfsync_in_clr(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_ins(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_iack(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_upd(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_upd_c(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_ureq(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_del(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_del_c(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_bus(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_tdb(struct pfsync_pkt *, struct mbuf *, int, int); -int pfsync_in_eof(struct pfsync_pkt *, struct mbuf *, int, int); - -int pfsync_in_error(struct pfsync_pkt *, struct mbuf *, int, int); - -int (*pfsync_acts[])(struct pfsync_pkt *, struct mbuf *, int, int) = { +static int pfsync_upd_tcp(struct pf_state *, struct pfsync_state_peer *, + struct pfsync_state_peer *); +static int pfsync_in_clr(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_ins(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_iack(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_upd(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_upd_c(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_ureq(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_del(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_del_c(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_bus(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_tdb(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_eof(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_error(struct pfsync_pkt *, struct mbuf *, int, int); + +static int (*pfsync_acts[])(struct pfsync_pkt *, struct mbuf *, int, int) = { pfsync_in_clr, /* PFSYNC_ACT_CLR */ pfsync_in_ins, /* PFSYNC_ACT_INS */ pfsync_in_iack, /* PFSYNC_ACT_INS_ACK */ @@ -194,12 +143,12 @@ struct pfsync_q { }; /* we have one of these for every PFSYNC_S_ */ -int pfsync_out_state(struct pf_state *, struct mbuf *, int); -int pfsync_out_iack(struct pf_state *, struct mbuf *, int); -int pfsync_out_upd_c(struct pf_state *, struct mbuf *, int); -int pfsync_out_del(struct pf_state *, struct mbuf *, int); +static int pfsync_out_state(struct pf_state *, struct mbuf *, int); +static int pfsync_out_iack(struct pf_state *, struct mbuf *, int); +static int pfsync_out_upd_c(struct pf_state *, struct mbuf *, int); +static int pfsync_out_del(struct pf_state *, struct mbuf *, int); -struct pfsync_q pfsync_qs[] = { +static struct pfsync_q pfsync_qs[] = { { pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_INS }, { pfsync_out_iack, sizeof(struct pfsync_ins_ack), PFSYNC_ACT_INS_ACK }, { pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_UPD }, @@ -207,98 +156,71 @@ struct pfsync_q pfsync_qs[] = { { pfsync_out_del, sizeof(struct pfsync_del_c), PFSYNC_ACT_DEL_C } }; -void pfsync_q_ins(struct pf_state *, int); -void pfsync_q_del(struct pf_state *); +static void pfsync_q_ins(struct pf_state *, int); +static void pfsync_q_del(struct pf_state *); + +static void pfsync_update_state(struct pf_state *); struct pfsync_upd_req_item { TAILQ_ENTRY(pfsync_upd_req_item) ur_entry; struct pfsync_upd_req ur_msg; }; -TAILQ_HEAD(pfsync_upd_reqs, pfsync_upd_req_item); struct pfsync_deferral { - TAILQ_ENTRY(pfsync_deferral) pd_entry; - struct pf_state *pd_st; - struct mbuf *pd_m; -#ifdef __FreeBSD__ - struct callout pd_tmo; -#else - struct timeout pd_tmo; -#endif -}; -TAILQ_HEAD(pfsync_deferrals, pfsync_deferral); - -#define PFSYNC_PLSIZE MAX(sizeof(struct pfsync_upd_req_item), \ - sizeof(struct pfsync_deferral)) + struct pfsync_softc *pd_sc; + TAILQ_ENTRY(pfsync_deferral) pd_entry; + u_int pd_refs; + struct callout pd_tmo; -#ifdef notyet -int pfsync_out_tdb(struct tdb *, struct mbuf *, int); -#endif + struct pf_state *pd_st; + struct mbuf *pd_m; +}; struct pfsync_softc { -#ifdef __FreeBSD__ + /* Configuration */ struct ifnet *sc_ifp; -#else - struct ifnet sc_if; -#endif struct ifnet *sc_sync_if; - -#ifdef __FreeBSD__ - uma_zone_t sc_pool; -#else - struct pool sc_pool; -#endif - - struct ip_moptions sc_imo; - - struct in_addr sc_sync_peer; - u_int8_t sc_maxupdates; -#ifdef __FreeBSD__ - int pfsync_sync_ok; -#endif - - struct ip sc_template; - - struct pf_state_queue sc_qs[PFSYNC_S_COUNT]; - size_t sc_len; - - struct pfsync_upd_reqs sc_upd_req_list; - - int sc_defer; - struct pfsync_deferrals sc_deferrals; - u_int sc_deferred; - + struct ip_moptions sc_imo; + struct in_addr sc_sync_peer; + uint32_t sc_flags; +#define PFSYNCF_OK 0x00000001 +#define PFSYNCF_DEFER 0x00000002 +#define PFSYNCF_PUSH 0x00000004 + uint8_t sc_maxupdates; + struct ip sc_template; + struct callout sc_tmo; + struct mtx sc_mtx; + + /* Queued data */ + size_t sc_len; + TAILQ_HEAD(, pf_state) sc_qs[PFSYNC_S_COUNT]; + TAILQ_HEAD(, pfsync_upd_req_item) sc_upd_req_list; + TAILQ_HEAD(, pfsync_deferral) sc_deferrals; + u_int sc_deferred; void *sc_plus; - size_t sc_pluslen; - - u_int32_t sc_ureq_sent; - int sc_bulk_tries; -#ifdef __FreeBSD__ - struct callout sc_bulkfail_tmo; -#else - struct timeout sc_bulkfail_tmo; -#endif - - u_int32_t sc_ureq_received; - struct pf_state *sc_bulk_next; - struct pf_state *sc_bulk_last; -#ifdef __FreeBSD__ - struct callout sc_bulk_tmo; -#else - struct timeout sc_bulk_tmo; -#endif + size_t sc_pluslen; + + /* Bulk update info */ + struct mtx sc_bulk_mtx; + uint32_t sc_ureq_sent; + int sc_bulk_tries; + uint32_t sc_ureq_received; + int sc_bulk_hashid; + uint64_t sc_bulk_stateid; + uint32_t sc_bulk_creatorid; + struct callout sc_bulk_tmo; + struct callout sc_bulkfail_tmo; +}; - TAILQ_HEAD(, tdb) sc_tdb_q; +#define PFSYNC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define PFSYNC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define PFSYNC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) -#ifdef __FreeBSD__ - struct callout sc_tmo; -#else - struct timeout sc_tmo; -#endif -}; +#define PFSYNC_BLOCK(sc) mtx_lock(&(sc)->sc_bulk_mtx) +#define PFSYNC_BUNLOCK(sc) mtx_unlock(&(sc)->sc_bulk_mtx) +#define PFSYNC_BLOCK_ASSERT(sc) mtx_assert(&(sc)->sc_bulk_mtx, MA_OWNED) -#ifdef __FreeBSD__ -static MALLOC_DEFINE(M_PFSYNC, "pfsync", "pfsync data"); +static MALLOC_DEFINE(M_PFSYNC, "pfsync", "pfsync(4) data"); static VNET_DEFINE(struct pfsync_softc *, pfsyncif) = NULL; #define V_pfsyncif VNET(pfsyncif) static VNET_DEFINE(void *, pfsync_swi_cookie) = NULL; @@ -308,14 +230,14 @@ static VNET_DEFINE(struct pfsyncstats, pfsyncstats); static VNET_DEFINE(int, pfsync_carp_adj) = CARP_MAXSKEW; #define V_pfsync_carp_adj VNET(pfsync_carp_adj) +static void pfsync_timeout(void *); +static void pfsync_push(struct pfsync_softc *); static void pfsyncintr(void *); -static int pfsync_multicast_setup(struct pfsync_softc *); +static int pfsync_multicast_setup(struct pfsync_softc *, struct ifnet *, + void *); static void pfsync_multicast_cleanup(struct pfsync_softc *); static int pfsync_init(void); static void pfsync_uninit(void); -static void pfsync_sendout1(int); - -#define schednetisr(NETISR_PFSYNC) swi_sched(V_pfsync_swi_cookie, 0) SYSCTL_NODE(_net, OID_AUTO, pfsync, CTLFLAG_RW, 0, "PFSYNC"); SYSCTL_VNET_STRUCT(_net_pfsync, OID_AUTO, stats, CTLFLAG_RW, @@ -323,85 +245,46 @@ SYSCTL_VNET_STRUCT(_net_pfsync, OID_AUTO, stats, CTLFLAG_RW, "PFSYNC statistics (struct pfsyncstats, net/if_pfsync.h)"); SYSCTL_INT(_net_pfsync, OID_AUTO, carp_demotion_factor, CTLFLAG_RW, &VNET_NAME(pfsync_carp_adj), 0, "pfsync's CARP demotion factor adjustment"); -#else -struct pfsync_softc *pfsyncif = NULL; -struct pfsyncstats pfsyncstats; -#define V_pfsyncstats pfsyncstats -#endif - -void pfsyncattach(int); -#ifdef __FreeBSD__ -int pfsync_clone_create(struct if_clone *, int, caddr_t); -void pfsync_clone_destroy(struct ifnet *); -#else -int pfsync_clone_create(struct if_clone *, int); -int pfsync_clone_destroy(struct ifnet *); -#endif -int pfsync_alloc_scrub_memory(struct pfsync_state_peer *, - struct pf_state_peer *); -void pfsync_update_net_tdb(struct pfsync_tdb *); -int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, -#ifdef __FreeBSD__ - struct route *); -#else - struct rtentry *); -#endif -int pfsyncioctl(struct ifnet *, u_long, caddr_t); -void pfsyncstart(struct ifnet *); -struct mbuf *pfsync_if_dequeue(struct ifnet *); +static int pfsync_clone_create(struct if_clone *, int, caddr_t); +static void pfsync_clone_destroy(struct ifnet *); +static int pfsync_alloc_scrub_memory(struct pfsync_state_peer *, + struct pf_state_peer *); +static int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct route *); +static int pfsyncioctl(struct ifnet *, u_long, caddr_t); -void pfsync_deferred(struct pf_state *, int); -void pfsync_undefer(struct pfsync_deferral *, int); -void pfsync_defer_tmo(void *); +static int pfsync_defer(struct pf_state *, struct mbuf *); +static void pfsync_undefer(struct pfsync_deferral *, int); +static void pfsync_undefer_state(struct pf_state *, int); +static void pfsync_defer_tmo(void *); -void pfsync_request_update(u_int32_t, u_int64_t); -void pfsync_update_state_req(struct pf_state *); +static void pfsync_request_update(u_int32_t, u_int64_t); +static void pfsync_update_state_req(struct pf_state *); -void pfsync_drop(struct pfsync_softc *); -void pfsync_sendout(void); -void pfsync_send_plus(void *, size_t); -void pfsync_timeout(void *); -void pfsync_tdb_timeout(void *); +static void pfsync_drop(struct pfsync_softc *); +static void pfsync_sendout(int); +static void pfsync_send_plus(void *, size_t); -void pfsync_bulk_start(void); -void pfsync_bulk_status(u_int8_t); -void pfsync_bulk_update(void *); -void pfsync_bulk_fail(void *); +static void pfsync_bulk_start(void); +static void pfsync_bulk_status(u_int8_t); +static void pfsync_bulk_update(void *); +static void pfsync_bulk_fail(void *); -#ifdef __FreeBSD__ -/* XXX: ugly */ -#define betoh64 (unsigned long long)be64toh -#define timeout_del callout_stop +#ifdef IPSEC +static void pfsync_update_net_tdb(struct pfsync_tdb *); #endif #define PFSYNC_MAX_BULKTRIES 12 -#ifndef __FreeBSD__ -int pfsync_sync_ok; -#endif -#ifdef __FreeBSD__ VNET_DEFINE(struct ifc_simple_data, pfsync_cloner_data); VNET_DEFINE(struct if_clone, pfsync_cloner); #define V_pfsync_cloner_data VNET(pfsync_cloner_data) #define V_pfsync_cloner VNET(pfsync_cloner) IFC_SIMPLE_DECLARE(pfsync, 1); -#else -struct if_clone pfsync_cloner = - IF_CLONE_INITIALIZER("pfsync", pfsync_clone_create, pfsync_clone_destroy); -#endif -void -pfsyncattach(int npfsync) -{ - if_clone_attach(&pfsync_cloner); -} -int -#ifdef __FreeBSD__ +static int pfsync_clone_create(struct if_clone *ifc, int unit, caddr_t param) -#else -pfsync_clone_create(struct if_clone *ifc, int unit) -#endif { struct pfsync_softc *sc; struct ifnet *ifp; @@ -410,204 +293,99 @@ pfsync_clone_create(struct if_clone *ifc, int unit) if (unit != 0) return (EINVAL); -#ifdef __FreeBSD__ sc = malloc(sizeof(struct pfsync_softc), M_PFSYNC, M_WAITOK | M_ZERO); - sc->pfsync_sync_ok = 1; -#else - pfsync_sync_ok = 1; - sc = malloc(sizeof(*pfsyncif), M_DEVBUF, M_NOWAIT | M_ZERO); -#endif + sc->sc_flags |= PFSYNCF_OK; for (q = 0; q < PFSYNC_S_COUNT; q++) TAILQ_INIT(&sc->sc_qs[q]); -#ifdef __FreeBSD__ - sc->sc_pool = uma_zcreate("pfsync", PFSYNC_PLSIZE, NULL, NULL, NULL, - NULL, UMA_ALIGN_PTR, 0); -#else - pool_init(&sc->sc_pool, PFSYNC_PLSIZE, 0, 0, 0, "pfsync", NULL); -#endif TAILQ_INIT(&sc->sc_upd_req_list); TAILQ_INIT(&sc->sc_deferrals); - sc->sc_deferred = 0; - - TAILQ_INIT(&sc->sc_tdb_q); sc->sc_len = PFSYNC_MINPKT; sc->sc_maxupdates = 128; -#ifndef __FreeBSD__ - sc->sc_imo.imo_membership = (struct in_multi **)malloc( - (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS, - M_WAITOK | M_ZERO); - sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; -#endif - -#ifdef __FreeBSD__ ifp = sc->sc_ifp = if_alloc(IFT_PFSYNC); if (ifp == NULL) { - uma_zdestroy(sc->sc_pool); free(sc, M_PFSYNC); return (ENOSPC); } if_initname(ifp, ifc->ifc_name, unit); -#else - ifp = &sc->sc_if; - snprintf(ifp->if_xname, sizeof ifp->if_xname, "pfsync%d", unit); -#endif ifp->if_softc = sc; ifp->if_ioctl = pfsyncioctl; ifp->if_output = pfsyncoutput; - ifp->if_start = pfsyncstart; ifp->if_type = IFT_PFSYNC; ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_hdrlen = sizeof(struct pfsync_header); ifp->if_mtu = ETHERMTU; -#ifdef __FreeBSD__ + mtx_init(&sc->sc_mtx, "pfsync", NULL, MTX_DEF); + mtx_init(&sc->sc_bulk_mtx, "pfsync bulk", NULL, MTX_DEF); callout_init(&sc->sc_tmo, CALLOUT_MPSAFE); - callout_init_mtx(&sc->sc_bulk_tmo, &pf_task_mtx, 0); - callout_init(&sc->sc_bulkfail_tmo, CALLOUT_MPSAFE); -#else - timeout_set(&sc->sc_tmo, pfsync_timeout, sc); - timeout_set(&sc->sc_bulk_tmo, pfsync_bulk_update, sc); - timeout_set(&sc->sc_bulkfail_tmo, pfsync_bulk_fail, sc); -#endif + callout_init_mtx(&sc->sc_bulk_tmo, &sc->sc_bulk_mtx, 0); + callout_init_mtx(&sc->sc_bulkfail_tmo, &sc->sc_bulk_mtx, 0); if_attach(ifp); -#ifndef __FreeBSD__ - if_alloc_sadl(ifp); - -#if NCARP > 0 - if_addgroup(ifp, "carp"); -#endif -#endif -#if NBPFILTER > 0 -#ifdef __FreeBSD__ bpfattach(ifp, DLT_PFSYNC, PFSYNC_HDRLEN); -#else - bpfattach(&sc->sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN); -#endif -#endif -#ifdef __FreeBSD__ V_pfsyncif = sc; -#else - pfsyncif = sc; -#endif return (0); } -#ifdef __FreeBSD__ -void -#else -int -#endif +static void pfsync_clone_destroy(struct ifnet *ifp) { struct pfsync_softc *sc = ifp->if_softc; -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - timeout_del(&sc->sc_bulkfail_tmo); - timeout_del(&sc->sc_bulk_tmo); - timeout_del(&sc->sc_tmo); -#ifdef __FreeBSD__ - PF_UNLOCK(); - if (!sc->pfsync_sync_ok && carp_demote_adj_p) + /* + * At this stage, everything should have already been + * cleared by pfsync_uninit(), and we have only to + * drain callouts. + */ + while (sc->sc_deferred > 0) { + struct pfsync_deferral *pd = TAILQ_FIRST(&sc->sc_deferrals); + + TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); + sc->sc_deferred--; + if (callout_stop(&pd->pd_tmo)) { + pf_release_state(pd->pd_st); + m_freem(pd->pd_m); + free(pd, M_PFSYNC); + } else { + pd->pd_refs++; + callout_drain(&pd->pd_tmo); + free(pd, M_PFSYNC); + } + } + + callout_drain(&sc->sc_tmo); + callout_drain(&sc->sc_bulkfail_tmo); + callout_drain(&sc->sc_bulk_tmo); + + if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) (*carp_demote_adj_p)(-V_pfsync_carp_adj, "pfsync destroy"); -#else -#if NCARP > 0 - if (!pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, -1); -#endif -#endif -#if NBPFILTER > 0 bpfdetach(ifp); -#endif if_detach(ifp); pfsync_drop(sc); - while (sc->sc_deferred > 0) - pfsync_undefer(TAILQ_FIRST(&sc->sc_deferrals), 0); - -#ifdef __FreeBSD__ - UMA_DESTROY(sc->sc_pool); -#else - pool_destroy(&sc->sc_pool); -#endif -#ifdef __FreeBSD__ if_free(ifp); if (sc->sc_imo.imo_membership) pfsync_multicast_cleanup(sc); + mtx_destroy(&sc->sc_mtx); + mtx_destroy(&sc->sc_bulk_mtx); free(sc, M_PFSYNC); -#else - free(sc->sc_imo.imo_membership, M_IPMOPTS); - free(sc, M_DEVBUF); -#endif -#ifdef __FreeBSD__ V_pfsyncif = NULL; -#else - pfsyncif = NULL; -#endif - -#ifndef __FreeBSD__ - return (0); -#endif -} - -struct mbuf * -pfsync_if_dequeue(struct ifnet *ifp) -{ - struct mbuf *m; -#ifndef __FreeBSD__ - int s; -#endif - -#ifdef __FreeBSD__ - IF_LOCK(&ifp->if_snd); - _IF_DROP(&ifp->if_snd); - _IF_DEQUEUE(&ifp->if_snd, m); - IF_UNLOCK(&ifp->if_snd); -#else - s = splnet(); - IF_DEQUEUE(&ifp->if_snd, m); - splx(s); -#endif - - return (m); -} - -/* - * Start output on the pfsync interface. - */ -void -pfsyncstart(struct ifnet *ifp) -{ - struct mbuf *m; - - while ((m = pfsync_if_dequeue(ifp)) != NULL) { -#ifndef __FreeBSD__ - IF_DROP(&ifp->if_snd); -#endif - m_freem(m); - } } -int +static int pfsync_alloc_scrub_memory(struct pfsync_state_peer *s, struct pf_state_peer *d) { if (s->scrub.scrub_flag && d->scrub == NULL) { -#ifdef __FreeBSD__ - d->scrub = pool_get(&V_pf_state_scrub_pl, PR_NOWAIT | PR_ZERO); -#else - d->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT | PR_ZERO); -#endif + d->scrub = uma_zalloc(V_pf_state_scrub_z, M_NOWAIT | M_ZERO); if (d->scrub == NULL) return (ENOMEM); } @@ -615,99 +393,29 @@ pfsync_alloc_scrub_memory(struct pfsync_state_peer *s, return (0); } -#ifndef __FreeBSD__ -void -pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) -{ - bzero(sp, sizeof(struct pfsync_state)); - - /* copy from state key */ - sp->key[PF_SK_WIRE].addr[0] = st->key[PF_SK_WIRE]->addr[0]; - sp->key[PF_SK_WIRE].addr[1] = st->key[PF_SK_WIRE]->addr[1]; - sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0]; - sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1]; - sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0]; - sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1]; - sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0]; - sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1]; - sp->proto = st->key[PF_SK_WIRE]->proto; - sp->af = st->key[PF_SK_WIRE]->af; - - /* copy from state */ - strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname)); - bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); - sp->creation = htonl(time_uptime - st->creation); - sp->expire = pf_state_expires(st); - if (sp->expire <= time_second) - sp->expire = htonl(0); - else - sp->expire = htonl(sp->expire - time_second); - - sp->direction = st->direction; - sp->log = st->log; - sp->timeout = st->timeout; - sp->state_flags = st->state_flags; - if (st->src_node) - sp->sync_flags |= PFSYNC_FLAG_SRCNODE; - if (st->nat_src_node) - sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE; - - bcopy(&st->id, &sp->id, sizeof(sp->id)); - sp->creatorid = st->creatorid; - pf_state_peer_hton(&st->src, &sp->src); - pf_state_peer_hton(&st->dst, &sp->dst); - - if (st->rule.ptr == NULL) - sp->rule = htonl(-1); - else - sp->rule = htonl(st->rule.ptr->nr); - if (st->anchor.ptr == NULL) - sp->anchor = htonl(-1); - else - sp->anchor = htonl(st->anchor.ptr->nr); - if (st->nat_rule.ptr == NULL) - sp->nat_rule = htonl(-1); - else - sp->nat_rule = htonl(st->nat_rule.ptr->nr); - - pf_state_counter_hton(st->packets[0], sp->packets[0]); - pf_state_counter_hton(st->packets[1], sp->packets[1]); - pf_state_counter_hton(st->bytes[0], sp->bytes[0]); - pf_state_counter_hton(st->bytes[1], sp->bytes[1]); - -} -#endif -int +static int pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) { + struct pfsync_softc *sc = V_pfsyncif; struct pf_state *st = NULL; struct pf_state_key *skw = NULL, *sks = NULL; struct pf_rule *r = NULL; struct pfi_kif *kif; - int pool_flags; int error; -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); + PF_RULES_RASSERT(); if (sp->creatorid == 0 && V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (sp->creatorid == 0 && pf_status.debug >= PF_DEBUG_MISC) { -#endif - printf("pfsync_state_import: invalid creator id:" - " %08x\n", ntohl(sp->creatorid)); + printf("%s: invalid creator id: %08x\n", __func__, + ntohl(sp->creatorid)); return (EINVAL); } - if ((kif = pfi_kif_get(sp->ifname)) == NULL) { -#ifdef __FreeBSD__ + if ((kif = pfi_kif_find(sp->ifname)) == NULL) { if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif - printf("pfsync_state_import: " - "unknown interface: %s\n", sp->ifname); + printf("%s: unknown interface: %s\n", __func__, + sp->ifname); if (flags & PFSYNC_SI_IOCTL) return (EINVAL); return (0); /* skip this state */ @@ -723,34 +431,18 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) r = pf_main_ruleset.rules[ PF_RULESET_FILTER].active.ptr_array[ntohl(sp->rule)]; else -#ifdef __FreeBSD__ r = &V_pf_default_rule; -#else - r = &pf_default_rule; -#endif if ((r->max_states && r->states_cur >= r->max_states)) goto cleanup; -#ifdef __FreeBSD__ - if (flags & PFSYNC_SI_IOCTL) - pool_flags = PR_WAITOK | PR_ZERO; - else - pool_flags = PR_NOWAIT | PR_ZERO; - - if ((st = pool_get(&V_pf_state_pl, pool_flags)) == NULL) - goto cleanup; -#else - if (flags & PFSYNC_SI_IOCTL) - pool_flags = PR_WAITOK | PR_LIMITFAIL | PR_ZERO; - else - pool_flags = PR_LIMITFAIL | PR_ZERO; - - if ((st = pool_get(&pf_state_pl, pool_flags)) == NULL) + /* + * XXXGL: consider M_WAITOK in ioctl path after. + */ + if ((st = uma_zalloc(V_pf_state_z, M_NOWAIT | M_ZERO)) == NULL) goto cleanup; -#endif - if ((skw = pf_alloc_state_key(pool_flags)) == NULL) + if ((skw = uma_zalloc(V_pf_state_key_z, M_NOWAIT)) == NULL) goto cleanup; if (PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0], @@ -759,7 +451,8 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) &sp->key[PF_SK_STACK].addr[1], sp->af) || sp->key[PF_SK_WIRE].port[0] != sp->key[PF_SK_STACK].port[0] || sp->key[PF_SK_WIRE].port[1] != sp->key[PF_SK_STACK].port[1]) { - if ((sks = pf_alloc_state_key(pool_flags)) == NULL) + sks = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (sks == NULL) goto cleanup; } else sks = skw; @@ -788,17 +481,13 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) /* copy to state */ bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr)); st->creation = time_uptime - ntohl(sp->creation); - st->expire = time_second; + st->expire = time_uptime; if (sp->expire) { uint32_t timeout; timeout = r->timeout[sp->timeout]; if (!timeout) -#ifdef __FreeBSD__ timeout = V_pf_default_rule.timeout[sp->timeout]; -#else - timeout = pf_default_rule.timeout[sp->timeout]; -#endif /* sp->expire may have been adaptively scaled by export. */ st->expire -= timeout - ntohl(sp->expire); @@ -809,7 +498,7 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) st->timeout = sp->timeout; st->state_flags = sp->state_flags; - bcopy(sp->id, &st->id, sizeof(st->id)); + st->id = sp->id; st->creatorid = sp->creatorid; pf_state_peer_ntoh(&sp->src, &st->src); pf_state_peer_ntoh(&sp->dst, &st->dst); @@ -826,8 +515,8 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) r->states_cur++; r->states_tot++; - if (!ISSET(flags, PFSYNC_SI_IOCTL)) - SET(st->state_flags, PFSTATE_NOSYNC); + if (!(flags & PFSYNC_SI_IOCTL)) + st->state_flags |= PFSTATE_NOSYNC; if ((error = pf_state_insert(kif, skw, sks, st)) != 0) { /* XXX when we have nat_rule/anchors, use STATE_DEC_COUNTERS */ @@ -835,14 +524,15 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) goto cleanup_state; } - if (!ISSET(flags, PFSYNC_SI_IOCTL)) { - CLR(st->state_flags, PFSTATE_NOSYNC); - if (ISSET(st->state_flags, PFSTATE_ACK)) { + if (!(flags & PFSYNC_SI_IOCTL)) { + st->state_flags &= ~PFSTATE_NOSYNC; + if (st->state_flags & PFSTATE_ACK) { pfsync_q_ins(st, PFSYNC_S_IACK); - schednetisr(NETISR_PFSYNC); + pfsync_push(sc); } } - CLR(st->state_flags, PFSTATE_ACK); + st->state_flags &= ~PFSTATE_ACK; + PF_STATE_UNLOCK(st); return (0); @@ -850,49 +540,26 @@ cleanup: error = ENOMEM; if (skw == sks) sks = NULL; -#ifdef __FreeBSD__ if (skw != NULL) - pool_put(&V_pf_state_key_pl, skw); + uma_zfree(V_pf_state_key_z, skw); if (sks != NULL) - pool_put(&V_pf_state_key_pl, sks); -#else - if (skw != NULL) - pool_put(&pf_state_key_pl, skw); - if (sks != NULL) - pool_put(&pf_state_key_pl, sks); -#endif + uma_zfree(V_pf_state_key_z, sks); -cleanup_state: /* pf_state_insert frees the state keys */ +cleanup_state: /* pf_state_insert() frees the state keys. */ if (st) { -#ifdef __FreeBSD__ - if (st->dst.scrub) - pool_put(&V_pf_state_scrub_pl, st->dst.scrub); - if (st->src.scrub) - pool_put(&V_pf_state_scrub_pl, st->src.scrub); - pool_put(&V_pf_state_pl, st); -#else if (st->dst.scrub) - pool_put(&pf_state_scrub_pl, st->dst.scrub); + uma_zfree(V_pf_state_scrub_z, st->dst.scrub); if (st->src.scrub) - pool_put(&pf_state_scrub_pl, st->src.scrub); - pool_put(&pf_state_pl, st); -#endif + uma_zfree(V_pf_state_scrub_z, st->src.scrub); + uma_zfree(V_pf_state_z, st); } return (error); } -void -#ifdef __FreeBSD__ +static void pfsync_input(struct mbuf *m, __unused int off) -#else -pfsync_input(struct mbuf *m, ...) -#endif { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif struct pfsync_pkt pkt; struct ip *ip = mtod(m, struct ip *); struct pfsync_header *ph; @@ -900,15 +567,13 @@ pfsync_input(struct mbuf *m, ...) int offset; int rv; + uint16_t count; V_pfsyncstats.pfsyncs_ipackets++; - /* verify that we have a sync interface configured */ -#ifdef __FreeBSD__ - if (!sc || !sc->sc_sync_if || !V_pf_status.running) -#else - if (!sc || !sc->sc_sync_if || !pf_status.running) -#endif + /* Verify that we have a sync interface configured. */ + if (!sc || !sc->sc_sync_if || !V_pf_status.running || + (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) goto done; /* verify that the packet came in on the right interface */ @@ -917,13 +582,8 @@ pfsync_input(struct mbuf *m, ...) goto done; } -#ifdef __FreeBSD__ sc->sc_ifp->if_ipackets++; sc->sc_ifp->if_ibytes += m->m_pkthdr.len; -#else - sc->sc_if.if_ipackets++; - sc->sc_if.if_ibytes += m->m_pkthdr.len; -#endif /* verify that the IP TTL is 255. */ if (ip->ip_ttl != PFSYNC_DFLTTL) { V_pfsyncstats.pfsyncs_badttl++; @@ -951,23 +611,17 @@ pfsync_input(struct mbuf *m, ...) goto done; } -#if 0 - if (pfsync_input_hmac(m, offset) != 0) { - /* XXX stats */ - goto done; - } -#endif - /* Cheaper to grab this now than having to mess with mbufs later */ pkt.ip = ip; pkt.src = ip->ip_src; pkt.flags = 0; -#ifdef __FreeBSD__ + /* + * Trusting pf_chksum during packet processing, as well as seeking + * in interface name tree, require holding PF_RULES_RLOCK(). + */ + PF_RULES_RLOCK(); if (!bcmp(&ph->pfcksum, &V_pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH)) -#else - if (!bcmp(&ph->pfcksum, &pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH)) -#endif pkt.flags |= PFSYNC_SI_CKSUM; offset += sizeof(*ph); @@ -977,34 +631,34 @@ pfsync_input(struct mbuf *m, ...) if (subh.action >= PFSYNC_ACT_MAX) { V_pfsyncstats.pfsyncs_badact++; + PF_RULES_RUNLOCK(); goto done; } - rv = (*pfsync_acts[subh.action])(&pkt, m, offset, - ntohs(subh.count)); - if (rv == -1) + count = ntohs(subh.count); + V_pfsyncstats.pfsyncs_iacts[subh.action] += count; + rv = (*pfsync_acts[subh.action])(&pkt, m, offset, count); + if (rv == -1) { + PF_RULES_RUNLOCK(); return; + } offset += rv; } + PF_RULES_RUNLOCK(); done: m_freem(m); } -int +static int pfsync_in_clr(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { struct pfsync_clr *clr; struct mbuf *mp; int len = sizeof(*clr) * count; int i, offp; - - struct pf_state *st, *nexts; - struct pf_state_key *sk, *nextsk; - struct pf_state_item *si; u_int32_t creatorid; - int s; mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { @@ -1013,64 +667,33 @@ pfsync_in_clr(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } clr = (struct pfsync_clr *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { creatorid = clr[i].creatorid; - if (clr[i].ifname[0] == '\0') { -#ifdef __FreeBSD__ - for (st = RB_MIN(pf_state_tree_id, &V_tree_id); - st; st = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &V_tree_id, st); -#else - for (st = RB_MIN(pf_state_tree_id, &tree_id); - st; st = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &tree_id, st); -#endif - if (st->creatorid == creatorid) { - SET(st->state_flags, PFSTATE_NOSYNC); - pf_unlink_state(st); - } - } - } else { - if (pfi_kif_get(clr[i].ifname) == NULL) - continue; + if (clr[i].ifname[0] != '\0' && + pfi_kif_find(clr[i].ifname) == NULL) + continue; - /* XXX correct? */ -#ifdef __FreeBSD__ - for (sk = RB_MIN(pf_state_tree, &V_pf_statetbl); -#else - for (sk = RB_MIN(pf_state_tree, &pf_statetbl); -#endif - sk; sk = nextsk) { - nextsk = RB_NEXT(pf_state_tree, -#ifdef __FreeBSD__ - &V_pf_statetbl, sk); -#else - &pf_statetbl, sk); -#endif - TAILQ_FOREACH(si, &sk->states, entry) { - if (si->s->creatorid == creatorid) { - SET(si->s->state_flags, - PFSTATE_NOSYNC); - pf_unlink_state(si->s); - } + for (int i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_state *s; +relock: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (s->creatorid == creatorid) { + s->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(s, PF_ENTER_LOCKED); + goto relock; } } + PF_HASHROW_UNLOCK(ih); } } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (len); } -int +static int pfsync_in_ins(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { struct mbuf *mp; @@ -1078,8 +701,6 @@ pfsync_in_ins(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) int len = sizeof(*sp) * count; int i, offp; - int s; - mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { V_pfsyncstats.pfsyncs_badlen++; @@ -1087,55 +708,38 @@ pfsync_in_ins(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } sa = (struct pfsync_state *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { sp = &sa[i]; - /* check for invalid values */ + /* Check for invalid values. */ if (sp->timeout >= PFTM_MAX || sp->src.state > PF_TCPS_PROXY_DST || sp->dst.state > PF_TCPS_PROXY_DST || sp->direction > PF_OUT || (sp->af != AF_INET && sp->af != AF_INET6)) { -#ifdef __FreeBSD__ - if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif - printf("pfsync_input: PFSYNC5_ACT_INS: " - "invalid value\n"); - } + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("%s: invalid value\n", __func__); V_pfsyncstats.pfsyncs_badval++; continue; } - if (pfsync_state_import(sp, pkt->flags) == ENOMEM) { - /* drop out, but process the rest of the actions */ + if (pfsync_state_import(sp, pkt->flags) == ENOMEM) + /* Drop out, but process the rest of the actions. */ break; - } } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (len); } -int +static int pfsync_in_iack(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { struct pfsync_ins_ack *ia, *iaa; - struct pf_state_cmp id_key; struct pf_state *st; struct mbuf *mp; int len = count * sizeof(*ia); int offp, i; - int s; mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { @@ -1144,27 +748,20 @@ pfsync_in_iack(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } iaa = (struct pfsync_ins_ack *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { ia = &iaa[i]; - bcopy(&ia->id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = ia->creatorid; - - st = pf_find_state_byid(&id_key); + st = pf_find_state_byid(ia->id, ia->creatorid); if (st == NULL) continue; - if (ISSET(st->state_flags, PFSTATE_ACK)) - pfsync_deferred(st, 0); + if (st->state_flags & PFSTATE_ACK) { + PFSYNC_LOCK(V_pfsyncif); + pfsync_undefer_state(st, 0); + PFSYNC_UNLOCK(V_pfsyncif); + } + PF_STATE_UNLOCK(st); } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); /* * XXX this is not yet implemented, but we know the size of the * message so we can skip it. @@ -1173,12 +770,14 @@ pfsync_in_iack(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) return (count * sizeof(struct pfsync_ins_ack)); } -int +static int pfsync_upd_tcp(struct pf_state *st, struct pfsync_state_peer *src, struct pfsync_state_peer *dst) { int sfail = 0; + PF_STATE_LOCK_ASSERT(st); + /* * The state should never go backwards except * for syn-proxy states. Neither should the @@ -1207,11 +806,11 @@ pfsync_upd_tcp(struct pf_state *st, struct pfsync_state_peer *src, return (sfail); } -int +static int pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { + struct pfsync_softc *sc = V_pfsyncif; struct pfsync_state *sa, *sp; - struct pf_state_cmp id_key; struct pf_state_key *sk; struct pf_state *st; int sfail; @@ -1219,7 +818,6 @@ pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) struct mbuf *mp; int len = count * sizeof(*sp); int offp, i; - int s; mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { @@ -1228,10 +826,6 @@ pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } sa = (struct pfsync_state *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { sp = &sa[i]; @@ -1239,11 +833,7 @@ pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) if (sp->timeout >= PFTM_MAX || sp->src.state > PF_TCPS_PROXY_DST || sp->dst.state > PF_TCPS_PROXY_DST) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pfsync_input: PFSYNC_ACT_UPD: " "invalid value\n"); } @@ -1251,10 +841,7 @@ pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) continue; } - bcopy(sp->id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = sp->creatorid; - - st = pf_find_state_byid(&id_key); + st = pf_find_state_byid(sp->id, sp->creatorid); if (st == NULL) { /* insert the update */ if (pfsync_state_import(sp, 0)) @@ -1262,8 +849,11 @@ pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) continue; } - if (ISSET(st->state_flags, PFSTATE_ACK)) - pfsync_deferred(st, 1); + if (st->state_flags & PFSTATE_ACK) { + PFSYNC_LOCK(sc); + pfsync_undefer_state(st, 1); + PFSYNC_UNLOCK(sc); + } sk = st->key[PF_SK_WIRE]; /* XXX right one? */ sfail = 0; @@ -1281,44 +871,40 @@ pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } if (sfail) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pfsync: %s stale update (%d)" " id: %016llx creatorid: %08x\n", (sfail < 7 ? "ignoring" : "partial"), - sfail, betoh64(st->id), + sfail, (unsigned long long)be64toh(st->id), ntohl(st->creatorid)); } V_pfsyncstats.pfsyncs_stale++; pfsync_update_state(st); - schednetisr(NETISR_PFSYNC); + PF_STATE_UNLOCK(st); + PFSYNC_LOCK(sc); + pfsync_push(sc); + PFSYNC_UNLOCK(sc); continue; } pfsync_alloc_scrub_memory(&sp->dst, &st->dst); pf_state_peer_ntoh(&sp->src, &st->src); pf_state_peer_ntoh(&sp->dst, &st->dst); - st->expire = time_second; + st->expire = time_uptime; st->timeout = sp->timeout; st->pfsync_time = time_uptime; + PF_STATE_UNLOCK(st); } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (len); } -int +static int pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { + struct pfsync_softc *sc = V_pfsyncif; struct pfsync_upd_c *ua, *up; struct pf_state_key *sk; - struct pf_state_cmp id_key; struct pf_state *st; int len = count * sizeof(*up); @@ -1326,7 +912,6 @@ pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) struct mbuf *mp; int offp, i; - int s; mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { @@ -1335,10 +920,6 @@ pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } ua = (struct pfsync_upd_c *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { up = &ua[i]; @@ -1346,11 +927,7 @@ pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) if (up->timeout >= PFTM_MAX || up->src.state > PF_TCPS_PROXY_DST || up->dst.state > PF_TCPS_PROXY_DST) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pfsync_input: " "PFSYNC_ACT_UPD_C: " "invalid value\n"); @@ -1359,18 +936,20 @@ pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) continue; } - bcopy(&up->id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = up->creatorid; - - st = pf_find_state_byid(&id_key); + st = pf_find_state_byid(up->id, up->creatorid); if (st == NULL) { /* We don't have this state. Ask for it. */ - pfsync_request_update(id_key.creatorid, id_key.id); + PFSYNC_LOCK(sc); + pfsync_request_update(up->creatorid, up->id); + PFSYNC_UNLOCK(sc); continue; } - if (ISSET(st->state_flags, PFSTATE_ACK)) - pfsync_deferred(st, 1); + if (st->state_flags & PFSTATE_ACK) { + PFSYNC_LOCK(sc); + pfsync_undefer_state(st, 1); + PFSYNC_UNLOCK(sc); + } sk = st->key[PF_SK_WIRE]; /* XXX right one? */ sfail = 0; @@ -1387,39 +966,35 @@ pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } if (sfail) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pfsync: ignoring stale update " "(%d) id: %016llx " "creatorid: %08x\n", sfail, - betoh64(st->id), + (unsigned long long)be64toh(st->id), ntohl(st->creatorid)); } V_pfsyncstats.pfsyncs_stale++; pfsync_update_state(st); - schednetisr(NETISR_PFSYNC); + PF_STATE_UNLOCK(st); + PFSYNC_LOCK(sc); + pfsync_push(sc); + PFSYNC_UNLOCK(sc); continue; } pfsync_alloc_scrub_memory(&up->dst, &st->dst); pf_state_peer_ntoh(&up->src, &st->src); pf_state_peer_ntoh(&up->dst, &st->dst); - st->expire = time_second; + st->expire = time_uptime; st->timeout = up->timeout; st->pfsync_time = time_uptime; + PF_STATE_UNLOCK(st); } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (len); } -int +static int pfsync_in_ureq(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { struct pfsync_upd_req *ur, *ura; @@ -1427,7 +1002,6 @@ pfsync_in_ureq(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) int len = count * sizeof(*ur); int i, offp; - struct pf_state_cmp id_key; struct pf_state *st; mp = m_pulldown(m, offset, len, &offp); @@ -1437,46 +1011,38 @@ pfsync_in_ureq(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } ura = (struct pfsync_upd_req *)(mp->m_data + offp); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { ur = &ura[i]; - bcopy(&ur->id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = ur->creatorid; - - if (id_key.id == 0 && id_key.creatorid == 0) + if (ur->id == 0 && ur->creatorid == 0) pfsync_bulk_start(); else { - st = pf_find_state_byid(&id_key); + st = pf_find_state_byid(ur->id, ur->creatorid); if (st == NULL) { V_pfsyncstats.pfsyncs_badstate++; continue; } - if (ISSET(st->state_flags, PFSTATE_NOSYNC)) + if (st->state_flags & PFSTATE_NOSYNC) { + PF_STATE_UNLOCK(st); continue; + } pfsync_update_state_req(st); + PF_STATE_UNLOCK(st); } } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif return (len); } -int +static int pfsync_in_del(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { struct mbuf *mp; struct pfsync_state *sa, *sp; - struct pf_state_cmp id_key; struct pf_state *st; int len = count * sizeof(*sp); int offp, i; - int s; mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { @@ -1485,42 +1051,29 @@ pfsync_in_del(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } sa = (struct pfsync_state *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { sp = &sa[i]; - bcopy(sp->id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = sp->creatorid; - - st = pf_find_state_byid(&id_key); + st = pf_find_state_byid(sp->id, sp->creatorid); if (st == NULL) { V_pfsyncstats.pfsyncs_badstate++; continue; } - SET(st->state_flags, PFSTATE_NOSYNC); - pf_unlink_state(st); + st->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(st, PF_ENTER_LOCKED); } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (len); } -int +static int pfsync_in_del_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { struct mbuf *mp; struct pfsync_del_c *sa, *sp; - struct pf_state_cmp id_key; struct pf_state *st; int len = count * sizeof(*sp); int offp, i; - int s; mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { @@ -1529,52 +1082,42 @@ pfsync_in_del_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } sa = (struct pfsync_del_c *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) { sp = &sa[i]; - bcopy(&sp->id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = sp->creatorid; - - st = pf_find_state_byid(&id_key); + st = pf_find_state_byid(sp->id, sp->creatorid); if (st == NULL) { V_pfsyncstats.pfsyncs_badstate++; continue; } - SET(st->state_flags, PFSTATE_NOSYNC); - pf_unlink_state(st); + st->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(st, PF_ENTER_LOCKED); } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); return (len); } -int +static int pfsync_in_bus(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif struct pfsync_bus *bus; struct mbuf *mp; int len = count * sizeof(*bus); int offp; + PFSYNC_BLOCK(sc); + /* If we're not waiting for a bulk update, who cares. */ - if (sc->sc_ureq_sent == 0) + if (sc->sc_ureq_sent == 0) { + PFSYNC_BUNLOCK(sc); return (len); + } mp = m_pulldown(m, offset, len, &offp); if (mp == NULL) { + PFSYNC_BUNLOCK(sc); V_pfsyncstats.pfsyncs_badlen++; return (-1); } @@ -1582,23 +1125,12 @@ pfsync_in_bus(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) switch (bus->status) { case PFSYNC_BUS_START: -#ifdef __FreeBSD__ callout_reset(&sc->sc_bulkfail_tmo, 4 * hz + - V_pf_pool_limits[PF_LIMIT_STATES].limit / + V_pf_limits[PF_LIMIT_STATES].limit / ((sc->sc_ifp->if_mtu - PFSYNC_MINPKT) / sizeof(struct pfsync_state)), - pfsync_bulk_fail, V_pfsyncif); -#else - timeout_add(&sc->sc_bulkfail_tmo, 4 * hz + - pf_pool_limits[PF_LIMIT_STATES].limit / - ((sc->sc_if.if_mtu - PFSYNC_MINPKT) / - sizeof(struct pfsync_state))); -#endif -#ifdef __FreeBSD__ + pfsync_bulk_fail, sc); if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif printf("pfsync: received bulk update start\n"); break; @@ -1608,42 +1140,27 @@ pfsync_in_bus(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) /* that's it, we're happy */ sc->sc_ureq_sent = 0; sc->sc_bulk_tries = 0; - timeout_del(&sc->sc_bulkfail_tmo); -#ifdef __FreeBSD__ - if (!sc->pfsync_sync_ok && carp_demote_adj_p) + callout_stop(&sc->sc_bulkfail_tmo); + if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) (*carp_demote_adj_p)(-V_pfsync_carp_adj, "pfsync bulk done"); - sc->pfsync_sync_ok = 1; -#else -#if NCARP > 0 - if (!pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, -1); -#endif - pfsync_sync_ok = 1; -#endif -#ifdef __FreeBSD__ + sc->sc_flags |= PFSYNCF_OK; if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif printf("pfsync: received valid " "bulk update end\n"); } else { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif printf("pfsync: received invalid " "bulk update end: bad timestamp\n"); } break; } + PFSYNC_BUNLOCK(sc); return (len); } -int +static int pfsync_in_tdb(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { int len = count * sizeof(struct pfsync_tdb); @@ -1662,16 +1179,8 @@ pfsync_in_tdb(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) } tp = (struct pfsync_tdb *)(mp->m_data + offp); - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif for (i = 0; i < count; i++) pfsync_update_net_tdb(&tp[i]); -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); #endif return (len); @@ -1679,7 +1188,7 @@ pfsync_in_tdb(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) #if defined(IPSEC) /* Update an in-kernel tdb. Silently fail if no tdb is found. */ -void +static void pfsync_update_net_tdb(struct pfsync_tdb *pt) { struct tdb *tdb; @@ -1688,34 +1197,27 @@ pfsync_update_net_tdb(struct pfsync_tdb *pt) /* check for invalid values */ if (ntohl(pt->spi) <= SPI_RESERVED_MAX || (pt->dst.sa.sa_family != AF_INET && - pt->dst.sa.sa_family != AF_INET6)) + pt->dst.sa.sa_family != AF_INET6)) goto bad; - s = spltdb(); tdb = gettdb(pt->spi, &pt->dst, pt->sproto); if (tdb) { pt->rpl = ntohl(pt->rpl); - pt->cur_bytes = betoh64(pt->cur_bytes); + pt->cur_bytes = (unsigned long long)be64toh(pt->cur_bytes); /* Neither replay nor byte counter should ever decrease. */ if (pt->rpl < tdb->tdb_rpl || pt->cur_bytes < tdb->tdb_cur_bytes) { - splx(s); goto bad; } tdb->tdb_rpl = pt->rpl; tdb->tdb_cur_bytes = pt->cur_bytes; } - splx(s); return; bad: -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif printf("pfsync_insert: PFSYNC_ACT_TDB_UPD: " "invalid value\n"); V_pfsyncstats.pfsyncs_badstate++; @@ -1724,7 +1226,7 @@ bad: #endif -int +static int pfsync_in_eof(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { /* check if we are at the right place in the packet */ @@ -1736,7 +1238,7 @@ pfsync_in_eof(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) return (-1); } -int +static int pfsync_in_error(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) { V_pfsyncstats.pfsyncs_badact++; @@ -1745,51 +1247,31 @@ pfsync_in_error(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) return (-1); } -int +static int pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, -#ifdef __FreeBSD__ struct route *rt) -#else - struct rtentry *rt) -#endif { m_freem(m); return (0); } /* ARGSUSED */ -int +static int pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { -#ifndef __FreeBSD__ - struct proc *p = curproc; -#endif struct pfsync_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; - struct ip_moptions *imo = &sc->sc_imo; struct pfsyncreq pfsyncr; - struct ifnet *sifp; - struct ip *ip; - int s, error; + int error; switch (cmd) { -#if 0 - case SIOCSIFADDR: - case SIOCAIFADDR: - case SIOCSIFDSTADDR: -#endif case SIOCSIFFLAGS: -#ifdef __FreeBSD__ + PFSYNC_LOCK(sc); if (ifp->if_flags & IFF_UP) ifp->if_drv_flags |= IFF_DRV_RUNNING; else ifp->if_drv_flags &= ~IFF_DRV_RUNNING; -#else - if (ifp->if_flags & IFF_UP) - ifp->if_flags |= IFF_RUNNING; - else - ifp->if_flags &= ~IFF_RUNNING; -#endif + PFSYNC_UNLOCK(sc); break; case SIOCSIFMTU: if (!sc->sc_sync_if || @@ -1797,201 +1279,128 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ifr->ifr_mtu > sc->sc_sync_if->if_mtu) return (EINVAL); if (ifr->ifr_mtu < ifp->if_mtu) { - s = splnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - pfsync_sendout(); -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); + PFSYNC_LOCK(sc); + if (sc->sc_len > PFSYNC_MINPKT) + pfsync_sendout(1); + PFSYNC_UNLOCK(sc); } ifp->if_mtu = ifr->ifr_mtu; break; case SIOCGETPFSYNC: bzero(&pfsyncr, sizeof(pfsyncr)); + PFSYNC_LOCK(sc); if (sc->sc_sync_if) { strlcpy(pfsyncr.pfsyncr_syncdev, sc->sc_sync_if->if_xname, IFNAMSIZ); } pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer; pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates; - pfsyncr.pfsyncr_defer = sc->sc_defer; + pfsyncr.pfsyncr_defer = (PFSYNCF_DEFER == + (sc->sc_flags & PFSYNCF_DEFER)); + PFSYNC_UNLOCK(sc); return (copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr))); case SIOCSETPFSYNC: -#ifdef __FreeBSD__ + { + struct ip_moptions *imo = &sc->sc_imo; + struct ifnet *sifp; + struct ip *ip; + void *mship = NULL; + if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0) -#else - if ((error = suser(p, p->p_acflag)) != 0) -#endif return (error); if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr)))) return (error); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif + if (pfsyncr.pfsyncr_maxupdates > 255) + return (EINVAL); + + if (pfsyncr.pfsyncr_syncdev[0] == 0) + sifp = NULL; + else if ((sifp = ifunit_ref(pfsyncr.pfsyncr_syncdev)) == NULL) + return (EINVAL); + + if (pfsyncr.pfsyncr_syncpeer.s_addr == 0 && sifp != NULL) + mship = malloc((sizeof(struct in_multi *) * + IP_MIN_MEMBERSHIPS), M_PFSYNC, M_WAITOK | M_ZERO); + + PFSYNC_LOCK(sc); if (pfsyncr.pfsyncr_syncpeer.s_addr == 0) -#ifdef __FreeBSD__ sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP); -#else - sc->sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP; -#endif else sc->sc_sync_peer.s_addr = pfsyncr.pfsyncr_syncpeer.s_addr; - if (pfsyncr.pfsyncr_maxupdates > 255) -#ifdef __FreeBSD__ - { - PF_UNLOCK(); -#endif - return (EINVAL); -#ifdef __FreeBSD__ - } -#endif sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates; - sc->sc_defer = pfsyncr.pfsyncr_defer; + if (pfsyncr.pfsyncr_defer) { + sc->sc_flags |= PFSYNCF_DEFER; + pfsync_defer_ptr = pfsync_defer; + } else { + sc->sc_flags &= ~PFSYNCF_DEFER; + pfsync_defer_ptr = NULL; + } - if (pfsyncr.pfsyncr_syncdev[0] == 0) { + if (sifp == NULL) { + if (sc->sc_sync_if) + if_rele(sc->sc_sync_if); sc->sc_sync_if = NULL; -#ifdef __FreeBSD__ - PF_UNLOCK(); if (imo->imo_membership) pfsync_multicast_cleanup(sc); -#else - if (imo->imo_num_memberships > 0) { - in_delmulti(imo->imo_membership[ - --imo->imo_num_memberships]); - imo->imo_multicast_ifp = NULL; - } -#endif + PFSYNC_UNLOCK(sc); break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - if ((sifp = ifunit(pfsyncr.pfsyncr_syncdev)) == NULL) - return (EINVAL); - -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - s = splnet(); -#ifdef __FreeBSD__ - if (sifp->if_mtu < sc->sc_ifp->if_mtu || -#else - if (sifp->if_mtu < sc->sc_if.if_mtu || -#endif + if (sc->sc_len > PFSYNC_MINPKT && + (sifp->if_mtu < sc->sc_ifp->if_mtu || (sc->sc_sync_if != NULL && sifp->if_mtu < sc->sc_sync_if->if_mtu) || - sifp->if_mtu < MCLBYTES - sizeof(struct ip)) - pfsync_sendout(); - sc->sc_sync_if = sifp; + sifp->if_mtu < MCLBYTES - sizeof(struct ip))) + pfsync_sendout(1); -#ifdef __FreeBSD__ - if (imo->imo_membership) { - PF_UNLOCK(); + if (imo->imo_membership) pfsync_multicast_cleanup(sc); - PF_LOCK(); - } -#else - if (imo->imo_num_memberships > 0) { - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); - imo->imo_multicast_ifp = NULL; - } -#endif -#ifdef __FreeBSD__ - if (sc->sc_sync_if && - sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) { - PF_UNLOCK(); - error = pfsync_multicast_setup(sc); - if (error) + if (sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) { + error = pfsync_multicast_setup(sc, sifp, mship); + if (error) { + if_rele(sifp); + free(mship, M_PFSYNC); return (error); - PF_LOCK(); - } -#else - if (sc->sc_sync_if && - sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) { - struct in_addr addr; - - if (!(sc->sc_sync_if->if_flags & IFF_MULTICAST)) { - sc->sc_sync_if = NULL; - splx(s); - return (EADDRNOTAVAIL); } - - addr.s_addr = INADDR_PFSYNC_GROUP; - - if ((imo->imo_membership[0] = - in_addmulti(&addr, sc->sc_sync_if)) == NULL) { - sc->sc_sync_if = NULL; - splx(s); - return (ENOBUFS); - } - imo->imo_num_memberships++; - imo->imo_multicast_ifp = sc->sc_sync_if; - imo->imo_multicast_ttl = PFSYNC_DFLTTL; - imo->imo_multicast_loop = 0; } -#endif /* !__FreeBSD__ */ + if (sc->sc_sync_if) + if_rele(sc->sc_sync_if); + sc->sc_sync_if = sifp; ip = &sc->sc_template; bzero(ip, sizeof(*ip)); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(sc->sc_template) >> 2; ip->ip_tos = IPTOS_LOWDELAY; - /* len and id are set later */ -#ifdef __FreeBSD__ + /* len and id are set later. */ ip->ip_off = IP_DF; -#else - ip->ip_off = htons(IP_DF); -#endif ip->ip_ttl = PFSYNC_DFLTTL; ip->ip_p = IPPROTO_PFSYNC; ip->ip_src.s_addr = INADDR_ANY; ip->ip_dst.s_addr = sc->sc_sync_peer.s_addr; - if (sc->sc_sync_if) { - /* Request a full state table update. */ - sc->sc_ureq_sent = time_uptime; -#ifdef __FreeBSD__ - if (sc->pfsync_sync_ok && carp_demote_adj_p) - (*carp_demote_adj_p)(V_pfsync_carp_adj, - "pfsync bulk start"); - sc->pfsync_sync_ok = 0; -#else -#if NCARP > 0 - if (pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, 1); -#endif - pfsync_sync_ok = 0; -#endif -#ifdef __FreeBSD__ - if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif - printf("pfsync: requesting bulk update\n"); -#ifdef __FreeBSD__ - callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, - pfsync_bulk_fail, V_pfsyncif); -#else - timeout_add_sec(&sc->sc_bulkfail_tmo, 5); -#endif - pfsync_request_update(0, 0); - } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); + /* Request a full state table update. */ + if ((sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) + (*carp_demote_adj_p)(V_pfsync_carp_adj, + "pfsync bulk start"); + sc->sc_flags &= ~PFSYNCF_OK; + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: requesting bulk update\n"); + pfsync_request_update(0, 0); + PFSYNC_UNLOCK(sc); + PFSYNC_BLOCK(sc); + sc->sc_ureq_sent = time_uptime; + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulk_fail, + sc); + PFSYNC_BUNLOCK(sc); break; - + } default: return (ENOTTY); } @@ -1999,7 +1408,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) return (0); } -int +static int pfsync_out_state(struct pf_state *st, struct mbuf *m, int offset) { struct pfsync_state *sp = (struct pfsync_state *)(m->m_data + offset); @@ -2009,7 +1418,7 @@ pfsync_out_state(struct pf_state *st, struct mbuf *m, int offset) return (sizeof(*sp)); } -int +static int pfsync_out_iack(struct pf_state *st, struct mbuf *m, int offset) { struct pfsync_ins_ack *iack = @@ -2021,7 +1430,7 @@ pfsync_out_iack(struct pf_state *st, struct mbuf *m, int offset) return (sizeof(*iack)); } -int +static int pfsync_out_upd_c(struct pf_state *st, struct mbuf *m, int offset) { struct pfsync_upd_c *up = (struct pfsync_upd_c *)(m->m_data + offset); @@ -2036,7 +1445,7 @@ pfsync_out_upd_c(struct pf_state *st, struct mbuf *m, int offset) return (sizeof(*up)); } -int +static int pfsync_out_del(struct pf_state *st, struct mbuf *m, int offset) { struct pfsync_del_c *dp = (struct pfsync_del_c *)(m->m_data + offset); @@ -2044,139 +1453,71 @@ pfsync_out_del(struct pf_state *st, struct mbuf *m, int offset) dp->id = st->id; dp->creatorid = st->creatorid; - SET(st->state_flags, PFSTATE_NOSYNC); + st->state_flags |= PFSTATE_NOSYNC; return (sizeof(*dp)); } -void +static void pfsync_drop(struct pfsync_softc *sc) { - struct pf_state *st; + struct pf_state *st, *next; struct pfsync_upd_req_item *ur; -#ifdef notyet - struct tdb *t; -#endif int q; for (q = 0; q < PFSYNC_S_COUNT; q++) { if (TAILQ_EMPTY(&sc->sc_qs[q])) continue; - TAILQ_FOREACH(st, &sc->sc_qs[q], sync_list) { -#ifdef PFSYNC_DEBUG -#ifdef __FreeBSD__ + TAILQ_FOREACH_SAFE(st, &sc->sc_qs[q], sync_list, next) { KASSERT(st->sync_state == q, ("%s: st->sync_state == q", - __FUNCTION__)); -#else - KASSERT(st->sync_state == q); -#endif -#endif + __func__)); st->sync_state = PFSYNC_S_NONE; + pf_release_state(st); } TAILQ_INIT(&sc->sc_qs[q]); } while ((ur = TAILQ_FIRST(&sc->sc_upd_req_list)) != NULL) { TAILQ_REMOVE(&sc->sc_upd_req_list, ur, ur_entry); - pool_put(&sc->sc_pool, ur); + free(ur, M_PFSYNC); } sc->sc_plus = NULL; - -#ifdef notyet - if (!TAILQ_EMPTY(&sc->sc_tdb_q)) { - TAILQ_FOREACH(t, &sc->sc_tdb_q, tdb_sync_entry) - CLR(t->tdb_flags, TDBF_PFSYNC); - - TAILQ_INIT(&sc->sc_tdb_q); - } -#endif - sc->sc_len = PFSYNC_MINPKT; } -#ifdef __FreeBSD__ -void pfsync_sendout() -{ - pfsync_sendout1(1); -} - static void -pfsync_sendout1(int schedswi) +pfsync_sendout(int schedswi) { struct pfsync_softc *sc = V_pfsyncif; -#else -void -pfsync_sendout(void) -{ - struct pfsync_softc *sc = pfsyncif; -#endif -#if NBPFILTER > 0 -#ifdef __FreeBSD__ struct ifnet *ifp = sc->sc_ifp; -#else - struct ifnet *ifp = &sc->sc_if; -#endif -#endif struct mbuf *m; struct ip *ip; struct pfsync_header *ph; struct pfsync_subheader *subh; - struct pf_state *st; + struct pf_state *st, *next; struct pfsync_upd_req_item *ur; -#ifdef notyet - struct tdb *t; -#endif int offset; int q, count = 0; -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_NET); -#endif - - if (sc == NULL || sc->sc_len == PFSYNC_MINPKT) - return; + KASSERT(sc != NULL, ("%s: null sc", __func__)); + KASSERT(sc->sc_len > PFSYNC_MINPKT, + ("%s: sc_len %zu", __func__, sc->sc_len)); + PFSYNC_LOCK_ASSERT(sc); -#if NBPFILTER > 0 if (ifp->if_bpf == NULL && sc->sc_sync_if == NULL) { -#else - if (sc->sc_sync_if == NULL) { -#endif pfsync_drop(sc); return; } -#ifdef __FreeBSD__ m = m_get2(M_NOWAIT, MT_DATA, M_PKTHDR, max_linkhdr + sc->sc_len); if (m == NULL) { sc->sc_ifp->if_oerrors++; V_pfsyncstats.pfsyncs_onomem++; return; } -#else - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - sc->sc_if.if_oerrors++; - pfsyncstats.pfsyncs_onomem++; - pfsync_drop(sc); - return; - } - - if (max_linkhdr + sc->sc_len > MHLEN) { - MCLGETI(m, M_DONTWAIT, NULL, max_linkhdr + sc->sc_len); - if (!ISSET(m->m_flags, M_EXT)) { - m_free(m); - sc->sc_if.if_oerrors++; - pfsyncstats.pfsyncs_onomem++; - pfsync_drop(sc); - return; - } - } -#endif m->m_data += max_linkhdr; m->m_len = m->m_pkthdr.len = sc->sc_len; @@ -2185,11 +1526,7 @@ pfsync_sendout(void) bcopy(&sc->sc_template, ip, sizeof(*ip)); offset = sizeof(*ip); -#ifdef __FreeBSD__ ip->ip_len = m->m_pkthdr.len; -#else - ip->ip_len = htons(m->m_pkthdr.len); -#endif ip->ip_id = htons(ip_randomid()); /* build the pfsync header */ @@ -2199,11 +1536,7 @@ pfsync_sendout(void) ph->version = PFSYNC_VERSION; ph->len = htons(sc->sc_len - sizeof(*ip)); -#ifdef __FreeBSD__ bcopy(V_pf_status.pf_chksum, ph->pfcksum, PF_MD5_DIGEST_LENGTH); -#else - bcopy(pf_status.pf_chksum, ph->pfcksum, PF_MD5_DIGEST_LENGTH); -#endif /* walk the queues */ for (q = 0; q < PFSYNC_S_COUNT; q++) { @@ -2214,19 +1547,17 @@ pfsync_sendout(void) offset += sizeof(*subh); count = 0; - TAILQ_FOREACH(st, &sc->sc_qs[q], sync_list) { -#ifdef PFSYNC_DEBUG -#ifdef __FreeBSD__ + TAILQ_FOREACH_SAFE(st, &sc->sc_qs[q], sync_list, next) { KASSERT(st->sync_state == q, ("%s: st->sync_state == q", - __FUNCTION__)); -#else - KASSERT(st->sync_state == q); -#endif -#endif - + __func__)); + /* + * XXXGL: some of write methods do unlocked reads + * of state data :( + */ offset += pfsync_qs[q].write(st, m, offset); st->sync_state = PFSYNC_S_NONE; + pf_release_state(st); count++; } TAILQ_INIT(&sc->sc_qs[q]); @@ -2234,6 +1565,7 @@ pfsync_sendout(void) bzero(subh, sizeof(*subh)); subh->action = pfsync_qs[q].action; subh->count = htons(count); + V_pfsyncstats.pfsyncs_oacts[pfsync_qs[q].action] += count; } if (!TAILQ_EMPTY(&sc->sc_upd_req_list)) { @@ -2247,15 +1579,14 @@ pfsync_sendout(void) bcopy(&ur->ur_msg, m->m_data + offset, sizeof(ur->ur_msg)); offset += sizeof(ur->ur_msg); - - pool_put(&sc->sc_pool, ur); - + free(ur, M_PFSYNC); count++; } bzero(subh, sizeof(*subh)); subh->action = PFSYNC_ACT_UPD_REQ; subh->count = htons(count); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_UPD_REQ] += count; } /* has someone built a custom region for us to add? */ @@ -2266,45 +1597,21 @@ pfsync_sendout(void) sc->sc_plus = NULL; } -#ifdef notyet - if (!TAILQ_EMPTY(&sc->sc_tdb_q)) { - subh = (struct pfsync_subheader *)(m->m_data + offset); - offset += sizeof(*subh); - - count = 0; - TAILQ_FOREACH(t, &sc->sc_tdb_q, tdb_sync_entry) { - offset += pfsync_out_tdb(t, m, offset); - CLR(t->tdb_flags, TDBF_PFSYNC); - - count++; - } - TAILQ_INIT(&sc->sc_tdb_q); - - bzero(subh, sizeof(*subh)); - subh->action = PFSYNC_ACT_TDB; - subh->count = htons(count); - } -#endif - subh = (struct pfsync_subheader *)(m->m_data + offset); offset += sizeof(*subh); bzero(subh, sizeof(*subh)); subh->action = PFSYNC_ACT_EOF; subh->count = htons(1); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_EOF]++; /* XXX write checksum in EOF here */ /* we're done, let's put it on the wire */ -#if NBPFILTER > 0 if (ifp->if_bpf) { m->m_data += sizeof(*ip); m->m_len = m->m_pkthdr.len = sc->sc_len - sizeof(*ip); -#ifdef __FreeBSD__ BPF_MTAP(ifp, m); -#else - bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); -#endif m->m_data -= sizeof(*ip); m->m_len = m->m_pkthdr.len = sc->sc_len; } @@ -2314,9 +1621,7 @@ pfsync_sendout(void) m_freem(m); return; } -#endif -#ifdef __FreeBSD__ sc->sc_ifp->if_opackets++; sc->sc_ifp->if_obytes += m->m_pkthdr.len; sc->sc_len = PFSYNC_MINPKT; @@ -2325,241 +1630,170 @@ pfsync_sendout(void) _IF_ENQUEUE(&sc->sc_ifp->if_snd, m); else { m_freem(m); - sc->sc_ifp->if_snd.ifq_drops++; + sc->sc_ifp->if_snd.ifq_drops++; } if (schedswi) swi_sched(V_pfsync_swi_cookie, 0); -#else - sc->sc_if.if_opackets++; - sc->sc_if.if_obytes += m->m_pkthdr.len; - - if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL) == 0) - pfsyncstats.pfsyncs_opackets++; - else - pfsyncstats.pfsyncs_oerrors++; - - /* start again */ - sc->sc_len = PFSYNC_MINPKT; -#endif } -void +static void pfsync_insert_state(struct pf_state *st) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_SOFTNET); -#endif + if (st->state_flags & PFSTATE_NOSYNC) + return; - if (ISSET(st->rule.ptr->rule_flag, PFRULE_NOSYNC) || + if ((st->rule.ptr->rule_flag & PFRULE_NOSYNC) || st->key[PF_SK_WIRE]->proto == IPPROTO_PFSYNC) { - SET(st->state_flags, PFSTATE_NOSYNC); + st->state_flags |= PFSTATE_NOSYNC; return; } - if (sc == NULL || ISSET(st->state_flags, PFSTATE_NOSYNC)) - return; - -#ifdef PFSYNC_DEBUG -#ifdef __FreeBSD__ KASSERT(st->sync_state == PFSYNC_S_NONE, - ("%s: st->sync_state == PFSYNC_S_NONE", __FUNCTION__)); -#else - KASSERT(st->sync_state == PFSYNC_S_NONE); -#endif -#endif + ("%s: st->sync_state == PFSYNC_S_NONE", __func__)); + PFSYNC_LOCK(sc); if (sc->sc_len == PFSYNC_MINPKT) -#ifdef __FreeBSD__ - callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, - V_pfsyncif); -#else - timeout_add_sec(&sc->sc_tmo, 1); -#endif + callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); pfsync_q_ins(st, PFSYNC_S_INS); + PFSYNC_UNLOCK(sc); st->sync_updates = 0; } -int defer = 10; - -int +static int pfsync_defer(struct pf_state *st, struct mbuf *m) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif struct pfsync_deferral *pd; -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_SOFTNET); -#endif + if (m->m_flags & (M_BCAST|M_MCAST)) + return (0); - if (!sc->sc_defer || m->m_flags & (M_BCAST|M_MCAST)) + PFSYNC_LOCK(sc); + + if (sc == NULL || !(sc->sc_ifp->if_flags & IFF_DRV_RUNNING) || + !(sc->sc_flags & PFSYNCF_DEFER)) { + PFSYNC_UNLOCK(sc); return (0); + } - if (sc->sc_deferred >= 128) + if (sc->sc_deferred >= 128) pfsync_undefer(TAILQ_FIRST(&sc->sc_deferrals), 0); - pd = pool_get(&sc->sc_pool, M_NOWAIT); + pd = malloc(sizeof(*pd), M_PFSYNC, M_NOWAIT); if (pd == NULL) return (0); sc->sc_deferred++; -#ifdef __FreeBSD__ m->m_flags |= M_SKIP_FIREWALL; -#else - m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; -#endif - SET(st->state_flags, PFSTATE_ACK); + st->state_flags |= PFSTATE_ACK; + pd->pd_sc = sc; + pd->pd_refs = 0; pd->pd_st = st; + pf_ref_state(st); pd->pd_m = m; TAILQ_INSERT_TAIL(&sc->sc_deferrals, pd, pd_entry); -#ifdef __FreeBSD__ - callout_init(&pd->pd_tmo, CALLOUT_MPSAFE); - callout_reset(&pd->pd_tmo, defer, pfsync_defer_tmo, - pd); -#else - timeout_set(&pd->pd_tmo, pfsync_defer_tmo, pd); - timeout_add(&pd->pd_tmo, defer); -#endif + callout_init_mtx(&pd->pd_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + callout_reset(&pd->pd_tmo, 10, pfsync_defer_tmo, pd); - swi_sched(V_pfsync_swi_cookie, 0); + pfsync_push(sc); return (1); } -void +static void pfsync_undefer(struct pfsync_deferral *pd, int drop) { -#ifdef __FreeBSD__ - struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - int s; + struct pfsync_softc *sc = pd->pd_sc; + struct mbuf *m = pd->pd_m; + struct pf_state *st = pd->pd_st; -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_SOFTNET); -#endif + PFSYNC_LOCK_ASSERT(sc); TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); sc->sc_deferred--; + pd->pd_st->state_flags &= ~PFSTATE_ACK; /* XXX: locking! */ + free(pd, M_PFSYNC); + pf_release_state(st); - CLR(pd->pd_st->state_flags, PFSTATE_ACK); - timeout_del(&pd->pd_tmo); /* bah */ if (drop) - m_freem(pd->pd_m); + m_freem(m); else { - s = splnet(); -#ifdef __FreeBSD__ - /* XXX: use pf_defered?! */ - PF_UNLOCK(); -#endif - ip_output(pd->pd_m, (void *)NULL, (void *)NULL, 0, - (void *)NULL, (void *)NULL); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - splx(s); + _IF_ENQUEUE(&sc->sc_ifp->if_snd, m); + pfsync_push(sc); } - - pool_put(&sc->sc_pool, pd); } -void +static void pfsync_defer_tmo(void *arg) { -#if defined(__FreeBSD__) && defined(VIMAGE) struct pfsync_deferral *pd = arg; -#endif - int s; + struct pfsync_softc *sc = pd->pd_sc; + struct mbuf *m = pd->pd_m; + struct pf_state *st = pd->pd_st; + + PFSYNC_LOCK_ASSERT(sc); + + CURVNET_SET(m->m_pkthdr.rcvif->if_vnet); + + TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); + sc->sc_deferred--; + pd->pd_st->state_flags &= ~PFSTATE_ACK; /* XXX: locking! */ + if (pd->pd_refs == 0) + free(pd, M_PFSYNC); + PFSYNC_UNLOCK(sc); + + ip_output(m, NULL, NULL, 0, NULL, NULL); + + pf_release_state(st); - s = splsoftnet(); -#ifdef __FreeBSD__ - CURVNET_SET(pd->pd_m->m_pkthdr.rcvif->if_vnet); /* XXX */ - PF_LOCK(); -#endif - pfsync_undefer(arg, 0); -#ifdef __FreeBSD__ - PF_UNLOCK(); CURVNET_RESTORE(); -#endif - splx(s); } -void -pfsync_deferred(struct pf_state *st, int drop) +static void +pfsync_undefer_state(struct pf_state *st, int drop) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif struct pfsync_deferral *pd; + PFSYNC_LOCK_ASSERT(sc); + TAILQ_FOREACH(pd, &sc->sc_deferrals, pd_entry) { if (pd->pd_st == st) { - pfsync_undefer(pd, drop); + if (callout_stop(&pd->pd_tmo)) + pfsync_undefer(pd, drop); return; } } - panic("pfsync_send_deferred: unable to find deferred state"); + panic("%s: unable to find deferred state", __func__); } -u_int pfsync_upds = 0; - -void +static void pfsync_update_state(struct pf_state *st) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif int sync = 0; -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_SOFTNET); -#endif + PF_STATE_LOCK_ASSERT(st); + PFSYNC_LOCK(sc); - if (sc == NULL) - return; - - if (ISSET(st->state_flags, PFSTATE_ACK)) - pfsync_deferred(st, 0); - if (ISSET(st->state_flags, PFSTATE_NOSYNC)) { + if (st->state_flags & PFSTATE_ACK) + pfsync_undefer_state(st, 0); + if (st->state_flags & PFSTATE_NOSYNC) { if (st->sync_state != PFSYNC_S_NONE) pfsync_q_del(st); + PFSYNC_UNLOCK(sc); return; } if (sc->sc_len == PFSYNC_MINPKT) -#ifdef __FreeBSD__ - callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, - V_pfsyncif); -#else - timeout_add_sec(&sc->sc_tmo, 1); -#endif + callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); switch (st->sync_state) { case PFSYNC_S_UPD_C: @@ -2582,40 +1816,31 @@ pfsync_update_state(struct pf_state *st) break; default: - panic("pfsync_update_state: unexpected sync state %d", - st->sync_state); + panic("%s: unexpected sync state %d", __func__, st->sync_state); } - if (sync || (time_uptime - st->pfsync_time) < 2) { - pfsync_upds++; - schednetisr(NETISR_PFSYNC); - } + if (sync || (time_uptime - st->pfsync_time) < 2) + pfsync_push(sc); + + PFSYNC_UNLOCK(sc); } -void +static void pfsync_request_update(u_int32_t creatorid, u_int64_t id) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif struct pfsync_upd_req_item *item; size_t nlen = sizeof(struct pfsync_upd_req); - int s; - PF_LOCK_ASSERT(); + PFSYNC_LOCK_ASSERT(sc); /* - * this code does nothing to prevent multiple update requests for the + * This code does nothing to prevent multiple update requests for the * same state being generated. */ - - item = pool_get(&sc->sc_pool, PR_NOWAIT); - if (item == NULL) { - /* XXX stats */ - return; - } + item = malloc(sizeof(*item), M_PFSYNC, M_NOWAIT); + if (item == NULL) + return; /* XXX stats */ item->ur_msg.id = id; item->ur_msg.creatorid = creatorid; @@ -2623,14 +1848,8 @@ pfsync_request_update(u_int32_t creatorid, u_int64_t id) if (TAILQ_EMPTY(&sc->sc_upd_req_list)) nlen += sizeof(struct pfsync_subheader); -#ifdef __FreeBSD__ if (sc->sc_len + nlen > sc->sc_ifp->if_mtu) { -#else - if (sc->sc_len + nlen > sc->sc_if.if_mtu) { -#endif - s = splnet(); - pfsync_sendout(); - splx(s); + pfsync_sendout(1); nlen = sizeof(struct pfsync_subheader) + sizeof(struct pfsync_upd_req); @@ -2639,26 +1858,21 @@ pfsync_request_update(u_int32_t creatorid, u_int64_t id) TAILQ_INSERT_TAIL(&sc->sc_upd_req_list, item, ur_entry); sc->sc_len += nlen; - schednetisr(NETISR_PFSYNC); + pfsync_push(sc); } -void +static void pfsync_update_state_req(struct pf_state *st) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - - PF_LOCK_ASSERT(); - if (sc == NULL) - panic("pfsync_update_state_req: nonexistant instance"); + PF_STATE_LOCK_ASSERT(st); + PFSYNC_LOCK(sc); - if (ISSET(st->state_flags, PFSTATE_NOSYNC)) { + if (st->state_flags & PFSTATE_NOSYNC) { if (st->sync_state != PFSYNC_S_NONE) pfsync_q_del(st); + PFSYNC_UNLOCK(sc); return; } @@ -2668,60 +1882,45 @@ pfsync_update_state_req(struct pf_state *st) pfsync_q_del(st); case PFSYNC_S_NONE: pfsync_q_ins(st, PFSYNC_S_UPD); - schednetisr(NETISR_PFSYNC); - return; + pfsync_push(sc); + break; case PFSYNC_S_INS: case PFSYNC_S_UPD: case PFSYNC_S_DEL: /* we're already handling it */ - return; + break; default: - panic("pfsync_update_state_req: unexpected sync state %d", - st->sync_state); + panic("%s: unexpected sync state %d", __func__, st->sync_state); } + + PFSYNC_UNLOCK(sc); } -void +static void pfsync_delete_state(struct pf_state *st) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_SOFTNET); -#endif - if (sc == NULL) - return; - - if (ISSET(st->state_flags, PFSTATE_ACK)) - pfsync_deferred(st, 1); - if (ISSET(st->state_flags, PFSTATE_NOSYNC)) { + PFSYNC_LOCK(sc); + if (st->state_flags & PFSTATE_ACK) + pfsync_undefer_state(st, 1); + if (st->state_flags & PFSTATE_NOSYNC) { if (st->sync_state != PFSYNC_S_NONE) pfsync_q_del(st); + PFSYNC_UNLOCK(sc); return; } if (sc->sc_len == PFSYNC_MINPKT) -#ifdef __FreeBSD__ - callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, - V_pfsyncif); -#else - timeout_add_sec(&sc->sc_tmo, 1); -#endif + callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); switch (st->sync_state) { case PFSYNC_S_INS: - /* we never got to tell the world so just forget about it */ + /* We never got to tell the world so just forget about it. */ pfsync_q_del(st); - return; + break; case PFSYNC_S_UPD_C: case PFSYNC_S_UPD: @@ -2731,87 +1930,55 @@ pfsync_delete_state(struct pf_state *st) case PFSYNC_S_NONE: pfsync_q_ins(st, PFSYNC_S_DEL); - return; + break; default: - panic("pfsync_delete_state: unexpected sync state %d", - st->sync_state); + panic("%s: unexpected sync state %d", __func__, st->sync_state); } + PFSYNC_UNLOCK(sc); } -void +static void pfsync_clear_states(u_int32_t creatorid, const char *ifname) { + struct pfsync_softc *sc = V_pfsyncif; struct { struct pfsync_subheader subh; struct pfsync_clr clr; } __packed r; -#ifdef __FreeBSD__ - struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#else - splassert(IPL_SOFTNET); -#endif - - if (sc == NULL) - return; - bzero(&r, sizeof(r)); r.subh.action = PFSYNC_ACT_CLR; r.subh.count = htons(1); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_CLR]++; strlcpy(r.clr.ifname, ifname, sizeof(r.clr.ifname)); r.clr.creatorid = creatorid; + PFSYNC_LOCK(sc); pfsync_send_plus(&r, sizeof(r)); + PFSYNC_UNLOCK(sc); } -void +static void pfsync_q_ins(struct pf_state *st, int q) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif size_t nlen = pfsync_qs[q].len; - int s; - PF_LOCK_ASSERT(); + PFSYNC_LOCK_ASSERT(sc); -#ifdef __FreeBSD__ KASSERT(st->sync_state == PFSYNC_S_NONE, - ("%s: st->sync_state == PFSYNC_S_NONE", __FUNCTION__)); -#else - KASSERT(st->sync_state == PFSYNC_S_NONE); -#endif + ("%s: st->sync_state == PFSYNC_S_NONE", __func__)); + KASSERT(sc->sc_len >= PFSYNC_MINPKT, ("pfsync pkt len is too low %zu", + sc->sc_len)); -#if 1 || defined(PFSYNC_DEBUG) - if (sc->sc_len < PFSYNC_MINPKT) -#ifdef __FreeBSD__ - panic("pfsync pkt len is too low %zu", sc->sc_len); -#else - panic("pfsync pkt len is too low %d", sc->sc_len); -#endif -#endif if (TAILQ_EMPTY(&sc->sc_qs[q])) nlen += sizeof(struct pfsync_subheader); -#ifdef __FreeBSD__ if (sc->sc_len + nlen > sc->sc_ifp->if_mtu) { -#else - if (sc->sc_len + nlen > sc->sc_if.if_mtu) { -#endif - s = splnet(); - pfsync_sendout(); - splx(s); + pfsync_sendout(1); nlen = sizeof(struct pfsync_subheader) + pfsync_qs[q].len; } @@ -2819,234 +1986,112 @@ pfsync_q_ins(struct pf_state *st, int q) sc->sc_len += nlen; TAILQ_INSERT_TAIL(&sc->sc_qs[q], st, sync_list); st->sync_state = q; + pf_ref_state(st); } -void +static void pfsync_q_del(struct pf_state *st) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif int q = st->sync_state; -#ifdef __FreeBSD__ + PFSYNC_LOCK_ASSERT(sc); KASSERT(st->sync_state != PFSYNC_S_NONE, - ("%s: st->sync_state != PFSYNC_S_NONE", __FUNCTION__)); -#else - KASSERT(st->sync_state != PFSYNC_S_NONE); -#endif + ("%s: st->sync_state != PFSYNC_S_NONE", __func__)); sc->sc_len -= pfsync_qs[q].len; TAILQ_REMOVE(&sc->sc_qs[q], st, sync_list); st->sync_state = PFSYNC_S_NONE; + pf_release_state(st); if (TAILQ_EMPTY(&sc->sc_qs[q])) sc->sc_len -= sizeof(struct pfsync_subheader); } -#ifdef notyet -void -pfsync_update_tdb(struct tdb *t, int output) -{ -#ifdef __FreeBSD__ - struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - size_t nlen = sizeof(struct pfsync_tdb); - int s; - - if (sc == NULL) - return; - - if (!ISSET(t->tdb_flags, TDBF_PFSYNC)) { - if (TAILQ_EMPTY(&sc->sc_tdb_q)) - nlen += sizeof(struct pfsync_subheader); - - if (sc->sc_len + nlen > sc->sc_if.if_mtu) { - s = splnet(); - PF_LOCK(); - pfsync_sendout(); - PF_UNLOCK(); - splx(s); - - nlen = sizeof(struct pfsync_subheader) + - sizeof(struct pfsync_tdb); - } - - sc->sc_len += nlen; - TAILQ_INSERT_TAIL(&sc->sc_tdb_q, t, tdb_sync_entry); - SET(t->tdb_flags, TDBF_PFSYNC); - t->tdb_updates = 0; - } else { - if (++t->tdb_updates >= sc->sc_maxupdates) - schednetisr(NETISR_PFSYNC); - } - - if (output) - SET(t->tdb_flags, TDBF_PFSYNC_RPL); - else - CLR(t->tdb_flags, TDBF_PFSYNC_RPL); -} - -void -pfsync_delete_tdb(struct tdb *t) -{ -#ifdef __FreeBSD__ - struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - - if (sc == NULL || !ISSET(t->tdb_flags, TDBF_PFSYNC)) - return; - - sc->sc_len -= sizeof(struct pfsync_tdb); - TAILQ_REMOVE(&sc->sc_tdb_q, t, tdb_sync_entry); - CLR(t->tdb_flags, TDBF_PFSYNC); - - if (TAILQ_EMPTY(&sc->sc_tdb_q)) - sc->sc_len -= sizeof(struct pfsync_subheader); -} - -int -pfsync_out_tdb(struct tdb *t, struct mbuf *m, int offset) -{ - struct pfsync_tdb *ut = (struct pfsync_tdb *)(m->m_data + offset); - - bzero(ut, sizeof(*ut)); - ut->spi = t->tdb_spi; - bcopy(&t->tdb_dst, &ut->dst, sizeof(ut->dst)); - /* - * When a failover happens, the master's rpl is probably above - * what we see here (we may be up to a second late), so - * increase it a bit for outbound tdbs to manage most such - * situations. - * - * For now, just add an offset that is likely to be larger - * than the number of packets we can see in one second. The RFC - * just says the next packet must have a higher seq value. - * - * XXX What is a good algorithm for this? We could use - * a rate-determined increase, but to know it, we would have - * to extend struct tdb. - * XXX pt->rpl can wrap over MAXINT, but if so the real tdb - * will soon be replaced anyway. For now, just don't handle - * this edge case. - */ -#define RPL_INCR 16384 - ut->rpl = htonl(t->tdb_rpl + (ISSET(t->tdb_flags, TDBF_PFSYNC_RPL) ? - RPL_INCR : 0)); - ut->cur_bytes = htobe64(t->tdb_cur_bytes); - ut->sproto = t->tdb_sproto; - - return (sizeof(*ut)); -} -#endif - -void +static void pfsync_bulk_start(void) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif printf("pfsync: received bulk update request\n"); -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); - if (TAILQ_EMPTY(&V_state_list)) -#else - if (TAILQ_EMPTY(&state_list)) -#endif - pfsync_bulk_status(PFSYNC_BUS_END); - else { - sc->sc_ureq_received = time_uptime; - if (sc->sc_bulk_next == NULL) -#ifdef __FreeBSD__ - sc->sc_bulk_next = TAILQ_FIRST(&V_state_list); -#else - sc->sc_bulk_next = TAILQ_FIRST(&state_list); -#endif - sc->sc_bulk_last = sc->sc_bulk_next; + PFSYNC_BLOCK(sc); - pfsync_bulk_status(PFSYNC_BUS_START); - callout_reset(&sc->sc_bulk_tmo, 1, pfsync_bulk_update, sc); - } + sc->sc_ureq_received = time_uptime; + sc->sc_bulk_hashid = 0; + sc->sc_bulk_stateid = 0; + pfsync_bulk_status(PFSYNC_BUS_START); + callout_reset(&sc->sc_bulk_tmo, 1, pfsync_bulk_update, sc); + PFSYNC_BUNLOCK(sc); } -void +static void pfsync_bulk_update(void *arg) { struct pfsync_softc *sc = arg; - struct pf_state *st = sc->sc_bulk_next; - int i = 0; - int s; + struct pf_state *s; + int i, sent = 0; - PF_LOCK_ASSERT(); - - s = splsoftnet(); -#ifdef __FreeBSD__ + PFSYNC_BLOCK_ASSERT(sc); CURVNET_SET(sc->sc_ifp->if_vnet); -#endif - for (;;) { - if (st->sync_state == PFSYNC_S_NONE && - st->timeout < PFTM_MAX && - st->pfsync_time <= sc->sc_ureq_received) { - pfsync_update_state_req(st); - i++; - } - st = TAILQ_NEXT(st, entry_list); - if (st == NULL) -#ifdef __FreeBSD__ - st = TAILQ_FIRST(&V_state_list); -#else - st = TAILQ_FIRST(&state_list); -#endif + /* + * Start with last state from previous invocation. + * It may had gone, in this case start from the + * hash slot. + */ + s = pf_find_state_byid(sc->sc_bulk_stateid, sc->sc_bulk_creatorid); - if (st == sc->sc_bulk_last) { - /* we're done */ - sc->sc_bulk_next = NULL; - sc->sc_bulk_last = NULL; - pfsync_bulk_status(PFSYNC_BUS_END); - break; + if (s != NULL) + i = PF_IDHASH(s); + else + i = sc->sc_bulk_hashid; + + for (; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + + if (s != NULL) + PF_HASHROW_ASSERT(ih); + else { + PF_HASHROW_LOCK(ih); + s = LIST_FIRST(&ih->states); } -#ifdef __FreeBSD__ - if (i > 1 && (sc->sc_ifp->if_mtu - sc->sc_len) < -#else - if (i > 1 && (sc->sc_if.if_mtu - sc->sc_len) < -#endif - sizeof(struct pfsync_state)) { - /* we've filled a packet */ - sc->sc_bulk_next = st; -#ifdef __FreeBSD__ - callout_reset(&sc->sc_bulk_tmo, 1, - pfsync_bulk_update, sc); -#else - timeout_add(&sc->sc_bulk_tmo, 1); -#endif - break; + for (; s; s = LIST_NEXT(s, entry)) { + + if (sent > 1 && (sc->sc_ifp->if_mtu - sc->sc_len) < + sizeof(struct pfsync_state)) { + /* We've filled a packet. */ + sc->sc_bulk_hashid = i; + sc->sc_bulk_stateid = s->id; + sc->sc_bulk_creatorid = s->creatorid; + PF_HASHROW_UNLOCK(ih); + callout_reset(&sc->sc_bulk_tmo, 1, + pfsync_bulk_update, sc); + goto full; + } + + if (s->sync_state == PFSYNC_S_NONE && + s->timeout < PFTM_MAX && + s->pfsync_time <= sc->sc_ureq_received) { + PFSYNC_LOCK(sc); + pfsync_update_state_req(s); + PFSYNC_UNLOCK(sc); + sent++; + } } + PF_HASHROW_UNLOCK(ih); } -#ifdef __FreeBSD__ + /* We're done. */ + pfsync_bulk_status(PFSYNC_BUS_END); + +full: CURVNET_RESTORE(); -#endif - splx(s); } -void +static void pfsync_bulk_status(u_int8_t status) { struct { @@ -3054,268 +2099,151 @@ pfsync_bulk_status(u_int8_t status) struct pfsync_bus bus; } __packed r; -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - - PF_LOCK_ASSERT(); bzero(&r, sizeof(r)); r.subh.action = PFSYNC_ACT_BUS; r.subh.count = htons(1); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_BUS]++; -#ifdef __FreeBSD__ r.bus.creatorid = V_pf_status.hostid; -#else - r.bus.creatorid = pf_status.hostid; -#endif r.bus.endtime = htonl(time_uptime - sc->sc_ureq_received); r.bus.status = status; + PFSYNC_LOCK(sc); pfsync_send_plus(&r, sizeof(r)); + PFSYNC_UNLOCK(sc); } -void +static void pfsync_bulk_fail(void *arg) { struct pfsync_softc *sc = arg; -#ifdef __FreeBSD__ CURVNET_SET(sc->sc_ifp->if_vnet); -#endif + + PFSYNC_BLOCK_ASSERT(sc); if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) { /* Try again */ -#ifdef __FreeBSD__ callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulk_fail, V_pfsyncif); -#else - timeout_add_sec(&sc->sc_bulkfail_tmo, 5); -#endif - PF_LOCK(); + PFSYNC_LOCK(sc); pfsync_request_update(0, 0); - PF_UNLOCK(); + PFSYNC_UNLOCK(sc); } else { - /* Pretend like the transfer was ok */ + /* Pretend like the transfer was ok. */ sc->sc_ureq_sent = 0; sc->sc_bulk_tries = 0; -#ifdef __FreeBSD__ - if (!sc->pfsync_sync_ok && carp_demote_adj_p) + PFSYNC_LOCK(sc); + if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) (*carp_demote_adj_p)(-V_pfsync_carp_adj, "pfsync bulk fail"); - sc->pfsync_sync_ok = 1; -#else -#if NCARP > 0 - if (!pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, -1); -#endif - pfsync_sync_ok = 1; -#endif -#ifdef __FreeBSD__ + sc->sc_flags |= PFSYNCF_OK; + PFSYNC_UNLOCK(sc); if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif printf("pfsync: failed to receive bulk update\n"); } -#ifdef __FreeBSD__ CURVNET_RESTORE(); -#endif } -void +static void pfsync_send_plus(void *plus, size_t pluslen) { -#ifdef __FreeBSD__ struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - int s; - PF_LOCK_ASSERT(); + PFSYNC_LOCK_ASSERT(sc); -#ifdef __FreeBSD__ - if (sc->sc_len + pluslen > sc->sc_ifp->if_mtu) { -#else - if (sc->sc_len + pluslen > sc->sc_if.if_mtu) { -#endif - s = splnet(); - pfsync_sendout(); - splx(s); - } + if (sc->sc_len + pluslen > sc->sc_ifp->if_mtu) + pfsync_sendout(1); sc->sc_plus = plus; sc->sc_len += (sc->sc_pluslen = pluslen); - s = splnet(); - pfsync_sendout(); - splx(s); -} - -int -pfsync_up(void) -{ -#ifdef __FreeBSD__ - struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - -#ifdef __FreeBSD__ - if (sc == NULL || !ISSET(sc->sc_ifp->if_flags, IFF_DRV_RUNNING)) -#else - if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING)) -#endif - return (0); - - return (1); -} - -int -pfsync_state_in_use(struct pf_state *st) -{ -#ifdef __FreeBSD__ - struct pfsync_softc *sc = V_pfsyncif; -#else - struct pfsync_softc *sc = pfsyncif; -#endif - - if (sc == NULL) - return (0); - - if (st->sync_state != PFSYNC_S_NONE || - st == sc->sc_bulk_next || - st == sc->sc_bulk_last) - return (1); - - return (0); + pfsync_sendout(1); } -u_int pfsync_ints; -u_int pfsync_tmos; - -void +static void pfsync_timeout(void *arg) { -#if defined(__FreeBSD__) && defined(VIMAGE) struct pfsync_softc *sc = arg; -#endif - int s; -#ifdef __FreeBSD__ CURVNET_SET(sc->sc_ifp->if_vnet); -#endif + PFSYNC_LOCK(sc); + pfsync_push(sc); + PFSYNC_UNLOCK(sc); + CURVNET_RESTORE(); +} - pfsync_tmos++; +static void +pfsync_push(struct pfsync_softc *sc) +{ - s = splnet(); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - pfsync_sendout(); -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - splx(s); + PFSYNC_LOCK_ASSERT(sc); -#ifdef __FreeBSD__ - CURVNET_RESTORE(); -#endif + sc->sc_flags |= PFSYNCF_PUSH; + swi_sched(V_pfsync_swi_cookie, 0); } -/* this is a softnet/netisr handler */ -void -#ifdef __FreeBSD__ +static void pfsyncintr(void *arg) { struct pfsync_softc *sc = arg; struct mbuf *m, *n; CURVNET_SET(sc->sc_ifp->if_vnet); - pfsync_ints++; - PF_LOCK(); - if (sc->sc_len > PFSYNC_MINPKT) - pfsync_sendout1(0); + PFSYNC_LOCK(sc); + if ((sc->sc_flags & PFSYNCF_PUSH) && sc->sc_len > PFSYNC_MINPKT) { + pfsync_sendout(0); + sc->sc_flags &= ~PFSYNCF_PUSH; + } _IF_DEQUEUE_ALL(&sc->sc_ifp->if_snd, m); - PF_UNLOCK(); + PFSYNC_UNLOCK(sc); for (; m != NULL; m = n) { n = m->m_nextpkt; m->m_nextpkt = NULL; - if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL) - == 0) + + /* + * We distinguish between a deferral packet and our + * own pfsync packet based on M_SKIP_FIREWALL + * flag. This is XXX. + */ + if (m->m_flags & M_SKIP_FIREWALL) + ip_output(m, NULL, NULL, 0, NULL, NULL); + else if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, + NULL) == 0) V_pfsyncstats.pfsyncs_opackets++; else V_pfsyncstats.pfsyncs_oerrors++; } CURVNET_RESTORE(); } -#else -pfsyncintr(void) -{ - int s; - pfsync_ints++; - - s = splnet(); - pfsync_sendout(); - splx(s); -} -#endif - -int -pfsync_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) -{ - -#ifdef notyet - /* All sysctl names at this level are terminal. */ - if (namelen != 1) - return (ENOTDIR); - - switch (name[0]) { - case PFSYNCCTL_STATS: - if (newp != NULL) - return (EPERM); - return (sysctl_struct(oldp, oldlenp, newp, newlen, - &V_pfsyncstats, sizeof(V_pfsyncstats))); - } -#endif - return (ENOPROTOOPT); -} - -#ifdef __FreeBSD__ static int -pfsync_multicast_setup(struct pfsync_softc *sc) +pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp, void *mship) { struct ip_moptions *imo = &sc->sc_imo; int error; - if (!(sc->sc_sync_if->if_flags & IFF_MULTICAST)) { - sc->sc_sync_if = NULL; + if (!(ifp->if_flags & IFF_MULTICAST)) return (EADDRNOTAVAIL); - } - imo->imo_membership = (struct in_multi **)malloc( - (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_PFSYNC, - M_WAITOK | M_ZERO); + imo->imo_membership = (struct in_multi **)mship; imo->imo_max_memberships = IP_MIN_MEMBERSHIPS; imo->imo_multicast_vif = -1; - if ((error = in_joingroup(sc->sc_sync_if, &sc->sc_sync_peer, NULL, + if ((error = in_joingroup(ifp, &sc->sc_sync_peer, NULL, &imo->imo_membership[0])) != 0) { - free(imo->imo_membership, M_PFSYNC); + imo->imo_membership = NULL; return (error); } imo->imo_num_memberships++; - imo->imo_multicast_ifp = sc->sc_sync_if; + imo->imo_multicast_ifp = ifp; imo->imo_multicast_ttl = PFSYNC_DFLTTL; imo->imo_multicast_loop = 0; @@ -3377,16 +2305,14 @@ pfsync_init() goto fail; } #endif - PF_LOCK(); + PF_RULES_WLOCK(); pfsync_state_import_ptr = pfsync_state_import; - pfsync_up_ptr = pfsync_up; pfsync_insert_state_ptr = pfsync_insert_state; pfsync_update_state_ptr = pfsync_update_state; pfsync_delete_state_ptr = pfsync_delete_state; pfsync_clear_states_ptr = pfsync_clear_states; - pfsync_state_in_use_ptr = pfsync_state_in_use; pfsync_defer_ptr = pfsync_defer; - PF_UNLOCK(); + PF_RULES_WUNLOCK(); return (0); @@ -3411,24 +2337,22 @@ pfsync_uninit() { VNET_ITERATOR_DECL(vnet_iter); - PF_LOCK(); + PF_RULES_WLOCK(); pfsync_state_import_ptr = NULL; - pfsync_up_ptr = NULL; pfsync_insert_state_ptr = NULL; pfsync_update_state_ptr = NULL; pfsync_delete_state_ptr = NULL; pfsync_clear_states_ptr = NULL; - pfsync_state_in_use_ptr = NULL; pfsync_defer_ptr = NULL; - PF_UNLOCK(); + PF_RULES_WUNLOCK(); ipproto_unregister(IPPROTO_PFSYNC); pf_proto_unregister(PF_INET, IPPROTO_PFSYNC, SOCK_RAW); VNET_LIST_RLOCK(); VNET_FOREACH(vnet_iter) { CURVNET_SET(vnet_iter); - swi_remove(V_pfsync_swi_cookie); if_clone_detach(&V_pfsync_cloner); + swi_remove(V_pfsync_swi_cookie); CURVNET_RESTORE(); } VNET_LIST_RUNLOCK(); @@ -3471,4 +2395,3 @@ static moduledata_t pfsync_mod = { DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); MODULE_VERSION(pfsync, PFSYNC_MODVER); MODULE_DEPEND(pfsync, pf, PF_MODVER, PF_MODVER, PF_MODVER); -#endif /* __FreeBSD__ */ diff --git a/sys/contrib/pf/net/if_pfsync.h b/sys/contrib/pf/net/if_pfsync.h index 3f340380181c3..5e71dd8e46f5c 100644 --- a/sys/contrib/pf/net/if_pfsync.h +++ b/sys/contrib/pf/net/if_pfsync.h @@ -182,7 +182,7 @@ struct pfsync_del_c { u_int32_t creatorid; } __packed; -/* +/* * INS_F, DEL_F */ @@ -256,6 +256,9 @@ struct pfsyncstats { u_int64_t pfsyncs_opackets6; /* total output packets, IPv6 */ u_int64_t pfsyncs_onomem; /* no memory for an mbuf */ u_int64_t pfsyncs_oerrors; /* ip output error */ + + u_int64_t pfsyncs_iacts[PFSYNC_ACT_MAX]; + u_int64_t pfsyncs_oacts[PFSYNC_ACT_MAX]; }; /* @@ -268,10 +271,8 @@ struct pfsyncreq { int pfsyncr_defer; }; -#ifdef __FreeBSD__ #define SIOCSETPFSYNC _IOW('i', 247, struct ifreq) #define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq) -#endif #ifdef _KERNEL @@ -288,37 +289,10 @@ struct pfsyncreq { #define PFSYNC_S_DEFER 0xfe #define PFSYNC_S_NONE 0xff -#ifdef __FreeBSD__ -void pfsync_input(struct mbuf *, __unused int); -#else -void pfsync_input(struct mbuf *, ...); -#endif -int pfsync_sysctl(int *, u_int, void *, size_t *, - void *, size_t); - #define PFSYNC_SI_IOCTL 0x01 #define PFSYNC_SI_CKSUM 0x02 #define PFSYNC_SI_ACK 0x04 -int pfsync_state_import(struct pfsync_state *, u_int8_t); -#ifndef __FreeBSD__ -void pfsync_state_export(struct pfsync_state *, - struct pf_state *); -#endif - -void pfsync_insert_state(struct pf_state *); -void pfsync_update_state(struct pf_state *); -void pfsync_delete_state(struct pf_state *); -void pfsync_clear_states(u_int32_t, const char *); - -#ifdef notyet -void pfsync_update_tdb(struct tdb *, int); -void pfsync_delete_tdb(struct tdb *); -#endif - -int pfsync_defer(struct pf_state *, struct mbuf *); - -int pfsync_up(void); -int pfsync_state_in_use(struct pf_state *); -#endif + +#endif /* _KERNEL */ #endif /* _NET_IF_PFSYNC_H_ */ diff --git a/sys/contrib/pf/net/pf.c b/sys/contrib/pf/net/pf.c index ac51282626977..b66d6dd538482 100644 --- a/sys/contrib/pf/net/pf.c +++ b/sys/contrib/pf/net/pf.c @@ -35,138 +35,77 @@ * */ -#ifdef __FreeBSD__ -#include "opt_inet.h" -#include "opt_inet6.h" - #include + __FBSDID("$FreeBSD$"); -#endif -#ifdef __FreeBSD__ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_bpf.h" #include "opt_pf.h" -#define NPFSYNC 1 - -#ifdef DEV_PFLOW -#define NPFLOW DEV_PFLOW -#else -#define NPFLOW 0 -#endif - -#else -#include "bpfilter.h" -#include "pflog.h" -#include "pfsync.h" -#include "pflow.h" -#endif - #include -#include -#include -#include -#include -#include -#include -#include -#ifdef __FreeBSD__ -#include -#include +#include #include -#define betoh64 be64toh -#else -#include -#endif -#include -#ifdef __FreeBSD__ +#include +#include +#include #include -#include -#include -#else -#include -#endif - -#ifdef __FreeBSD__ +#include +#include #include -#else -#include -#endif +#include +#include +#include +#include +#include +#include #include #include -#include #include -#ifdef __FreeBSD__ -#ifdef RADIX_MPATH -#include -#endif -#else #include -#endif +#include + +#include +#include +#include +#include -#include +#include #include -#include #include +#include +#include +#include #include +#include /* XXX: only for DIR_IN/DIR_OUT */ #include +#include #include -#include -#include -#include #include #include +#include #include -#include -#include -#ifdef __FreeBSD__ -#include -#include /* XXX: only for DIR_IN/DIR_OUT */ -#endif - -#ifndef __FreeBSD__ -#include -#endif -#include -#include -#include -#include #ifdef INET6 #include -#include #include #include -#ifdef __FreeBSD__ #include #include -#endif #endif /* INET6 */ -#ifdef __FreeBSD__ #include -#include -#include #include -extern int ip_optcopy(struct ip *, struct ip *); -#endif - -#ifdef __FreeBSD__ #define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x -#else -#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x -#endif /* * Global variables */ /* state tables */ -#ifdef __FreeBSD__ -VNET_DEFINE(struct pf_state_tree, pf_statetbl); - VNET_DEFINE(struct pf_altqqueue, pf_altqs[2]); VNET_DEFINE(struct pf_palist, pf_pabuf); VNET_DEFINE(struct pf_altqqueue *, pf_altqs_active); @@ -196,221 +135,206 @@ struct pf_anchor_stackframe { VNET_DEFINE(struct pf_anchor_stackframe, pf_anchor_stack[64]); #define V_pf_anchor_stack VNET(pf_anchor_stack) -VNET_DEFINE(uma_zone_t, pf_src_tree_pl); -VNET_DEFINE(uma_zone_t, pf_rule_pl); -VNET_DEFINE(uma_zone_t, pf_pooladdr_pl); -VNET_DEFINE(uma_zone_t, pf_state_pl); -VNET_DEFINE(uma_zone_t, pf_state_key_pl); -VNET_DEFINE(uma_zone_t, pf_state_item_pl); -VNET_DEFINE(uma_zone_t, pf_altq_pl); -#else -struct pf_state_tree pf_statetbl; - -struct pf_altqqueue pf_altqs[2]; -struct pf_palist pf_pabuf; -struct pf_altqqueue *pf_altqs_active; -struct pf_altqqueue *pf_altqs_inactive; -struct pf_status pf_status; +/* + * Queue for pf_intr() sends. + */ +static MALLOC_DEFINE(M_PFTEMP, "pf_temp", "pf(4) temporary allocations"); +struct pf_send_entry { + STAILQ_ENTRY(pf_send_entry) pfse_next; + struct mbuf *pfse_m; + enum { + PFSE_IP, + PFSE_IP6, + PFSE_ICMP, + PFSE_ICMP6, + } pfse_type; + union { + struct route ro; + struct { + int type; + int code; + int mtu; + } icmpopts; + } u; +#define pfse_ro u.ro +#define pfse_icmp_type u.icmpopts.type +#define pfse_icmp_code u.icmpopts.code +#define pfse_icmp_mtu u.icmpopts.mtu +}; -u_int32_t ticket_altqs_active; -u_int32_t ticket_altqs_inactive; -int altqs_inactive_open; -u_int32_t ticket_pabuf; +STAILQ_HEAD(pf_send_head, pf_send_entry); +static VNET_DEFINE(struct pf_send_head, pf_sendqueue); +#define V_pf_sendqueue VNET(pf_sendqueue) -MD5_CTX pf_tcp_secret_ctx; -u_char pf_tcp_secret[16]; -int pf_tcp_secret_init; -int pf_tcp_iss_off; +static struct mtx pf_sendqueue_mtx; +#define PF_SENDQ_LOCK() mtx_lock(&pf_sendqueue_mtx) +#define PF_SENDQ_UNLOCK() mtx_unlock(&pf_sendqueue_mtx) -struct pf_anchor_stackframe { - struct pf_ruleset *rs; - struct pf_rule *r; - struct pf_anchor_node *parent; - struct pf_anchor *child; -} pf_anchor_stack[64]; - -struct pool pf_src_tree_pl, pf_rule_pl, pf_pooladdr_pl; -struct pool pf_state_pl, pf_state_key_pl, pf_state_item_pl; -struct pool pf_altq_pl; -#endif +/* + * Queue for pf_flush_task() tasks. + */ +struct pf_flush_entry { + SLIST_ENTRY(pf_flush_entry) next; + struct pf_addr addr; + sa_family_t af; + uint8_t dir; + struct pf_rule *rule; /* never dereferenced */ +}; -void pf_init_threshold(struct pf_threshold *, u_int32_t, +SLIST_HEAD(pf_flush_head, pf_flush_entry); +static VNET_DEFINE(struct pf_flush_head, pf_flushqueue); +#define V_pf_flushqueue VNET(pf_flushqueue) +static VNET_DEFINE(struct task, pf_flushtask); +#define V_pf_flushtask VNET(pf_flushtask) + +static struct mtx pf_flushqueue_mtx; +#define PF_FLUSHQ_LOCK() mtx_lock(&pf_flushqueue_mtx) +#define PF_FLUSHQ_UNLOCK() mtx_unlock(&pf_flushqueue_mtx) + +VNET_DEFINE(struct pf_rulequeue, pf_unlinked_rules); +struct mtx pf_unlnkdrules_mtx; + +static VNET_DEFINE(uma_zone_t, pf_sources_z); +#define V_pf_sources_z VNET(pf_sources_z) +static VNET_DEFINE(uma_zone_t, pf_mtag_z); +#define V_pf_mtag_z VNET(pf_mtag_z) +VNET_DEFINE(uma_zone_t, pf_state_z); +VNET_DEFINE(uma_zone_t, pf_state_key_z); + +VNET_DEFINE(uint64_t, pf_stateid[MAXCPU]); +#define PFID_CPUBITS 8 +#define PFID_CPUSHIFT (sizeof(uint64_t) * NBBY - PFID_CPUBITS) +#define PFID_CPUMASK ((uint64_t)((1 << PFID_CPUBITS) - 1) << PFID_CPUSHIFT) +#define PFID_MAXID (~PFID_CPUMASK) +CTASSERT((1 << PFID_CPUBITS) > MAXCPU); + +static void pf_src_tree_remove_state(struct pf_state *); +static void pf_init_threshold(struct pf_threshold *, u_int32_t, u_int32_t); -void pf_add_threshold(struct pf_threshold *); -int pf_check_threshold(struct pf_threshold *); +static void pf_add_threshold(struct pf_threshold *); +static int pf_check_threshold(struct pf_threshold *); -void pf_change_ap(struct pf_addr *, u_int16_t *, +static void pf_change_ap(struct pf_addr *, u_int16_t *, u_int16_t *, u_int16_t *, struct pf_addr *, u_int16_t, u_int8_t, sa_family_t); -int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *, +static int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *); -#ifdef INET6 -void pf_change_a6(struct pf_addr *, u_int16_t *, - struct pf_addr *, u_int8_t); -#endif /* INET6 */ -void pf_change_icmp(struct pf_addr *, u_int16_t *, +static void pf_change_icmp(struct pf_addr *, u_int16_t *, struct pf_addr *, struct pf_addr *, u_int16_t, u_int16_t *, u_int16_t *, u_int16_t *, u_int16_t *, u_int8_t, sa_family_t); -#ifdef __FreeBSD__ -void pf_send_tcp(struct mbuf *, +static void pf_send_tcp(struct mbuf *, const struct pf_rule *, sa_family_t, -#else -void pf_send_tcp(const struct pf_rule *, sa_family_t, -#endif const struct pf_addr *, const struct pf_addr *, u_int16_t, u_int16_t, u_int32_t, u_int32_t, u_int8_t, u_int16_t, u_int16_t, u_int8_t, int, - u_int16_t, struct ether_header *, struct ifnet *); + u_int16_t, struct ifnet *); static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, sa_family_t, struct pf_rule *); -void pf_detach_state(struct pf_state *); -void pf_state_key_detach(struct pf_state *, int); -u_int32_t pf_tcp_iss(struct pf_pdesc *); -int pf_test_rule(struct pf_rule **, struct pf_state **, +static void pf_detach_state(struct pf_state *); +static int pf_state_key_attach(struct pf_state_key *, + struct pf_state_key *, struct pf_state *); +static void pf_state_key_detach(struct pf_state *, int); +static int pf_state_key_ctor(void *, int, void *, int); +static u_int32_t pf_tcp_iss(struct pf_pdesc *); +static int pf_test_rule(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, - void *, struct pf_pdesc *, struct pf_rule **, -#ifdef __FreeBSD__ - struct pf_ruleset **, struct ifqueue *, - struct inpcb *); -#else - struct pf_ruleset **, struct ifqueue *); -#endif -static __inline int pf_create_state(struct pf_rule *, struct pf_rule *, + struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **, struct inpcb *); +static int pf_create_state(struct pf_rule *, struct pf_rule *, struct pf_rule *, struct pf_pdesc *, struct pf_src_node *, struct pf_state_key *, - struct pf_state_key *, struct pf_state_key *, struct pf_state_key *, struct mbuf *, int, u_int16_t, u_int16_t, int *, struct pfi_kif *, struct pf_state **, int, u_int16_t, u_int16_t, int); -int pf_test_fragment(struct pf_rule **, int, +static int pf_test_fragment(struct pf_rule **, int, struct pfi_kif *, struct mbuf *, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); -int pf_tcp_track_full(struct pf_state_peer *, +static int pf_tcp_track_full(struct pf_state_peer *, struct pf_state_peer *, struct pf_state **, struct pfi_kif *, struct mbuf *, int, struct pf_pdesc *, u_short *, int *); -int pf_tcp_track_sloppy(struct pf_state_peer *, +static int pf_tcp_track_sloppy(struct pf_state_peer *, struct pf_state_peer *, struct pf_state **, struct pf_pdesc *, u_short *); -int pf_test_state_tcp(struct pf_state **, int, +static int pf_test_state_tcp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); -int pf_test_state_udp(struct pf_state **, int, +static int pf_test_state_udp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *); -int pf_test_state_icmp(struct pf_state **, int, +static int pf_test_state_icmp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); -int pf_test_state_other(struct pf_state **, int, +static int pf_test_state_other(struct pf_state **, int, struct pfi_kif *, struct mbuf *, struct pf_pdesc *); -void pf_route(struct mbuf **, struct pf_rule *, int, - struct ifnet *, struct pf_state *, - struct pf_pdesc *); -void pf_route6(struct mbuf **, struct pf_rule *, int, - struct ifnet *, struct pf_state *, - struct pf_pdesc *); -#ifndef __FreeBSD__ -int pf_socket_lookup(int, struct pf_pdesc *); -#endif -u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, +static u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t); -u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, +static u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, sa_family_t); -u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, +static u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, int, u_int16_t); -void pf_set_rt_ifp(struct pf_state *, +static void pf_set_rt_ifp(struct pf_state *, struct pf_addr *); -int pf_check_proto_cksum(struct mbuf *, int, int, +static int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); -#ifndef __FreeBSD__ -struct pf_divert *pf_get_divert(struct mbuf *); -#endif -void pf_print_state_parts(struct pf_state *, +static void pf_print_state_parts(struct pf_state *, struct pf_state_key *, struct pf_state_key *); -int pf_addr_wrap_neq(struct pf_addr_wrap *, +static int pf_addr_wrap_neq(struct pf_addr_wrap *, struct pf_addr_wrap *); -int pf_compare_state_keys(struct pf_state_key *, - struct pf_state_key *, struct pfi_kif *, u_int); -#ifdef __FreeBSD__ -struct pf_state *pf_find_state(struct pfi_kif *, - struct pf_state_key_cmp *, u_int, struct mbuf *, - struct pf_mtag *); -#else -struct pf_state *pf_find_state(struct pfi_kif *, - struct pf_state_key_cmp *, u_int, struct mbuf *); -#endif -int pf_src_connlimit(struct pf_state **); -int pf_check_congestion(struct ifqueue *); +static struct pf_state *pf_find_state(struct pfi_kif *, + struct pf_state_key_cmp *, u_int); +static int pf_src_connlimit(struct pf_state **); +static void pf_flush_task(void *c, int pending); +static int pf_insert_src_node(struct pf_src_node **, + struct pf_rule *, struct pf_addr *, sa_family_t); +static int pf_purge_expired_states(int); +static void pf_purge_unlinked_rules(void); +static int pf_mtag_init(void *, int, int); +static void pf_mtag_free(struct m_tag *); +#ifdef INET +static void pf_route(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *, + struct pf_pdesc *); +#endif /* INET */ +#ifdef INET6 +static void pf_change_a6(struct pf_addr *, u_int16_t *, + struct pf_addr *, u_int8_t); +static void pf_route6(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *, + struct pf_pdesc *); +#endif /* INET6 */ -#ifdef __FreeBSD__ int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); VNET_DECLARE(int, pf_end_threads); -VNET_DEFINE(struct pf_pool_limit, pf_pool_limits[PF_LIMIT_MAX]); -#else -extern struct pool pfr_ktable_pl; -extern struct pool pfr_kentry_pl; - -struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = { - { &pf_state_pl, PFSTATE_HIWAT }, - { &pf_src_tree_pl, PFSNODE_HIWAT }, - { &pf_frent_pl, PFFRAG_FRENT_HIWAT }, - { &pfr_ktable_pl, PFR_KTABLE_HIWAT }, - { &pfr_kentry_pl, PFR_KENTRY_HIWAT } -}; -#endif - -#ifdef __FreeBSD__ -#define PPACKET_LOOPED() \ - (pd->pf_mtag->flags & PF_PACKET_LOOPED) +VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); -#define PACKET_LOOPED() \ - (pd.pf_mtag->flags & PF_PACKET_LOOPED) +#define PACKET_LOOPED(pd) ((pd)->pf_mtag && \ + (pd)->pf_mtag->flags & PF_PACKET_LOOPED) -#define STATE_LOOKUP(i, k, d, s, m, pt) \ +#define STATE_LOOKUP(i, k, d, s, pd) \ do { \ - s = pf_find_state(i, k, d, m, pt); \ - if (s == NULL || (s)->timeout == PFTM_PURGE) \ + (s) = pf_find_state((i), (k), (d)); \ + if ((s) == NULL || (s)->timeout == PFTM_PURGE) \ return (PF_DROP); \ - if (PPACKET_LOOPED()) \ - return (PF_PASS); \ - if (d == PF_OUT && \ - (((s)->rule.ptr->rt == PF_ROUTETO && \ - (s)->rule.ptr->direction == PF_OUT) || \ - ((s)->rule.ptr->rt == PF_REPLYTO && \ - (s)->rule.ptr->direction == PF_IN)) && \ - (s)->rt_kif != NULL && \ - (s)->rt_kif != i) \ + if (PACKET_LOOPED(pd)) \ return (PF_PASS); \ - } while (0) -#else -#define STATE_LOOKUP(i, k, d, s, m) \ - do { \ - s = pf_find_state(i, k, d, m); \ - if (s == NULL || (s)->timeout == PFTM_PURGE) \ - return (PF_DROP); \ - if (d == PF_OUT && \ + if ((d) == PF_OUT && \ (((s)->rule.ptr->rt == PF_ROUTETO && \ (s)->rule.ptr->direction == PF_OUT) || \ ((s)->rule.ptr->rt == PF_REPLYTO && \ (s)->rule.ptr->direction == PF_IN)) && \ (s)->rt_kif != NULL && \ - (s)->rt_kif != i) \ + (s)->rt_kif != (i)) \ return (PF_PASS); \ } while (0) -#endif -#ifdef __FreeBSD__ #define BOUND_IFACE(r, k) \ ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all -#else -#define BOUND_IFACE(r, k) \ - ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : pfi_all -#endif #define STATE_INC_COUNTERS(s) \ do { \ @@ -435,71 +359,40 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = { s->rule.ptr->states_cur--; \ } while (0) -static __inline int pf_src_compare(struct pf_src_node *, struct pf_src_node *); -static __inline int pf_state_compare_key(struct pf_state_key *, - struct pf_state_key *); -static __inline int pf_state_compare_id(struct pf_state *, - struct pf_state *); +static MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures"); +VNET_DEFINE(struct pf_keyhash *, pf_keyhash); +VNET_DEFINE(struct pf_idhash *, pf_idhash); +VNET_DEFINE(u_long, pf_hashmask); +VNET_DEFINE(struct pf_srchash *, pf_srchash); +VNET_DEFINE(u_long, pf_srchashmask); -#ifdef __FreeBSD__ -VNET_DEFINE(struct pf_src_tree, tree_src_tracking); +SYSCTL_NODE(_net, OID_AUTO, pf, CTLFLAG_RW, 0, "pf(4)"); -VNET_DEFINE(struct pf_state_tree_id, tree_id); -VNET_DEFINE(struct pf_state_queue, state_list); -#else -struct pf_src_tree tree_src_tracking; +VNET_DEFINE(u_long, pf_hashsize); +#define V_pf_hashsize VNET(pf_hashsize) +SYSCTL_VNET_UINT(_net_pf, OID_AUTO, states_hashsize, CTLFLAG_RDTUN, + &VNET_NAME(pf_hashsize), 0, "Size of pf(4) states hashtable"); -struct pf_state_tree_id tree_id; -struct pf_state_queue state_list; -#endif +VNET_DEFINE(u_long, pf_srchashsize); +#define V_pf_srchashsize VNET(pf_srchashsize) +SYSCTL_VNET_UINT(_net_pf, OID_AUTO, source_nodes_hashsize, CTLFLAG_RDTUN, + &VNET_NAME(pf_srchashsize), 0, "Size of pf(4) source nodes hashtable"); -RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare); -RB_GENERATE(pf_state_tree, pf_state_key, entry, pf_state_compare_key); -RB_GENERATE(pf_state_tree_id, pf_state, - entry_id, pf_state_compare_id); +VNET_DEFINE(void *, pf_swi_cookie); -static __inline int -pf_src_compare(struct pf_src_node *a, struct pf_src_node *b) +VNET_DEFINE(uint32_t, pf_hashseed); +#define V_pf_hashseed VNET(pf_hashseed) + +static __inline uint32_t +pf_hashkey(struct pf_state_key *sk) { - int diff; + uint32_t h; - if (a->rule.ptr > b->rule.ptr) - return (1); - if (a->rule.ptr < b->rule.ptr) - return (-1); - if ((diff = a->af - b->af) != 0) - return (diff); - switch (a->af) { -#ifdef INET - case AF_INET: - if (a->addr.addr32[0] > b->addr.addr32[0]) - return (1); - if (a->addr.addr32[0] < b->addr.addr32[0]) - return (-1); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (a->addr.addr32[3] > b->addr.addr32[3]) - return (1); - if (a->addr.addr32[3] < b->addr.addr32[3]) - return (-1); - if (a->addr.addr32[2] > b->addr.addr32[2]) - return (1); - if (a->addr.addr32[2] < b->addr.addr32[2]) - return (-1); - if (a->addr.addr32[1] > b->addr.addr32[1]) - return (1); - if (a->addr.addr32[1] < b->addr.addr32[1]) - return (-1); - if (a->addr.addr32[0] > b->addr.addr32[0]) - return (1); - if (a->addr.addr32[0] < b->addr.addr32[0]) - return (-1); - break; -#endif /* INET6 */ - } - return (0); + h = jenkins_hash32((uint32_t *)sk, + sizeof(struct pf_state_key_cmp)/sizeof(uint32_t), + V_pf_hashseed); + + return (h & V_pf_hashmask); } #ifdef INET6 @@ -522,20 +415,20 @@ pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) } #endif /* INET6 */ -void +static void pf_init_threshold(struct pf_threshold *threshold, u_int32_t limit, u_int32_t seconds) { threshold->limit = limit * PF_THRESHOLD_MULT; threshold->seconds = seconds; threshold->count = 0; - threshold->last = time_second; + threshold->last = time_uptime; } -void +static void pf_add_threshold(struct pf_threshold *threshold) { - u_int32_t t = time_second, diff = t - threshold->last; + u_int32_t t = time_uptime, diff = t - threshold->last; if (diff >= threshold->seconds) threshold->count = 0; @@ -546,17 +439,21 @@ pf_add_threshold(struct pf_threshold *threshold) threshold->last = t; } -int +static int pf_check_threshold(struct pf_threshold *threshold) { return (threshold->count > threshold->limit); } -int +static int pf_src_connlimit(struct pf_state **state) { + struct pfr_addr p; + struct pf_flush_entry *pffe; int bad = 0; + PF_STATE_LOCK_ASSERT(*state); + (*state)->src_node->conn++; (*state)->src.tcp_est = 1; pf_add_threshold(&(*state)->src_node->conn_rate); @@ -564,333 +461,407 @@ pf_src_connlimit(struct pf_state **state) if ((*state)->rule.ptr->max_src_conn && (*state)->rule.ptr->max_src_conn < (*state)->src_node->conn) { -#ifdef __FreeBSD__ V_pf_status.lcounters[LCNT_SRCCONN]++; -#else - pf_status.lcounters[LCNT_SRCCONN]++; -#endif bad++; } if ((*state)->rule.ptr->max_src_conn_rate.limit && pf_check_threshold(&(*state)->src_node->conn_rate)) { -#ifdef __FreeBSD__ V_pf_status.lcounters[LCNT_SRCCONNRATE]++; -#else - pf_status.lcounters[LCNT_SRCCONNRATE]++; -#endif bad++; } if (!bad) return (0); - if ((*state)->rule.ptr->overload_tbl) { - struct pfr_addr p; - u_int32_t killed = 0; + /* Kill this state. */ + (*state)->timeout = PFTM_PURGE; + (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; + + if ((*state)->rule.ptr->overload_tbl == NULL) + return (1); -#ifdef __FreeBSD__ - V_pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; - if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif - printf("pf_src_connlimit: blocking address "); - pf_print_host(&(*state)->src_node->addr, 0, - (*state)->key[PF_SK_WIRE]->af); - } + V_pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("%s: blocking address ", __func__); + pf_print_host(&(*state)->src_node->addr, 0, + (*state)->key[PF_SK_WIRE]->af); + printf("\n"); + } - bzero(&p, sizeof(p)); - p.pfra_af = (*state)->key[PF_SK_WIRE]->af; - switch ((*state)->key[PF_SK_WIRE]->af) { + bzero(&p, sizeof(p)); + p.pfra_af = (*state)->key[PF_SK_WIRE]->af; + switch ((*state)->key[PF_SK_WIRE]->af) { #ifdef INET - case AF_INET: - p.pfra_net = 32; - p.pfra_ip4addr = (*state)->src_node->addr.v4; - break; + case AF_INET: + p.pfra_net = 32; + p.pfra_ip4addr = (*state)->src_node->addr.v4; + break; #endif /* INET */ #ifdef INET6 - case AF_INET6: - p.pfra_net = 128; - p.pfra_ip6addr = (*state)->src_node->addr.v6; - break; + case AF_INET6: + p.pfra_net = 128; + p.pfra_ip6addr = (*state)->src_node->addr.v6; + break; #endif /* INET6 */ - } + } - pfr_insert_kentry((*state)->rule.ptr->overload_tbl, - &p, time_second); + pfr_insert_kentry((*state)->rule.ptr->overload_tbl, &p, time_second); - /* kill existing states if that's required. */ - if ((*state)->rule.ptr->flush) { - struct pf_state_key *sk; - struct pf_state *st; + if ((*state)->rule.ptr->flush == 0) + return (1); -#ifdef __FreeBSD__ - V_pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; - RB_FOREACH(st, pf_state_tree_id, &V_tree_id) { -#else - pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; - RB_FOREACH(st, pf_state_tree_id, &tree_id) { -#endif - sk = st->key[PF_SK_WIRE]; - /* - * Kill states from this source. (Only those - * from the same rule if PF_FLUSH_GLOBAL is not - * set) - */ - if (sk->af == - (*state)->key[PF_SK_WIRE]->af && - (((*state)->direction == PF_OUT && - PF_AEQ(&(*state)->src_node->addr, - &sk->addr[1], sk->af)) || - ((*state)->direction == PF_IN && - PF_AEQ(&(*state)->src_node->addr, - &sk->addr[0], sk->af))) && - ((*state)->rule.ptr->flush & - PF_FLUSH_GLOBAL || - (*state)->rule.ptr == st->rule.ptr)) { - st->timeout = PFTM_PURGE; - st->src.state = st->dst.state = - TCPS_CLOSED; - killed++; - } + /* Schedule flushing task. */ + pffe = malloc(sizeof(*pffe), M_PFTEMP, M_NOWAIT); + if (pffe == NULL) + return (1); /* too bad :( */ + + bcopy(&(*state)->src_node->addr, &pffe->addr, sizeof(pffe->addr)); + pffe->af = (*state)->key[PF_SK_WIRE]->af; + pffe->dir = (*state)->direction; + if ((*state)->rule.ptr->flush & PF_FLUSH_GLOBAL) + pffe->rule = NULL; + else + pffe->rule = (*state)->rule.ptr; + PF_FLUSHQ_LOCK(); + SLIST_INSERT_HEAD(&V_pf_flushqueue, pffe, next); + PF_FLUSHQ_UNLOCK(); + taskqueue_enqueue(taskqueue_swi, &V_pf_flushtask); + + return (1); +} + +static void +pf_flush_task(void *c, int pending) +{ + struct pf_flush_head queue; + struct pf_flush_entry *pffe, *pffe1; + uint32_t killed = 0; + + PF_FLUSHQ_LOCK(); + queue = *(struct pf_flush_head *)c; + SLIST_INIT((struct pf_flush_head *)c); + PF_FLUSHQ_UNLOCK(); + + V_pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; + + for (int i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_state_key *sk; + struct pf_state *s; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + sk = s->key[PF_SK_WIRE]; + SLIST_FOREACH(pffe, &queue, next) + if (sk->af == pffe->af && (pffe->rule == NULL || + pffe->rule == s->rule.ptr) && + ((pffe->dir == PF_OUT && + PF_AEQ(&pffe->addr, &sk->addr[1], sk->af)) || + (pffe->dir == PF_IN && + PF_AEQ(&pffe->addr, &sk->addr[0], sk->af)))) { + s->timeout = PFTM_PURGE; + s->src.state = s->dst.state = TCPS_CLOSED; + killed++; } -#ifdef __FreeBSD__ - if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif - printf(", %u states killed", killed); } -#ifdef __FreeBSD__ - if (V_pf_status.debug >= PF_DEBUG_MISC) -#else - if (pf_status.debug >= PF_DEBUG_MISC) -#endif - printf("\n"); + PF_HASHROW_UNLOCK(ih); } + SLIST_FOREACH_SAFE(pffe, &queue, next, pffe1) + free(pffe, M_PFTEMP); + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("%s: %u states killed", __func__, killed); +} - /* kill this state */ - (*state)->timeout = PFTM_PURGE; - (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; - return (1); +/* + * Can return locked on failure, so that we can consistently + * allocate and insert a new one. + */ +struct pf_src_node * +pf_find_src_node(struct pf_addr *src, struct pf_rule *rule, sa_family_t af, + int returnlocked) +{ + struct pf_srchash *sh; + struct pf_src_node *n; + + V_pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; + + sh = &V_pf_srchash[pf_hashsrc(src, af)]; + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) + if (n->rule.ptr == rule && n->af == af && + ((af == AF_INET && n->addr.v4.s_addr == src->v4.s_addr) || + (af == AF_INET6 && bcmp(&n->addr, src, sizeof(*src)) == 0))) + break; + if (n != NULL || returnlocked == 0) + PF_HASHROW_UNLOCK(sh); + + return (n); } -int +static int pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, struct pf_addr *src, sa_family_t af) { - struct pf_src_node k; + + KASSERT((rule->rule_flag & PFRULE_RULESRCTRACK || + rule->rpool.opts & PF_POOL_STICKYADDR), + ("%s for non-tracking rule %p", __func__, rule)); + + if (*sn == NULL) + *sn = pf_find_src_node(src, rule, af, 1); if (*sn == NULL) { - k.af = af; - PF_ACPY(&k.addr, src, af); - if (rule->rule_flag & PFRULE_RULESRCTRACK || - rule->rpool.opts & PF_POOL_STICKYADDR) - k.rule.ptr = rule; - else - k.rule.ptr = NULL; -#ifdef __FreeBSD__ - V_pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; - *sn = RB_FIND(pf_src_tree, &V_tree_src_tracking, &k); -#else - pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; - *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); -#endif - } - if (*sn == NULL) { + struct pf_srchash *sh = &V_pf_srchash[pf_hashsrc(src, af)]; + + PF_HASHROW_ASSERT(sh); + if (!rule->max_src_nodes || rule->src_nodes < rule->max_src_nodes) -#ifdef __FreeBSD__ - (*sn) = pool_get(&V_pf_src_tree_pl, PR_NOWAIT | PR_ZERO); -#else - (*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT | PR_ZERO); -#endif + (*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO); else -#ifdef __FreeBSD__ V_pf_status.lcounters[LCNT_SRCNODES]++; -#else - pf_status.lcounters[LCNT_SRCNODES]++; -#endif - if ((*sn) == NULL) + if ((*sn) == NULL) { + PF_HASHROW_UNLOCK(sh); return (-1); + } pf_init_threshold(&(*sn)->conn_rate, rule->max_src_conn_rate.limit, rule->max_src_conn_rate.seconds); (*sn)->af = af; - if (rule->rule_flag & PFRULE_RULESRCTRACK || - rule->rpool.opts & PF_POOL_STICKYADDR) - (*sn)->rule.ptr = rule; - else - (*sn)->rule.ptr = NULL; + (*sn)->rule.ptr = rule; PF_ACPY(&(*sn)->addr, src, af); - if (RB_INSERT(pf_src_tree, -#ifdef __FreeBSD__ - &V_tree_src_tracking, *sn) != NULL) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - &tree_src_tracking, *sn) != NULL) { - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif - printf("pf: src_tree insert failed: "); - pf_print_host(&(*sn)->addr, 0, af); - printf("\n"); - } -#ifdef __FreeBSD__ - pool_put(&V_pf_src_tree_pl, *sn); -#else - pool_put(&pf_src_tree_pl, *sn); -#endif - return (-1); - } - (*sn)->creation = time_second; + LIST_INSERT_HEAD(&sh->nodes, *sn, entry); + (*sn)->creation = time_uptime; (*sn)->ruletype = rule->action; if ((*sn)->rule.ptr != NULL) (*sn)->rule.ptr->src_nodes++; -#ifdef __FreeBSD__ + PF_HASHROW_UNLOCK(sh); V_pf_status.scounters[SCNT_SRC_NODE_INSERT]++; V_pf_status.src_nodes++; -#else - pf_status.scounters[SCNT_SRC_NODE_INSERT]++; - pf_status.src_nodes++; -#endif } else { if (rule->max_src_states && (*sn)->states >= rule->max_src_states) { -#ifdef __FreeBSD__ V_pf_status.lcounters[LCNT_SRCSTATES]++; -#else - pf_status.lcounters[LCNT_SRCSTATES]++; -#endif return (-1); } } return (0); } -/* state table stuff */ +static void +pf_remove_src_node(struct pf_src_node *src) +{ + struct pf_srchash *sh; + + sh = &V_pf_srchash[pf_hashsrc(&src->addr, src->af)]; + PF_HASHROW_LOCK(sh); + LIST_REMOVE(src, entry); + PF_HASHROW_UNLOCK(sh); +} -static __inline int -pf_state_compare_key(struct pf_state_key *a, struct pf_state_key *b) +/* Data storage structures initialization. */ +void +pf_initialize() { - int diff; + struct pf_keyhash *kh; + struct pf_idhash *ih; + struct pf_srchash *sh; + u_int i; + + TUNABLE_ULONG_FETCH("net.pf.states_hashsize", &V_pf_hashsize); + if (V_pf_hashsize == 0 || !powerof2(V_pf_hashsize)) + V_pf_hashsize = PF_HASHSIZ; + TUNABLE_ULONG_FETCH("net.pf.source_nodes_hashsize", &V_pf_srchashsize); + if (V_pf_srchashsize == 0 || !powerof2(V_pf_srchashsize)) + V_pf_srchashsize = PF_HASHSIZ / 4; + + V_pf_hashseed = arc4random(); + + /* States and state keys storage. */ + V_pf_state_z = uma_zcreate("pf states", sizeof(struct pf_state), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + V_pf_limits[PF_LIMIT_STATES].zone = V_pf_state_z; + uma_zone_set_max(V_pf_state_z, PFSTATE_HIWAT); + + V_pf_state_key_z = uma_zcreate("pf state keys", + sizeof(struct pf_state_key), pf_state_key_ctor, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + V_pf_keyhash = malloc(V_pf_hashsize * sizeof(struct pf_keyhash), + M_PFHASH, M_WAITOK | M_ZERO); + V_pf_idhash = malloc(V_pf_hashsize * sizeof(struct pf_idhash), + M_PFHASH, M_WAITOK | M_ZERO); + V_pf_hashmask = V_pf_hashsize - 1; + for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask; + i++, kh++, ih++) { + mtx_init(&kh->lock, "pf_keyhash", NULL, MTX_DEF); + mtx_init(&ih->lock, "pf_idhash", NULL, MTX_DEF); + } + + /* Source nodes. */ + V_pf_sources_z = uma_zcreate("pf source nodes", + sizeof(struct pf_src_node), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, + 0); + V_pf_limits[PF_LIMIT_SRC_NODES].zone = V_pf_sources_z; + uma_zone_set_max(V_pf_sources_z, PFSNODE_HIWAT); + V_pf_srchash = malloc(V_pf_srchashsize * sizeof(struct pf_srchash), + M_PFHASH, M_WAITOK|M_ZERO); + V_pf_srchashmask = V_pf_srchashsize - 1; + for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) + mtx_init(&sh->lock, "pf_srchash", NULL, MTX_DEF); + + /* ALTQ */ + TAILQ_INIT(&V_pf_altqs[0]); + TAILQ_INIT(&V_pf_altqs[1]); + TAILQ_INIT(&V_pf_pabuf); + V_pf_altqs_active = &V_pf_altqs[0]; + V_pf_altqs_inactive = &V_pf_altqs[1]; + + /* Mbuf tags */ + V_pf_mtag_z = uma_zcreate("pf mtags", sizeof(struct m_tag) + + sizeof(struct pf_mtag), NULL, NULL, pf_mtag_init, NULL, + UMA_ALIGN_PTR, 0); + + /* Send & flush queues. */ + STAILQ_INIT(&V_pf_sendqueue); + SLIST_INIT(&V_pf_flushqueue); + TASK_INIT(&V_pf_flushtask, 0, pf_flush_task, &V_pf_flushqueue); + mtx_init(&pf_sendqueue_mtx, "pf send queue", NULL, MTX_DEF); + mtx_init(&pf_flushqueue_mtx, "pf flush queue", NULL, MTX_DEF); + + /* Unlinked, but may be referenced rules. */ + TAILQ_INIT(&V_pf_unlinked_rules); + mtx_init(&pf_unlnkdrules_mtx, "pf unlinked rules", NULL, MTX_DEF); +} - if ((diff = a->proto - b->proto) != 0) - return (diff); - if ((diff = a->af - b->af) != 0) - return (diff); - switch (a->af) { -#ifdef INET - case AF_INET: - if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) - return (1); - if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) - return (-1); - if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) - return (1); - if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) - return (-1); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (a->addr[0].addr32[3] > b->addr[0].addr32[3]) - return (1); - if (a->addr[0].addr32[3] < b->addr[0].addr32[3]) - return (-1); - if (a->addr[1].addr32[3] > b->addr[1].addr32[3]) - return (1); - if (a->addr[1].addr32[3] < b->addr[1].addr32[3]) - return (-1); - if (a->addr[0].addr32[2] > b->addr[0].addr32[2]) - return (1); - if (a->addr[0].addr32[2] < b->addr[0].addr32[2]) - return (-1); - if (a->addr[1].addr32[2] > b->addr[1].addr32[2]) - return (1); - if (a->addr[1].addr32[2] < b->addr[1].addr32[2]) - return (-1); - if (a->addr[0].addr32[1] > b->addr[0].addr32[1]) - return (1); - if (a->addr[0].addr32[1] < b->addr[0].addr32[1]) - return (-1); - if (a->addr[1].addr32[1] > b->addr[1].addr32[1]) - return (1); - if (a->addr[1].addr32[1] < b->addr[1].addr32[1]) - return (-1); - if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) - return (1); - if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) - return (-1); - if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) - return (1); - if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) - return (-1); - break; -#endif /* INET6 */ - } +void +pf_cleanup() +{ + struct pf_keyhash *kh; + struct pf_idhash *ih; + struct pf_srchash *sh; + struct pf_send_entry *pfse, *next; + u_int i; + + for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask; + i++, kh++, ih++) { + KASSERT(LIST_EMPTY(&kh->keys), ("%s: key hash not empty", + __func__)); + KASSERT(LIST_EMPTY(&ih->states), ("%s: id hash not empty", + __func__)); + mtx_destroy(&kh->lock); + mtx_destroy(&ih->lock); + } + free(V_pf_keyhash, M_PFHASH); + free(V_pf_idhash, M_PFHASH); + + for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) { + KASSERT(LIST_EMPTY(&sh->nodes), + ("%s: source node hash not empty", __func__)); + mtx_destroy(&sh->lock); + } + free(V_pf_srchash, M_PFHASH); + + STAILQ_FOREACH_SAFE(pfse, &V_pf_sendqueue, pfse_next, next) { + m_freem(pfse->pfse_m); + free(pfse, M_PFTEMP); + } + + mtx_destroy(&pf_sendqueue_mtx); + mtx_destroy(&pf_flushqueue_mtx); + mtx_destroy(&pf_unlnkdrules_mtx); + + uma_zdestroy(V_pf_mtag_z); + uma_zdestroy(V_pf_sources_z); + uma_zdestroy(V_pf_state_z); + uma_zdestroy(V_pf_state_key_z); +} - if ((diff = a->port[0] - b->port[0]) != 0) - return (diff); - if ((diff = a->port[1] - b->port[1]) != 0) - return (diff); +static int +pf_mtag_init(void *mem, int size, int how) +{ + struct m_tag *t; + + t = (struct m_tag *)mem; + t->m_tag_cookie = MTAG_ABI_COMPAT; + t->m_tag_id = PACKET_TAG_PF; + t->m_tag_len = sizeof(struct pf_mtag); + t->m_tag_free = pf_mtag_free; return (0); } -static __inline int -pf_state_compare_id(struct pf_state *a, struct pf_state *b) +static void +pf_mtag_free(struct m_tag *t) { - if (a->id > b->id) - return (1); - if (a->id < b->id) - return (-1); - if (a->creatorid > b->creatorid) - return (1); - if (a->creatorid < b->creatorid) - return (-1); - return (0); + uma_zfree(V_pf_mtag_z, t); } -int -pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx) +struct pf_mtag * +pf_get_mtag(struct mbuf *m) { - struct pf_state_item *si; - struct pf_state_key *cur; - struct pf_state *olds = NULL; + struct m_tag *mtag; -#ifdef __FreeBSD__ - KASSERT(s->key[idx] == NULL, ("%s: key is null!", __FUNCTION__)); -#else - KASSERT(s->key[idx] == NULL); /* XXX handle this? */ -#endif + if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) != NULL) + return ((struct pf_mtag *)(mtag + 1)); -#ifdef __FreeBSD__ - if ((cur = RB_INSERT(pf_state_tree, &V_pf_statetbl, sk)) != NULL) { -#else - if ((cur = RB_INSERT(pf_state_tree, &pf_statetbl, sk)) != NULL) { -#endif - /* key exists. check for same kif, if none, add to key */ - TAILQ_FOREACH(si, &cur->states, entry) - if (si->s->kif == s->kif && - si->s->direction == s->direction) { + mtag = uma_zalloc(V_pf_mtag_z, M_NOWAIT); + if (mtag == NULL) + return (NULL); + bzero(mtag + 1, sizeof(struct pf_mtag)); + m_tag_prepend(m, mtag); + + return ((struct pf_mtag *)(mtag + 1)); +} + +static int +pf_state_key_attach(struct pf_state_key *skw, struct pf_state_key *sks, + struct pf_state *s) +{ + struct pf_keyhash *kh; + struct pf_state_key *sk, *cur; + struct pf_state *si, *olds = NULL; + int idx; + + KASSERT(s->refs == 0, ("%s: state not pristine", __func__)); + KASSERT(s->key[PF_SK_WIRE] == NULL, ("%s: state has key", __func__)); + KASSERT(s->key[PF_SK_STACK] == NULL, ("%s: state has key", __func__)); + + /* + * First run: start with wire key. + */ + sk = skw; + idx = PF_SK_WIRE; + +keyattach: + kh = &V_pf_keyhash[pf_hashkey(sk)]; + + PF_HASHROW_LOCK(kh); + LIST_FOREACH(cur, &kh->keys, entry) + if (bcmp(cur, sk, sizeof(struct pf_state_key_cmp)) == 0) + break; + + if (cur != NULL) { + /* Key exists. Check for same kif, if none, add to key. */ + TAILQ_FOREACH(si, &cur->states[idx], key_list[idx]) { + struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(si)]; + + PF_HASHROW_LOCK(ih); + if (si->kif == s->kif && + si->direction == s->direction) { if (sk->proto == IPPROTO_TCP && - si->s->src.state >= TCPS_FIN_WAIT_2 && - si->s->dst.state >= TCPS_FIN_WAIT_2) { - si->s->src.state = si->s->dst.state = + si->src.state >= TCPS_FIN_WAIT_2 && + si->dst.state >= TCPS_FIN_WAIT_2) { + si->src.state = si->dst.state = TCPS_CLOSED; - /* unlink late or sks can go away */ - olds = si->s; + /* Unlink later or cur can go away. */ + pf_ref_state(si); + olds = si; } else { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: %s key attach " "failed on %s: ", (idx == PF_SK_WIRE) ? @@ -902,375 +873,294 @@ pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx) (idx == PF_SK_STACK) ? sk : NULL); printf(", existing: "); - pf_print_state_parts(si->s, + pf_print_state_parts(si, (idx == PF_SK_WIRE) ? sk : NULL, (idx == PF_SK_STACK) ? sk : NULL); printf("\n"); } -#ifdef __FreeBSD__ - pool_put(&V_pf_state_key_pl, sk); -#else - pool_put(&pf_state_key_pl, sk); -#endif + PF_HASHROW_UNLOCK(ih); + PF_HASHROW_UNLOCK(kh); + uma_zfree(V_pf_state_key_z, sk); + if (idx == PF_SK_STACK) + pf_detach_state(s); return (-1); /* collision! */ } } -#ifdef __FreeBSD__ - pool_put(&V_pf_state_key_pl, sk); -#else - pool_put(&pf_state_key_pl, sk); -#endif + PF_HASHROW_UNLOCK(ih); + } + uma_zfree(V_pf_state_key_z, sk); s->key[idx] = cur; - } else + } else { + LIST_INSERT_HEAD(&kh->keys, sk, entry); s->key[idx] = sk; - -#ifdef __FreeBSD__ - if ((si = pool_get(&V_pf_state_item_pl, PR_NOWAIT)) == NULL) { -#else - if ((si = pool_get(&pf_state_item_pl, PR_NOWAIT)) == NULL) { -#endif - pf_state_key_detach(s, idx); - return (-1); } - si->s = s; - /* list is sorted, if-bound states before floating */ -#ifdef __FreeBSD__ +stateattach: + /* List is sorted, if-bound states before floating. */ if (s->kif == V_pfi_all) -#else - if (s->kif == pfi_all) -#endif - TAILQ_INSERT_TAIL(&s->key[idx]->states, si, entry); + TAILQ_INSERT_TAIL(&s->key[idx]->states[idx], s, key_list[idx]); else - TAILQ_INSERT_HEAD(&s->key[idx]->states, si, entry); + TAILQ_INSERT_HEAD(&s->key[idx]->states[idx], s, key_list[idx]); - if (olds) - pf_unlink_state(olds); + /* + * Attach done. See how should we (or should not?) + * attach a second key. + */ + if (sks == skw) { + s->key[PF_SK_STACK] = s->key[PF_SK_WIRE]; + idx = PF_SK_STACK; + sks = NULL; + goto stateattach; + } else if (sks != NULL) { + PF_HASHROW_UNLOCK(kh); + if (olds) { + pf_unlink_state(olds, 0); + pf_release_state(olds); + olds = NULL; + } + /* + * Continue attaching with stack key. + */ + sk = sks; + idx = PF_SK_STACK; + sks = NULL; + goto keyattach; + } else + PF_HASHROW_UNLOCK(kh); + + if (olds) { + pf_unlink_state(olds, 0); + pf_release_state(olds); + } + + KASSERT(s->key[PF_SK_WIRE] != NULL && s->key[PF_SK_STACK] != NULL, + ("%s failure", __func__)); return (0); } -void +static void pf_detach_state(struct pf_state *s) { - if (s->key[PF_SK_WIRE] == s->key[PF_SK_STACK]) - s->key[PF_SK_WIRE] = NULL; - - if (s->key[PF_SK_STACK] != NULL) - pf_state_key_detach(s, PF_SK_STACK); + struct pf_state_key *sks = s->key[PF_SK_STACK]; + struct pf_keyhash *kh; + + if (sks != NULL) { + kh = &V_pf_keyhash[pf_hashkey(sks)]; + PF_HASHROW_LOCK(kh); + if (s->key[PF_SK_STACK] != NULL) + pf_state_key_detach(s, PF_SK_STACK); + /* + * If both point to same key, then we are done. + */ + if (sks == s->key[PF_SK_WIRE]) { + pf_state_key_detach(s, PF_SK_WIRE); + PF_HASHROW_UNLOCK(kh); + return; + } + PF_HASHROW_UNLOCK(kh); + } - if (s->key[PF_SK_WIRE] != NULL) - pf_state_key_detach(s, PF_SK_WIRE); + if (s->key[PF_SK_WIRE] != NULL) { + kh = &V_pf_keyhash[pf_hashkey(s->key[PF_SK_WIRE])]; + PF_HASHROW_LOCK(kh); + if (s->key[PF_SK_WIRE] != NULL) + pf_state_key_detach(s, PF_SK_WIRE); + PF_HASHROW_UNLOCK(kh); + } } -void +static void pf_state_key_detach(struct pf_state *s, int idx) { - struct pf_state_item *si; + struct pf_state_key *sk = s->key[idx]; +#ifdef INVARIANTS + struct pf_keyhash *kh = &V_pf_keyhash[pf_hashkey(sk)]; - si = TAILQ_FIRST(&s->key[idx]->states); - while (si && si->s != s) - si = TAILQ_NEXT(si, entry); - - if (si) { - TAILQ_REMOVE(&s->key[idx]->states, si, entry); -#ifdef __FreeBSD__ - pool_put(&V_pf_state_item_pl, si); -#else - pool_put(&pf_state_item_pl, si); + PF_HASHROW_ASSERT(kh); #endif - } + TAILQ_REMOVE(&sk->states[idx], s, key_list[idx]); + s->key[idx] = NULL; - if (TAILQ_EMPTY(&s->key[idx]->states)) { -#ifdef __FreeBSD__ - RB_REMOVE(pf_state_tree, &V_pf_statetbl, s->key[idx]); -#else - RB_REMOVE(pf_state_tree, &pf_statetbl, s->key[idx]); -#endif - if (s->key[idx]->reverse) - s->key[idx]->reverse->reverse = NULL; -#ifdef __FreeBSD__ - /* XXX: implement this */ -#else - if (s->key[idx]->inp) - s->key[idx]->inp->inp_pf_sk = NULL; -#endif -#ifdef __FreeBSD__ - pool_put(&V_pf_state_key_pl, s->key[idx]); -#else - pool_put(&pf_state_key_pl, s->key[idx]); -#endif + if (TAILQ_EMPTY(&sk->states[0]) && TAILQ_EMPTY(&sk->states[1])) { + LIST_REMOVE(sk, entry); + uma_zfree(V_pf_state_key_z, sk); } - s->key[idx] = NULL; +} + +static int +pf_state_key_ctor(void *mem, int size, void *arg, int flags) +{ + struct pf_state_key *sk = mem; + + bzero(sk, sizeof(struct pf_state_key_cmp)); + TAILQ_INIT(&sk->states[PF_SK_WIRE]); + TAILQ_INIT(&sk->states[PF_SK_STACK]); + + return (0); } struct pf_state_key * -pf_alloc_state_key(int pool_flags) +pf_state_key_setup(struct pf_pdesc *pd, struct pf_addr *saddr, + struct pf_addr *daddr, u_int16_t sport, u_int16_t dport) { - struct pf_state_key *sk; + struct pf_state_key *sk; -#ifdef __FreeBSD__ - if ((sk = pool_get(&V_pf_state_key_pl, pool_flags)) == NULL) -#else - if ((sk = pool_get(&pf_state_key_pl, pool_flags)) == NULL) -#endif + sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (sk == NULL) return (NULL); - TAILQ_INIT(&sk->states); + + PF_ACPY(&sk->addr[pd->sidx], saddr, pd->af); + PF_ACPY(&sk->addr[pd->didx], daddr, pd->af); + sk->port[pd->sidx] = sport; + sk->port[pd->didx] = dport; + sk->proto = pd->proto; + sk->af = pd->af; return (sk); } -int -pf_state_key_setup(struct pf_pdesc *pd, struct pf_rule *nr, - struct pf_state_key **skw, struct pf_state_key **sks, - struct pf_state_key **skp, struct pf_state_key **nkp, - struct pf_addr *saddr, struct pf_addr *daddr, - u_int16_t sport, u_int16_t dport) +struct pf_state_key * +pf_state_key_clone(struct pf_state_key *orig) { -#ifdef __FreeBSD__ - KASSERT((*skp == NULL && *nkp == NULL), - ("%s: skp == NULL && nkp == NULL", __FUNCTION__)); -#else - KASSERT((*skp == NULL && *nkp == NULL)); -#endif + struct pf_state_key *sk; - if ((*skp = pf_alloc_state_key(PR_NOWAIT | PR_ZERO)) == NULL) - return (ENOMEM); + sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (sk == NULL) + return (NULL); - PF_ACPY(&(*skp)->addr[pd->sidx], saddr, pd->af); - PF_ACPY(&(*skp)->addr[pd->didx], daddr, pd->af); - (*skp)->port[pd->sidx] = sport; - (*skp)->port[pd->didx] = dport; - (*skp)->proto = pd->proto; - (*skp)->af = pd->af; - - if (nr != NULL) { - if ((*nkp = pf_alloc_state_key(PR_NOWAIT | PR_ZERO)) == NULL) - return (ENOMEM); /* caller must handle cleanup */ - - /* XXX maybe just bcopy and TAILQ_INIT(&(*nkp)->states) */ - PF_ACPY(&(*nkp)->addr[0], &(*skp)->addr[0], pd->af); - PF_ACPY(&(*nkp)->addr[1], &(*skp)->addr[1], pd->af); - (*nkp)->port[0] = (*skp)->port[0]; - (*nkp)->port[1] = (*skp)->port[1]; - (*nkp)->proto = pd->proto; - (*nkp)->af = pd->af; - } else - *nkp = *skp; + bcopy(orig, sk, sizeof(struct pf_state_key_cmp)); - if (pd->dir == PF_IN) { - *skw = *skp; - *sks = *nkp; - } else { - *sks = *skp; - *skw = *nkp; - } - return (0); + return (sk); } - int pf_state_insert(struct pfi_kif *kif, struct pf_state_key *skw, struct pf_state_key *sks, struct pf_state *s) { -#ifndef __FreeBSD__ - splassert(IPL_SOFTNET); -#endif + struct pf_idhash *ih; + struct pf_state *cur; + + KASSERT(TAILQ_EMPTY(&sks->states[0]) && TAILQ_EMPTY(&sks->states[1]), + ("%s: sks not pristine", __func__)); + KASSERT(TAILQ_EMPTY(&skw->states[0]) && TAILQ_EMPTY(&skw->states[1]), + ("%s: skw not pristine", __func__)); + KASSERT(s->refs == 0, ("%s: state not pristine", __func__)); s->kif = kif; - if (skw == sks) { - if (pf_state_key_attach(skw, s, PF_SK_WIRE)) - return (-1); - s->key[PF_SK_STACK] = s->key[PF_SK_WIRE]; - } else { - if (pf_state_key_attach(skw, s, PF_SK_WIRE)) { -#ifdef __FreeBSD__ - pool_put(&V_pf_state_key_pl, sks); -#else - pool_put(&pf_state_key_pl, sks); -#endif - return (-1); - } - if (pf_state_key_attach(sks, s, PF_SK_STACK)) { - pf_state_key_detach(s, PF_SK_WIRE); - return (-1); - } - } + if (pf_state_key_attach(skw, sks, s)) + return (-1); if (s->id == 0 && s->creatorid == 0) { -#ifdef __FreeBSD__ - s->id = htobe64(V_pf_status.stateid++); + /* XXX: should be atomic, but probability of collision low */ + if ((s->id = V_pf_stateid[curcpu]++) == PFID_MAXID) + V_pf_stateid[curcpu] = 1; + s->id |= (uint64_t )curcpu << PFID_CPUSHIFT; + s->id = htobe64(s->id); s->creatorid = V_pf_status.hostid; -#else - s->id = htobe64(pf_status.stateid++); - s->creatorid = pf_status.hostid; -#endif } -#ifdef __FreeBSD__ - if (RB_INSERT(pf_state_tree_id, &V_tree_id, s) != NULL) { + + ih = &V_pf_idhash[PF_IDHASH(s)]; + PF_HASHROW_LOCK(ih); + LIST_FOREACH(cur, &ih->states, entry) + if (cur->id == s->id && cur->creatorid == s->creatorid) + break; + + if (cur != NULL) { + PF_HASHROW_UNLOCK(ih); if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (RB_INSERT(pf_state_tree_id, &tree_id, s) != NULL) { - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: state insert failed: " "id: %016llx creatorid: %08x", -#ifdef __FreeBSD__ - (unsigned long long)betoh64(s->id), ntohl(s->creatorid)); -#else - betoh64(s->id), ntohl(s->creatorid)); -#endif + (unsigned long long)be64toh(s->id), + ntohl(s->creatorid)); printf("\n"); } pf_detach_state(s); return (-1); } -#ifdef __FreeBSD__ - TAILQ_INSERT_TAIL(&V_state_list, s, entry_list); + LIST_INSERT_HEAD(&ih->states, s, entry); + /* One for keys, one for ID hash. */ + refcount_init(&s->refs, 2); + V_pf_status.fcounters[FCNT_STATE_INSERT]++; - V_pf_status.states++; -#else - TAILQ_INSERT_TAIL(&state_list, s, entry_list); - pf_status.fcounters[FCNT_STATE_INSERT]++; - pf_status.states++; -#endif - pfi_kif_ref(kif, PFI_KIF_REF_STATE); -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_insert_state_ptr != NULL) pfsync_insert_state_ptr(s); -#else - pfsync_insert_state(s); -#endif -#endif + + /* Returns locked. */ return (0); } +/* + * Find state by ID: returns with locked row on success. + */ struct pf_state * -pf_find_state_byid(struct pf_state_cmp *key) +pf_find_state_byid(uint64_t id, uint32_t creatorid) { -#ifdef __FreeBSD__ + struct pf_idhash *ih; + struct pf_state *s; + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; - return (RB_FIND(pf_state_tree_id, &V_tree_id, (struct pf_state *)key)); -#else - pf_status.fcounters[FCNT_STATE_SEARCH]++; + ih = &V_pf_idhash[(be64toh(id) % (V_pf_hashmask + 1))]; - return (RB_FIND(pf_state_tree_id, &tree_id, (struct pf_state *)key)); -#endif -} + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) + if (s->id == id && s->creatorid == creatorid) + break; -/* XXX debug function, intended to be removed one day */ -int -pf_compare_state_keys(struct pf_state_key *a, struct pf_state_key *b, - struct pfi_kif *kif, u_int dir) -{ - /* a (from hdr) and b (new) must be exact opposites of each other */ - if (a->af == b->af && a->proto == b->proto && - PF_AEQ(&a->addr[0], &b->addr[1], a->af) && - PF_AEQ(&a->addr[1], &b->addr[0], a->af) && - a->port[0] == b->port[1] && - a->port[1] == b->port[0]) - return (0); - else { - /* mismatch. must not happen. */ - printf("pf: state key linking mismatch! dir=%s, " - "if=%s, stored af=%u, a0: ", - dir == PF_OUT ? "OUT" : "IN", kif->pfik_name, a->af); - pf_print_host(&a->addr[0], a->port[0], a->af); - printf(", a1: "); - pf_print_host(&a->addr[1], a->port[1], a->af); - printf(", proto=%u", a->proto); - printf(", found af=%u, a0: ", b->af); - pf_print_host(&b->addr[0], b->port[0], b->af); - printf(", a1: "); - pf_print_host(&b->addr[1], b->port[1], b->af); - printf(", proto=%u", b->proto); - printf(".\n"); - return (-1); - } + if (s == NULL) + PF_HASHROW_UNLOCK(ih); + + return (s); } -struct pf_state * -#ifdef __FreeBSD__ -pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir, - struct mbuf *m, struct pf_mtag *pftag) -#else -pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir, - struct mbuf *m) -#endif +/* + * Find state by key. + * Returns with ID hash slot locked on success. + */ +static struct pf_state * +pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir) { + struct pf_keyhash *kh; struct pf_state_key *sk; - struct pf_state_item *si; + struct pf_state *s; + int idx; -#ifdef __FreeBSD__ V_pf_status.fcounters[FCNT_STATE_SEARCH]++; -#else - pf_status.fcounters[FCNT_STATE_SEARCH]++; -#endif -#ifdef __FreeBSD__ - if (dir == PF_OUT && pftag->statekey && - ((struct pf_state_key *)pftag->statekey)->reverse) - sk = ((struct pf_state_key *)pftag->statekey)->reverse; - else { -#ifdef __FreeBSD__ - if ((sk = RB_FIND(pf_state_tree, &V_pf_statetbl, -#else - if ((sk = RB_FIND(pf_state_tree, &pf_statetbl, -#endif - (struct pf_state_key *)key)) == NULL) - return (NULL); - if (dir == PF_OUT && pftag->statekey && - pf_compare_state_keys(pftag->statekey, sk, - kif, dir) == 0) { - ((struct pf_state_key *) - pftag->statekey)->reverse = sk; - sk->reverse = pftag->statekey; - } - } -#else - if (dir == PF_OUT && m->m_pkthdr.pf.statekey && - ((struct pf_state_key *)m->m_pkthdr.pf.statekey)->reverse) - sk = ((struct pf_state_key *)m->m_pkthdr.pf.statekey)->reverse; - else { -#ifdef __FreeBSD__ - if ((sk = RB_FIND(pf_state_tree, &V_pf_statetbl, -#else - if ((sk = RB_FIND(pf_state_tree, &pf_statetbl, -#endif - (struct pf_state_key *)key)) == NULL) - return (NULL); - if (dir == PF_OUT && m->m_pkthdr.pf.statekey && - pf_compare_state_keys(m->m_pkthdr.pf.statekey, sk, - kif, dir) == 0) { - ((struct pf_state_key *) - m->m_pkthdr.pf.statekey)->reverse = sk; - sk->reverse = m->m_pkthdr.pf.statekey; - } + kh = &V_pf_keyhash[pf_hashkey((struct pf_state_key *)key)]; + + PF_HASHROW_LOCK(kh); + LIST_FOREACH(sk, &kh->keys, entry) + if (bcmp(sk, key, sizeof(struct pf_state_key_cmp)) == 0) + break; + if (sk == NULL) { + PF_HASHROW_UNLOCK(kh); + return (NULL); } -#endif - if (dir == PF_OUT) -#ifdef __FreeBSD__ - pftag->statekey = NULL; -#else - m->m_pkthdr.pf.statekey = NULL; -#endif + idx = (dir == PF_IN ? PF_SK_WIRE : PF_SK_STACK); - /* list is sorted, if-bound states before floating ones */ - TAILQ_FOREACH(si, &sk->states, entry) -#ifdef __FreeBSD__ - if ((si->s->kif == V_pfi_all || si->s->kif == kif) && -#else - if ((si->s->kif == pfi_all || si->s->kif == kif) && -#endif - sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] : - si->s->key[PF_SK_STACK])) - return (si->s); + /* List is sorted, if-bound states before floating ones. */ + TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) + if (s->kif == V_pfi_all || s->kif == kif) { + PF_STATE_LOCK(s); + PF_HASHROW_UNLOCK(kh); + if (s->timeout == PFTM_UNLINKED) { + /* + * State is being processed + * by pf_unlink_state() in + * an other thread. + */ + PF_STATE_UNLOCK(s); + return (NULL); + } + return (s); + } + PF_HASHROW_UNLOCK(kh); return (NULL); } @@ -1278,115 +1168,178 @@ pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir, struct pf_state * pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more) { + struct pf_keyhash *kh; struct pf_state_key *sk; - struct pf_state_item *si, *ret = NULL; + struct pf_state *s, *ret = NULL; + int idx, inout = 0; -#ifdef __FreeBSD__ V_pf_status.fcounters[FCNT_STATE_SEARCH]++; -#else - pf_status.fcounters[FCNT_STATE_SEARCH]++; -#endif -#ifdef __FreeBSD__ - sk = RB_FIND(pf_state_tree, &V_pf_statetbl, (struct pf_state_key *)key); -#else - sk = RB_FIND(pf_state_tree, &pf_statetbl, (struct pf_state_key *)key); -#endif - if (sk != NULL) { - TAILQ_FOREACH(si, &sk->states, entry) - if (dir == PF_INOUT || - (sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] : - si->s->key[PF_SK_STACK]))) { - if (more == NULL) - return (si->s); - - if (ret) - (*more)++; - else - ret = si; - } + kh = &V_pf_keyhash[pf_hashkey((struct pf_state_key *)key)]; + + PF_HASHROW_LOCK(kh); + LIST_FOREACH(sk, &kh->keys, entry) + if (bcmp(sk, key, sizeof(struct pf_state_key_cmp)) == 0) + break; + if (sk == NULL) { + PF_HASHROW_UNLOCK(kh); + return (NULL); + } + switch (dir) { + case PF_IN: + idx = PF_SK_WIRE; + break; + case PF_OUT: + idx = PF_SK_STACK; + break; + case PF_INOUT: + idx = PF_SK_WIRE; + inout = 1; + break; + default: + panic("%s: dir %u", __func__, dir); + } +second_run: + TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) { + if (more == NULL) { + PF_HASHROW_UNLOCK(kh); + return (s); + } + + if (ret) + (*more)++; + else + ret = s; + } + if (inout == 1) { + inout = 0; + idx = PF_SK_STACK; + goto second_run; } - return (ret ? ret->s : NULL); + PF_HASHROW_UNLOCK(kh); + + return (ret); } /* END state table stuff */ +static void +pf_send(struct pf_send_entry *pfse) +{ + + PF_SENDQ_LOCK(); + STAILQ_INSERT_TAIL(&V_pf_sendqueue, pfse, pfse_next); + PF_SENDQ_UNLOCK(); + swi_sched(V_pf_swi_cookie, 0); +} + +void +pf_intr(void *v) +{ + struct pf_send_head queue; + struct pf_send_entry *pfse, *next; + + CURVNET_SET((struct vnet *)v); + + PF_SENDQ_LOCK(); + queue = V_pf_sendqueue; + STAILQ_INIT(&V_pf_sendqueue); + PF_SENDQ_UNLOCK(); + + STAILQ_FOREACH_SAFE(pfse, &queue, pfse_next, next) { + switch (pfse->pfse_type) { +#ifdef INET + case PFSE_IP: + ip_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL); + break; + case PFSE_ICMP: + icmp_error(pfse->pfse_m, pfse->pfse_icmp_type, + pfse->pfse_icmp_code, 0, pfse->pfse_icmp_mtu); + break; +#endif /* INET */ +#ifdef INET6 + case PFSE_IP6: + ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL, + NULL); + break; + case PFSE_ICMP6: + icmp6_error(pfse->pfse_m, pfse->pfse_icmp_type, + pfse->pfse_icmp_code, pfse->pfse_icmp_mtu); + break; +#endif /* INET6 */ + default: + panic("%s: unknown type", __func__); + } + free(pfse, M_PFTEMP); + } + CURVNET_RESTORE(); +} void pf_purge_thread(void *v) { - int nloops = 0, s; -#ifdef __FreeBSD__ - int locked; -#endif + int fullrun; CURVNET_SET((struct vnet *)v); for (;;) { - tsleep(pf_purge_thread, PWAIT, "pftm", 1 * hz); - -#ifdef __FreeBSD__ - sx_slock(&V_pf_consistency_lock); - PF_LOCK(); - locked = 0; + PF_RULES_RLOCK(); + rw_sleep(pf_purge_thread, &pf_rules_lock, 0, "pftm", hz / 10); if (V_pf_end_threads) { - PF_UNLOCK(); - sx_sunlock(&V_pf_consistency_lock); - sx_xlock(&V_pf_consistency_lock); - PF_LOCK(); + /* + * To cleanse up all kifs and rules we need + * two runs: first one clears reference flags, + * then pf_purge_expired_states() doesn't + * raise them, and then second run frees. + */ + PF_RULES_RUNLOCK(); + pf_purge_unlinked_rules(); + pfi_kif_purge(); - pf_purge_expired_states(V_pf_status.states, 1); + /* + * Now purge everything. + */ + pf_purge_expired_states(V_pf_hashmask + 1); pf_purge_expired_fragments(); - pf_purge_expired_src_nodes(1); - V_pf_end_threads++; + pf_purge_expired_src_nodes(); + + /* + * Now all kifs & rules should be unreferenced, + * thus should be successfully freed. + */ + pf_purge_unlinked_rules(); + pfi_kif_purge(); - sx_xunlock(&V_pf_consistency_lock); - PF_UNLOCK(); + /* + * Announce success and exit. + */ + PF_RULES_RLOCK(); + V_pf_end_threads++; + PF_RULES_RUNLOCK(); wakeup(pf_purge_thread); kproc_exit(0); } -#endif - s = splsoftnet(); - - /* process a fraction of the state table every second */ -#ifdef __FreeBSD__ - if (!pf_purge_expired_states(1 + (V_pf_status.states / - V_pf_default_rule.timeout[PFTM_INTERVAL]), 0)) { - PF_UNLOCK(); - sx_sunlock(&V_pf_consistency_lock); - sx_xlock(&V_pf_consistency_lock); - PF_LOCK(); - locked = 1; - - pf_purge_expired_states(1 + (V_pf_status.states / - V_pf_default_rule.timeout[PFTM_INTERVAL]), 1); - } -#else - pf_purge_expired_states(1 + (pf_status.states - / pf_default_rule.timeout[PFTM_INTERVAL])); -#endif + PF_RULES_RUNLOCK(); - /* purge other expired types every PFTM_INTERVAL seconds */ -#ifdef __FreeBSD__ - if (++nloops >= V_pf_default_rule.timeout[PFTM_INTERVAL]) { -#else - if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) { -#endif + /* Process 1/interval fraction of the state table every run. */ + fullrun = pf_purge_expired_states(V_pf_hashmask / + (V_pf_default_rule.timeout[PFTM_INTERVAL] * 10)); + + /* Purge other expired types every PFTM_INTERVAL seconds. */ + if (fullrun) { + /* + * Order is important: + * - states and src nodes reference rules + * - states and rules reference kifs + */ pf_purge_expired_fragments(); - pf_purge_expired_src_nodes(0); - nloops = 0; + pf_purge_expired_src_nodes(); + pf_purge_unlinked_rules(); + pfi_kif_purge(); } - - splx(s); -#ifdef __FreeBSD__ - PF_UNLOCK(); - if (locked) - sx_xunlock(&V_pf_consistency_lock); - else - sx_sunlock(&V_pf_consistency_lock); -#endif } + /* not reached */ CURVNET_RESTORE(); } @@ -1400,117 +1353,59 @@ pf_state_expires(const struct pf_state *state) /* handle all PFTM_* > PFTM_MAX here */ if (state->timeout == PFTM_PURGE) - return (time_second); + return (time_uptime); if (state->timeout == PFTM_UNTIL_PACKET) return (0); -#ifdef __FreeBSD__ KASSERT(state->timeout != PFTM_UNLINKED, ("pf_state_expires: timeout == PFTM_UNLINKED")); - KASSERT((state->timeout < PFTM_MAX), + KASSERT((state->timeout < PFTM_MAX), ("pf_state_expires: timeout > PFTM_MAX")); -#else - KASSERT(state->timeout != PFTM_UNLINKED); - KASSERT(state->timeout < PFTM_MAX); -#endif timeout = state->rule.ptr->timeout[state->timeout]; if (!timeout) -#ifdef __FreeBSD__ timeout = V_pf_default_rule.timeout[state->timeout]; -#else - timeout = pf_default_rule.timeout[state->timeout]; -#endif start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; if (start) { end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; - states = state->rule.ptr->states_cur; + states = state->rule.ptr->states_cur; /* XXXGL */ } else { -#ifdef __FreeBSD__ start = V_pf_default_rule.timeout[PFTM_ADAPTIVE_START]; end = V_pf_default_rule.timeout[PFTM_ADAPTIVE_END]; states = V_pf_status.states; -#else - start = pf_default_rule.timeout[PFTM_ADAPTIVE_START]; - end = pf_default_rule.timeout[PFTM_ADAPTIVE_END]; - states = pf_status.states; -#endif } if (end && states > start && start < end) { if (states < end) return (state->expire + timeout * (end - states) / (end - start)); else - return (time_second); + return (time_uptime); } return (state->expire + timeout); } -#ifdef __FreeBSD__ -int -pf_purge_expired_src_nodes(int waslocked) -#else void -pf_purge_expired_src_nodes(int waslocked) -#endif +pf_purge_expired_src_nodes() { - struct pf_src_node *cur, *next; - int locked = waslocked; - -#ifdef __FreeBSD__ - for (cur = RB_MIN(pf_src_tree, &V_tree_src_tracking); cur; cur = next) { - next = RB_NEXT(pf_src_tree, &V_tree_src_tracking, cur); -#else - for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { - next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); -#endif + struct pf_srchash *sh; + struct pf_src_node *cur, *next; + int i; - if (cur->states <= 0 && cur->expire <= time_second) { - if (! locked) { -#ifdef __FreeBSD__ - if (!sx_try_upgrade(&V_pf_consistency_lock)) - return (0); -#else - rw_enter_write(&pf_consistency_lock); -#endif - next = RB_NEXT(pf_src_tree, -#ifdef __FreeBSD__ - &V_tree_src_tracking, cur); -#else - &tree_src_tracking, cur); -#endif - locked = 1; - } - if (cur->rule.ptr != NULL) { + for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH_SAFE(cur, &sh->nodes, entry, next) + if (cur->states <= 0 && cur->expire <= time_uptime) { + if (cur->rule.ptr != NULL) cur->rule.ptr->src_nodes--; - if (cur->rule.ptr->states_cur <= 0 && - cur->rule.ptr->max_src_nodes <= 0) - pf_rm_rule(NULL, cur->rule.ptr); - } -#ifdef __FreeBSD__ - RB_REMOVE(pf_src_tree, &V_tree_src_tracking, cur); + LIST_REMOVE(cur, entry); V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; V_pf_status.src_nodes--; - pool_put(&V_pf_src_tree_pl, cur); -#else - RB_REMOVE(pf_src_tree, &tree_src_tracking, cur); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, cur); -#endif - } + uma_zfree(V_pf_sources_z, cur); + } else if (cur->rule.ptr != NULL) + cur->rule.ptr->rule_flag |= PFRULE_REFS; + PF_HASHROW_UNLOCK(sh); } - - if (locked && !waslocked) -#ifdef __FreeBSD__ - { - sx_downgrade(&V_pf_consistency_lock); - } - return (1); -#else - rw_exit_write(&pf_consistency_lock); -#endif } -void +static void pf_src_tree_remove_state(struct pf_state *s) { u_int32_t timeout; @@ -1522,12 +1417,8 @@ pf_src_tree_remove_state(struct pf_state *s) timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = -#ifdef __FreeBSD__ V_pf_default_rule.timeout[PFTM_SRC_NODE]; -#else - pf_default_rule.timeout[PFTM_SRC_NODE]; -#endif - s->src_node->expire = time_second + timeout; + s->src_node->expire = time_uptime + timeout; } } if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { @@ -1535,226 +1426,167 @@ pf_src_tree_remove_state(struct pf_state *s) timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = -#ifdef __FreeBSD__ V_pf_default_rule.timeout[PFTM_SRC_NODE]; -#else - pf_default_rule.timeout[PFTM_SRC_NODE]; -#endif - s->nat_src_node->expire = time_second + timeout; + s->nat_src_node->expire = time_uptime + timeout; } } s->src_node = s->nat_src_node = NULL; } -/* callers should be at splsoftnet */ -void -pf_unlink_state(struct pf_state *cur) +/* + * Unlink and potentilly free a state. Function may be + * called with ID hash row locked, but always returns + * unlocked, since it needs to go through key hash locking. + */ +int +pf_unlink_state(struct pf_state *s, u_int flags) { -#ifdef __FreeBSD__ - if (cur->local_flags & PFSTATE_EXPIRING) - return; - cur->local_flags |= PFSTATE_EXPIRING; -#else - splassert(IPL_SOFTNET); -#endif + struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(s)]; + + if ((flags & PF_ENTER_LOCKED) == 0) + PF_HASHROW_LOCK(ih); + else + PF_HASHROW_ASSERT(ih); - if (cur->src.state == PF_TCPS_PROXY_DST) { + if (s->timeout == PFTM_UNLINKED) { + /* + * State is being processed + * by pf_unlink_state() in + * an other thread. + */ + PF_HASHROW_UNLOCK(ih); + return (0); /* XXXGL: undefined actually */ + } + + s->timeout = PFTM_UNLINKED; + + if (s->src.state == PF_TCPS_PROXY_DST) { /* XXX wire key the right one? */ -#ifdef __FreeBSD__ - pf_send_tcp(NULL, cur->rule.ptr, cur->key[PF_SK_WIRE]->af, -#else - pf_send_tcp(cur->rule.ptr, cur->key[PF_SK_WIRE]->af, -#endif - &cur->key[PF_SK_WIRE]->addr[1], - &cur->key[PF_SK_WIRE]->addr[0], - cur->key[PF_SK_WIRE]->port[1], - cur->key[PF_SK_WIRE]->port[0], - cur->src.seqhi, cur->src.seqlo + 1, - TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, NULL, NULL); - } -#ifdef __FreeBSD__ - RB_REMOVE(pf_state_tree_id, &V_tree_id, cur); -#else - RB_REMOVE(pf_state_tree_id, &tree_id, cur); -#endif -#if NPFLOW > 0 - if (cur->state_flags & PFSTATE_PFLOW) -#ifdef __FreeBSD__ - if (export_pflow_ptr != NULL) - export_pflow_ptr(cur); -#else - export_pflow(cur); -#endif -#endif -#if NPFSYNC > 0 -#ifdef __FreeBSD__ + pf_send_tcp(NULL, s->rule.ptr, s->key[PF_SK_WIRE]->af, + &s->key[PF_SK_WIRE]->addr[1], + &s->key[PF_SK_WIRE]->addr[0], + s->key[PF_SK_WIRE]->port[1], + s->key[PF_SK_WIRE]->port[0], + s->src.seqhi, s->src.seqlo + 1, + TH_RST|TH_ACK, 0, 0, 0, 1, s->tag, NULL); + } + + LIST_REMOVE(s, entry); + pf_src_tree_remove_state(s); + PF_HASHROW_UNLOCK(ih); + if (pfsync_delete_state_ptr != NULL) - pfsync_delete_state_ptr(cur); -#else - pfsync_delete_state(cur); -#endif -#endif - cur->timeout = PFTM_UNLINKED; - pf_src_tree_remove_state(cur); - pf_detach_state(cur); + pfsync_delete_state_ptr(s); + + pf_detach_state(s); + refcount_release(&s->refs); + + return (pf_release_state(s)); } -/* callers should be at splsoftnet and hold the - * write_lock on pf_consistency_lock */ void pf_free_state(struct pf_state *cur) { -#ifndef __FreeBSD__ - splassert(IPL_SOFTNET); -#endif -#if NPFSYNC > 0 -#ifdef __FreeBSD__ - if (pfsync_state_in_use_ptr != NULL && - pfsync_state_in_use_ptr(cur)) -#else - if (pfsync_state_in_use(cur)) -#endif - return; -#endif -#ifdef __FreeBSD__ - KASSERT(cur->timeout == PFTM_UNLINKED, - ("pf_free_state: cur->timeout != PFTM_UNLINKED")); -#else - KASSERT(cur->timeout == PFTM_UNLINKED); -#endif - if (--cur->rule.ptr->states_cur <= 0 && - cur->rule.ptr->src_nodes <= 0) - pf_rm_rule(NULL, cur->rule.ptr); + KASSERT(cur->refs == 0, ("%s: %p has refs", __func__, cur)); + KASSERT(cur->timeout == PFTM_UNLINKED, ("%s: timeout %u", __func__, + cur->timeout)); + --cur->rule.ptr->states_cur; if (cur->nat_rule.ptr != NULL) - if (--cur->nat_rule.ptr->states_cur <= 0 && - cur->nat_rule.ptr->src_nodes <= 0) - pf_rm_rule(NULL, cur->nat_rule.ptr); + --cur->nat_rule.ptr->states_cur; if (cur->anchor.ptr != NULL) - if (--cur->anchor.ptr->states_cur <= 0) - pf_rm_rule(NULL, cur->anchor.ptr); + --cur->anchor.ptr->states_cur; pf_normalize_tcp_cleanup(cur); - pfi_kif_unref(cur->kif, PFI_KIF_REF_STATE); -#ifdef __FreeBSD__ - TAILQ_REMOVE(&V_state_list, cur, entry_list); -#else - TAILQ_REMOVE(&state_list, cur, entry_list); -#endif - if (cur->tag) - pf_tag_unref(cur->tag); -#ifdef __FreeBSD__ - pool_put(&V_pf_state_pl, cur); + uma_zfree(V_pf_state_z, cur); V_pf_status.fcounters[FCNT_STATE_REMOVALS]++; - V_pf_status.states--; -#else - pool_put(&pf_state_pl, cur); - pf_status.fcounters[FCNT_STATE_REMOVALS]++; - pf_status.states--; -#endif } -#ifdef __FreeBSD__ -int -pf_purge_expired_states(u_int32_t maxcheck, int waslocked) -#else -void -pf_purge_expired_states(u_int32_t maxcheck) -#endif +/* + * Called only from pf_purge_thread(), thus serialized. + */ +static int +pf_purge_expired_states(int maxcheck) { - static struct pf_state *cur = NULL; - struct pf_state *next; -#ifdef __FreeBSD__ - int locked = waslocked; -#else - int locked = 0; -#endif + static u_int i = 0; - while (maxcheck--) { - /* wrap to start of list when we hit the end */ - if (cur == NULL) { -#ifdef __FreeBSD__ - cur = TAILQ_FIRST(&V_state_list); -#else - cur = TAILQ_FIRST(&state_list); -#endif - if (cur == NULL) - break; /* list empty */ - } + struct pf_idhash *ih; + struct pf_state *s; + int rv = 0; - /* get next state, as cur may get deleted */ - next = TAILQ_NEXT(cur, entry_list); + V_pf_status.states = uma_zone_get_cur(V_pf_state_z); - if (cur->timeout == PFTM_UNLINKED) { - /* free unlinked state */ - if (! locked) { -#ifdef __FreeBSD__ - if (!sx_try_upgrade(&V_pf_consistency_lock)) - return (0); -#else - rw_enter_write(&pf_consistency_lock); -#endif - locked = 1; - } - pf_free_state(cur); - } else if (pf_state_expires(cur) <= time_second) { - /* unlink and free expired state */ - pf_unlink_state(cur); - if (! locked) { -#ifdef __FreeBSD__ - if (!sx_try_upgrade(&V_pf_consistency_lock)) - return (0); -#else - rw_enter_write(&pf_consistency_lock); -#endif - locked = 1; + /* + * Go through hash and unlink states that expire now. + */ + while (maxcheck > 0) { + + /* Wrap to start of hash when we hit the end. */ + if (i > V_pf_hashmask) { + i = 0; + rv = 1; + } + + ih = &V_pf_idhash[i]; +relock: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (pf_state_expires(s) <= time_uptime) { + V_pf_status.states -= + pf_unlink_state(s, PF_ENTER_LOCKED); + goto relock; } - pf_free_state(cur); + s->rule.ptr->rule_flag |= PFRULE_REFS; + if (s->nat_rule.ptr != NULL) + s->nat_rule.ptr->rule_flag |= PFRULE_REFS; + if (s->anchor.ptr != NULL) + s->anchor.ptr->rule_flag |= PFRULE_REFS; + s->kif->pfik_flags |= PFI_IFLAG_REFS; + if (s->rt_kif) + s->rt_kif->pfik_flags |= PFI_IFLAG_REFS; } - cur = next; + PF_HASHROW_UNLOCK(ih); + i++; + maxcheck--; } -#ifdef __FreeBSD__ - if (!waslocked && locked) - sx_downgrade(&V_pf_consistency_lock); - - return (1); -#else - if (locked) - rw_exit_write(&pf_consistency_lock); -#endif -} + V_pf_status.states = uma_zone_get_cur(V_pf_state_z); -int -pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw) -{ - if (aw->type != PF_ADDR_TABLE) - return (0); - if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, 1)) == NULL) - return (1); - return (0); + return (rv); } -void -pf_tbladdr_remove(struct pf_addr_wrap *aw) +static void +pf_purge_unlinked_rules() { - if (aw->type != PF_ADDR_TABLE || aw->p.tbl == NULL) - return; - pfr_detach_table(aw->p.tbl); - aw->p.tbl = NULL; -} + struct pf_rulequeue tmpq; + struct pf_rule *r, *r1; -void -pf_tbladdr_copyout(struct pf_addr_wrap *aw) -{ - struct pfr_ktable *kt = aw->p.tbl; + /* + * Do naive mark-and-sweep garbage collecting of old rules. + * Reference flag is raised by pf_purge_expired_states() + * and pf_purge_expired_src_nodes(). + * + * To avoid LOR between PF_UNLNKDRULES_LOCK/PF_RULES_WLOCK, + * use a temporary queue. + */ + TAILQ_INIT(&tmpq); + PF_UNLNKDRULES_LOCK(); + TAILQ_FOREACH_SAFE(r, &V_pf_unlinked_rules, entries, r1) { + if (!(r->rule_flag & PFRULE_REFS)) { + TAILQ_REMOVE(&V_pf_unlinked_rules, r, entries); + TAILQ_INSERT_TAIL(&tmpq, r, entries); + } else + r->rule_flag &= ~PFRULE_REFS; + } + PF_UNLNKDRULES_UNLOCK(); - if (aw->type != PF_ADDR_TABLE || kt == NULL) - return; - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) - kt = kt->pfrkt_root; - aw->p.tbl = NULL; - aw->p.tblcnt = (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) ? - kt->pfrkt_cnt : -1; + if (!TAILQ_EMPTY(&tmpq)) { + PF_RULES_WLOCK(); + TAILQ_FOREACH_SAFE(r, &tmpq, entries, r1) { + TAILQ_REMOVE(&tmpq, r, entries); + pf_free_rule(r); + } + PF_RULES_WUNLOCK(); + } } void @@ -1826,7 +1658,7 @@ pf_print_state(struct pf_state *s) pf_print_state_parts(s, NULL, NULL); } -void +static void pf_print_state_parts(struct pf_state *s, struct pf_state_key *skwp, struct pf_state_key *sksp) { @@ -1979,7 +1811,7 @@ pf_calc_skip_steps(struct pf_rulequeue *rules) PF_SET_SKIP_STEPS(i); } -int +static int pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) { if (aw1->type != aw2->type) @@ -1999,8 +1831,6 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) return (0); case PF_ADDR_TABLE: return (aw1->p.tbl != aw2->p.tbl); - case PF_ADDR_RTLABEL: - return (aw1->v.rtlabel != aw2->v.rtlabel); default: printf("invalid address type: %d\n", aw1->type); return (1); @@ -2022,7 +1852,7 @@ pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) return (l); } -void +static void pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af) { @@ -2080,7 +1910,7 @@ pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) } #ifdef INET6 -void +static void pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) { struct pf_addr ao; @@ -2102,7 +1932,7 @@ pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) } #endif /* INET6 */ -void +static void pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, struct pf_addr *na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c, u_int16_t *ic, u_int16_t *hc, u_int8_t u, sa_family_t af) @@ -2195,16 +2025,12 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, * Need to modulate the sequence numbers in the TCP SACK option * (credits to Krzysztof Pfaff for report and patch) */ -int +static int pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, struct tcphdr *th, struct pf_state_peer *dst) { int hlen = (th->th_off << 2) - sizeof(*th), thoptlen = hlen; -#ifdef __FreeBSD__ u_int8_t opts[TCP_MAXOLEN], *opt = opts; -#else - u_int8_t opts[MAX_TCPOPTLEN], *opt = opts; -#endif int copyback = 0, i, olen; struct sackblk sack; @@ -2248,60 +2074,32 @@ pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, } if (copyback) -#ifdef __FreeBSD__ m_copyback(m, off + sizeof(*th), thoptlen, (caddr_t)opts); -#else - m_copyback(m, off + sizeof(*th), thoptlen, opts); -#endif return (copyback); } -void -#ifdef __FreeBSD__ +static void pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af, -#else -pf_send_tcp(const struct pf_rule *r, sa_family_t af, -#endif const struct pf_addr *saddr, const struct pf_addr *daddr, u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag, - u_int16_t rtag, struct ether_header *eh, struct ifnet *ifp) + u_int16_t rtag, struct ifnet *ifp) { + struct pf_send_entry *pfse; struct mbuf *m; int len, tlen; #ifdef INET - struct ip *h; + struct ip *h = NULL; #endif /* INET */ #ifdef INET6 - struct ip6_hdr *h6; + struct ip6_hdr *h6 = NULL; #endif /* INET6 */ struct tcphdr *th; char *opt; -#ifdef __FreeBSD__ struct pf_mtag *pf_mtag; - KASSERT( -#ifdef INET - af == AF_INET -#else - 0 -#endif - || -#ifdef INET6 - af == AF_INET6 -#else - 0 -#endif - , ("Unsupported AF %d", af)); len = 0; th = NULL; -#ifdef INET - h = NULL; -#endif -#ifdef INET6 - h6 = NULL; -#endif -#endif /* __FreeBSD__ */ /* maximum segment size tcp option */ tlen = sizeof(struct tcphdr); @@ -2319,54 +2117,40 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, len = sizeof(struct ip6_hdr) + tlen; break; #endif /* INET6 */ + default: + panic("%s: unsupported af %d", __func__, af); } - /* create outgoing mbuf */ - m = m_gethdr(M_DONTWAIT, MT_HEADER); - if (m == NULL) + /* Allocate outgoing queue entry, mbuf and mbuf tag. */ + pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); + if (pfse == NULL) return; -#ifdef __FreeBSD__ + m = m_gethdr(M_NOWAIT, MT_HEADER); + if (m == NULL) { + free(pfse, M_PFTEMP); + return; + } #ifdef MAC mac_netinet_firewall_send(m); #endif if ((pf_mtag = pf_get_mtag(m)) == NULL) { + free(pfse, M_PFTEMP); m_freem(m); return; } -#endif if (tag) -#ifdef __FreeBSD__ m->m_flags |= M_SKIP_FIREWALL; pf_mtag->tag = rtag; -#else - m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; - m->m_pkthdr.pf.tag = rtag; -#endif if (r != NULL && r->rtableid >= 0) -#ifdef __FreeBSD__ - { M_SETFIB(m, r->rtableid); - pf_mtag->rtableid = r->rtableid; -#else - m->m_pkthdr.pf.rtableid = r->rtableid; -#endif -#ifdef __FreeBSD__ - } -#endif #ifdef ALTQ if (r != NULL && r->qid) { -#ifdef __FreeBSD__ pf_mtag->qid = r->qid; /* add hints for ecn */ pf_mtag->hdr = mtod(m, struct ip *); -#else - m->m_pkthdr.pf.qid = r->qid; - /* add hints for ecn */ - m->m_pkthdr.pf.hdr = mtod(m, struct ip *); -#endif } #endif /* ALTQ */ m->m_data += max_linkhdr; @@ -2429,53 +2213,12 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, h->ip_v = 4; h->ip_hl = sizeof(*h) >> 2; h->ip_tos = IPTOS_LOWDELAY; -#ifdef __FreeBSD__ h->ip_off = V_path_mtu_discovery ? IP_DF : 0; h->ip_len = len; h->ip_ttl = ttl ? ttl : V_ip_defttl; -#else - h->ip_len = htons(len); - h->ip_off = htons(ip_mtudisc ? IP_DF : 0); - h->ip_ttl = ttl ? ttl : ip_defttl; -#endif h->ip_sum = 0; - if (eh == NULL) { -#ifdef __FreeBSD__ - PF_UNLOCK(); - ip_output(m, (void *)NULL, (void *)NULL, 0, - (void *)NULL, (void *)NULL); - PF_LOCK(); -#else /* ! __FreeBSD__ */ - ip_output(m, (void *)NULL, (void *)NULL, 0, - (void *)NULL, (void *)NULL); -#endif - } else { - struct route ro; - struct rtentry rt; - struct ether_header *e = (void *)ro.ro_dst.sa_data; - if (ifp == NULL) { - m_freem(m); - return; - } - rt.rt_ifp = ifp; - ro.ro_rt = &rt; - ro.ro_dst.sa_len = sizeof(ro.ro_dst); - ro.ro_dst.sa_family = pseudo_AF_HDRCMPLT; - bcopy(eh->ether_dhost, e->ether_shost, ETHER_ADDR_LEN); - bcopy(eh->ether_shost, e->ether_dhost, ETHER_ADDR_LEN); - e->ether_type = eh->ether_type; -#ifdef __FreeBSD__ - PF_UNLOCK(); - /* XXX_IMPORT: later */ - ip_output(m, (void *)NULL, &ro, 0, - (void *)NULL, (void *)NULL); - PF_LOCK(); -#else /* ! __FreeBSD__ */ - ip_output(m, (void *)NULL, &ro, IP_ROUTETOETHER, - (void *)NULL, (void *)NULL); -#endif - } + pfse->pfse_type = PFSE_IP; break; #endif /* INET */ #ifdef INET6 @@ -2487,102 +2230,75 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, h6->ip6_vfc |= IPV6_VERSION; h6->ip6_hlim = IPV6_DEFHLIM; -#ifdef __FreeBSD__ - PF_UNLOCK(); - ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); - PF_LOCK(); -#else - ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); -#endif + pfse->pfse_type = PFSE_IP6; break; #endif /* INET6 */ } + pfse->pfse_m = m; + pf_send(pfse); } static void pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, struct pf_rule *r) { - struct mbuf *m0; -#ifdef __FreeBSD__ -#ifdef INET - struct ip *ip; -#endif + struct pf_send_entry *pfse; + struct mbuf *m0; struct pf_mtag *pf_mtag; -#endif -#ifdef __FreeBSD__ - m0 = m_copypacket(m, M_DONTWAIT); - if (m0 == NULL) + /* Allocate outgoing queue entry, mbuf and mbuf tag. */ + pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); + if (pfse == NULL) return; -#else - if ((m0 = m_copy(m, 0, M_COPYALL)) == NULL) + + if ((m0 = m_copypacket(m, M_NOWAIT)) == NULL) { + free(pfse, M_PFTEMP); return; -#endif + } -#ifdef __FreeBSD__ - if ((pf_mtag = pf_get_mtag(m0)) == NULL) + if ((pf_mtag = pf_get_mtag(m0)) == NULL) { + free(pfse, M_PFTEMP); return; + } /* XXX: revisit */ m0->m_flags |= M_SKIP_FIREWALL; -#else - m0->m_pkthdr.pf.flags |= PF_TAG_GENERATED; -#endif if (r->rtableid >= 0) -#ifdef __FreeBSD__ - { M_SETFIB(m0, r->rtableid); - pf_mtag->rtableid = r->rtableid; -#else - m0->m_pkthdr.pf.rtableid = r->rtableid; -#endif -#ifdef __FreeBSD__ - } -#endif #ifdef ALTQ if (r->qid) { -#ifdef __FreeBSD__ pf_mtag->qid = r->qid; /* add hints for ecn */ pf_mtag->hdr = mtod(m0, struct ip *); -#else - m0->m_pkthdr.pf.qid = r->qid; - /* add hints for ecn */ - m0->m_pkthdr.pf.hdr = mtod(m0, struct ip *); -#endif } #endif /* ALTQ */ switch (af) { #ifdef INET case AF_INET: -#ifdef __FreeBSD__ + { + struct ip *ip; + /* icmp_error() expects host byte ordering */ ip = mtod(m0, struct ip *); NTOHS(ip->ip_len); NTOHS(ip->ip_off); - PF_UNLOCK(); - icmp_error(m0, type, code, 0, 0); - PF_LOCK(); -#else - icmp_error(m0, type, code, 0, 0); -#endif + + pfse->pfse_type = PFSE_ICMP; break; + } #endif /* INET */ #ifdef INET6 case AF_INET6: -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - icmp6_error(m0, type, code, 0); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif + pfse->pfse_type = PFSE_ICMP6; break; #endif /* INET6 */ } + pfse->pfse_m = m0; + pfse->pfse_icmp_type = type; + pfse->pfse_icmp_code = code; + pf_send(pfse); } /* @@ -2669,7 +2385,7 @@ pf_match_addr_range(struct pf_addr *b, struct pf_addr *e, return (1); } -int +static int pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p) { switch (op) { @@ -2704,7 +2420,7 @@ pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) return (pf_match(op, a1, a2, p)); } -int +static int pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) { if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE) @@ -2712,7 +2428,7 @@ pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) return (pf_match(op, a1, a2, u)); } -int +static int pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) { if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) @@ -2721,49 +2437,25 @@ pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) } int -#ifdef __FreeBSD__ -pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag, - struct pf_mtag *pf_mtag) -#else -pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag) -#endif +pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag, int mtag) { if (*tag == -1) -#ifdef __FreeBSD__ - *tag = pf_mtag->tag; -#else - *tag = m->m_pkthdr.pf.tag; -#endif + *tag = mtag; return ((!r->match_tag_not && r->match_tag == *tag) || (r->match_tag_not && r->match_tag != *tag)); } int -#ifdef __FreeBSD__ -pf_tag_packet(struct mbuf *m, int tag, int rtableid, - struct pf_mtag *pf_mtag) -#else -pf_tag_packet(struct mbuf *m, int tag, int rtableid) -#endif +pf_tag_packet(struct mbuf *m, struct pf_pdesc *pd, int tag) { - if (tag <= 0 && rtableid < 0) - return (0); - if (tag > 0) -#ifdef __FreeBSD__ - pf_mtag->tag = tag; -#else - m->m_pkthdr.pf.tag = tag; -#endif - if (rtableid >= 0) -#ifdef __FreeBSD__ - { - M_SETFIB(m, rtableid); - } -#else - m->m_pkthdr.pf.rtableid = rtableid; -#endif + KASSERT(tag > 0, ("%s: tag %d", __func__, tag)); + + if (pd->pf_mtag == NULL && ((pd->pf_mtag = pf_get_mtag(m)) == NULL)) + return (ENOMEM); + + pd->pf_mtag->tag = tag; return (0); } @@ -2774,26 +2466,19 @@ pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n, { struct pf_anchor_stackframe *f; + PF_RULES_RASSERT(); + (*r)->anchor->match = 0; if (match) *match = 0; -#ifdef __FreeBSD__ if (*depth >= sizeof(V_pf_anchor_stack) / sizeof(V_pf_anchor_stack[0])) { -#else - if (*depth >= sizeof(pf_anchor_stack) / - sizeof(pf_anchor_stack[0])) { -#endif printf("pf_step_into_anchor: stack overflow\n"); *r = TAILQ_NEXT(*r, entries); return; } else if (*depth == 0 && a != NULL) *a = *r; -#ifdef __FreeBSD__ f = V_pf_anchor_stack + (*depth)++; -#else - f = pf_anchor_stack + (*depth)++; -#endif f->rs = *rs; f->r = *r; if ((*r)->anchor_wildcard) { @@ -2819,14 +2504,12 @@ pf_step_out_of_anchor(int *depth, struct pf_ruleset **rs, int n, struct pf_anchor_stackframe *f; int quick = 0; + PF_RULES_RASSERT(); + do { if (*depth <= 0) break; -#ifdef __FreeBSD__ f = V_pf_anchor_stack + *depth - 1; -#else - f = pf_anchor_stack + *depth - 1; -#endif if (f->parent != NULL && f->child != NULL) { if (f->child->match || (match != NULL && *match)) { @@ -2913,35 +2596,15 @@ pf_addr_inc(struct pf_addr *addr, sa_family_t af) #endif /* INET6 */ int -#ifdef __FreeBSD__ -pf_socket_lookup(int direction, struct pf_pdesc *pd, struct inpcb *inp_arg) -#else -pf_socket_lookup(int direction, struct pf_pdesc *pd) -#endif +pf_socket_lookup(int direction, struct pf_pdesc *pd, struct mbuf *m) { struct pf_addr *saddr, *daddr; u_int16_t sport, dport; -#ifdef __FreeBSD__ struct inpcbinfo *pi; -#else - struct inpcbtable *tb; -#endif struct inpcb *inp; - if (pd == NULL) - return (-1); pd->lookup.uid = UID_MAX; pd->lookup.gid = GID_MAX; - pd->lookup.pid = NO_PID; - -#ifdef __FreeBSD__ - if (inp_arg != NULL) { - INP_LOCK_ASSERT(inp_arg); - pd->lookup.uid = inp_arg->inp_cred->cr_uid; - pd->lookup.gid = inp_arg->inp_cred->cr_groups[0]; - return (1); - } -#endif switch (pd->proto) { case IPPROTO_TCP: @@ -2949,22 +2612,14 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd) return (-1); sport = pd->hdr.tcp->th_sport; dport = pd->hdr.tcp->th_dport; -#ifdef __FreeBSD__ pi = &V_tcbinfo; -#else - tb = &tcbtable; -#endif break; case IPPROTO_UDP: if (pd->hdr.udp == NULL) return (-1); sport = pd->hdr.udp->uh_sport; dport = pd->hdr.udp->uh_dport; -#ifdef __FreeBSD__ pi = &V_udbinfo; -#else - tb = &udbtable; -#endif break; default: return (-1); @@ -2984,77 +2639,43 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd) switch (pd->af) { #ifdef INET case AF_INET: -#ifdef __FreeBSD__ - /* - * XXXRW: would be nice if we had an mbuf here so that we - * could use in_pcblookup_mbuf(). - */ - inp = in_pcblookup(pi, saddr->v4, sport, daddr->v4, - dport, INPLOOKUP_RLOCKPCB, NULL); + inp = in_pcblookup_mbuf(pi, saddr->v4, sport, daddr->v4, + dport, INPLOOKUP_RLOCKPCB, NULL, m); if (inp == NULL) { - inp = in_pcblookup(pi, saddr->v4, sport, + inp = in_pcblookup_mbuf(pi, saddr->v4, sport, daddr->v4, dport, INPLOOKUP_WILDCARD | - INPLOOKUP_RLOCKPCB, NULL); - if (inp == NULL) - return (-1); - } -#else - inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport); - if (inp == NULL) { - inp = in_pcblookup_listen(tb, daddr->v4, dport, 0, - NULL); + INPLOOKUP_RLOCKPCB, NULL, m); if (inp == NULL) return (-1); } -#endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: -#ifdef __FreeBSD__ - /* - * XXXRW: would be nice if we had an mbuf here so that we - * could use in6_pcblookup_mbuf(). - */ - inp = in6_pcblookup(pi, &saddr->v6, sport, - &daddr->v6, dport, INPLOOKUP_RLOCKPCB, NULL); + inp = in6_pcblookup_mbuf(pi, &saddr->v6, sport, &daddr->v6, + dport, INPLOOKUP_RLOCKPCB, NULL, m); if (inp == NULL) { - inp = in6_pcblookup(pi, &saddr->v6, sport, + inp = in6_pcblookup_mbuf(pi, &saddr->v6, sport, &daddr->v6, dport, INPLOOKUP_WILDCARD | - INPLOOKUP_RLOCKPCB, NULL); - if (inp == NULL) - return (-1); - } -#else - inp = in6_pcbhashlookup(tb, &saddr->v6, sport, &daddr->v6, - dport); - if (inp == NULL) { - inp = in6_pcblookup_listen(tb, &daddr->v6, dport, 0, - NULL); + INPLOOKUP_RLOCKPCB, NULL, m); if (inp == NULL) return (-1); } -#endif break; #endif /* INET6 */ default: return (-1); } -#ifdef __FreeBSD__ INP_RLOCK_ASSERT(inp); pd->lookup.uid = inp->inp_cred->cr_uid; pd->lookup.gid = inp->inp_cred->cr_groups[0]; INP_RUNLOCK(inp); -#else - pd->lookup.uid = inp->inp_socket->so_euid; - pd->lookup.gid = inp->inp_socket->so_egid; - pd->lookup.pid = inp->inp_socket->so_cpid; -#endif + return (1); } -u_int8_t +static u_int8_t pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) { int hlen; @@ -3094,17 +2715,13 @@ pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) return (wscale); } -u_int16_t +static u_int16_t pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) { int hlen; u_int8_t hdr[60]; u_int8_t *opt, optlen; -#ifdef __FreeBSD__ u_int16_t mss = V_tcp_mssdflt; -#else - u_int16_t mss = tcp_mssdflt; -#endif hlen = th_off << 2; /* hlen <= sizeof(hdr) */ if (hlen <= sizeof(struct tcphdr)) @@ -3136,7 +2753,7 @@ pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) return (mss); } -u_int16_t +static u_int16_t pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) { #ifdef INET @@ -3148,13 +2765,8 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) struct route_in6 ro6; #endif /* INET6 */ struct rtentry *rt = NULL; -#ifdef __FreeBSD__ int hlen = 0; u_int16_t mss = V_tcp_mssdflt; -#else - int hlen; - u_int16_t mss = tcp_mssdflt; -#endif switch (af) { #ifdef INET @@ -3165,11 +2777,7 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = addr->v4; -#ifdef __FreeBSD__ in_rtalloc_ign(&ro, 0, rtableid); -#else /* ! __FreeBSD__ */ - rtalloc_noclone(&ro, NO_CLONING); -#endif rt = ro.ro_rt; break; #endif /* INET */ @@ -3181,11 +2789,7 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); dst6->sin6_addr = addr->v6; -#ifdef __FreeBSD__ in6_rtalloc_ign(&ro6, 0, rtableid); -#else /* ! __FreeBSD__ */ - rtalloc_noclone((struct route *)&ro6, NO_CLONING); -#endif rt = ro6.ro_rt; break; #endif /* INET6 */ @@ -3193,11 +2797,7 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) if (rt && rt->rt_ifp) { mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); -#ifdef __FreeBSD__ mss = max(V_tcp_mssdflt, mss); -#else - mss = max(tcp_mssdflt, mss); -#endif RTFREE(rt); } mss = min(mss, offer); @@ -3205,7 +2805,7 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) return (mss); } -void +static void pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) { struct pf_rule *r = s->rule.ptr; @@ -3230,13 +2830,12 @@ pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) } } -u_int32_t +static u_int32_t pf_tcp_iss(struct pf_pdesc *pd) { MD5_CTX ctx; u_int32_t digest[4]; -#ifdef __FreeBSD__ if (V_pf_tcp_secret_init == 0) { read_random(&V_pf_tcp_secret, sizeof(V_pf_tcp_secret)); MD5Init(&V_pf_tcp_secret_ctx); @@ -3246,17 +2845,6 @@ pf_tcp_iss(struct pf_pdesc *pd) } ctx = V_pf_tcp_secret_ctx; -#else - if (pf_tcp_secret_init == 0) { - arc4random_buf(pf_tcp_secret, sizeof(pf_tcp_secret)); - MD5Init(&pf_tcp_secret_ctx); - MD5Update(&pf_tcp_secret_ctx, pf_tcp_secret, - sizeof(pf_tcp_secret)); - pf_tcp_secret_init = 1; - } - - ctx = pf_tcp_secret_ctx; -#endif MD5Update(&ctx, (char *)&pd->hdr.tcp->th_sport, sizeof(u_short)); MD5Update(&ctx, (char *)&pd->hdr.tcp->th_dport, sizeof(u_short)); @@ -3268,36 +2856,26 @@ pf_tcp_iss(struct pf_pdesc *pd) MD5Update(&ctx, (char *)&pd->dst->v4, sizeof(struct in_addr)); } MD5Final((u_char *)digest, &ctx); -#ifdef __FreeBSD__ V_pf_tcp_iss_off += 4096; #define ISN_RANDOM_INCREMENT (4096 - 1) return (digest[0] + (arc4random() & ISN_RANDOM_INCREMENT) + V_pf_tcp_iss_off); -#undef ISN_RANDOM_INCREMENT -#else - pf_tcp_iss_off += 4096; - return (digest[0] + tcp_iss + pf_tcp_iss_off); -#endif +#undef ISN_RANDOM_INCREMENT } -int +static int pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, - struct pfi_kif *kif, struct mbuf *m, int off, void *h, - struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, -#ifdef __FreeBSD__ - struct ifqueue *ifq, struct inpcb *inp) -#else - struct ifqueue *ifq) -#endif + struct pfi_kif *kif, struct mbuf *m, int off, struct pf_pdesc *pd, + struct pf_rule **am, struct pf_ruleset **rsm, struct inpcb *inp) { struct pf_rule *nr = NULL; - struct pf_addr *saddr = pd->src, *daddr = pd->dst; + struct pf_addr * const saddr = pd->src; + struct pf_addr * const daddr = pd->dst; sa_family_t af = pd->af; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_src_node *nsn = NULL; struct tcphdr *th = pd->hdr.tcp; - struct pf_state_key *skw = NULL, *sks = NULL; struct pf_state_key *sk = NULL, *nk = NULL; u_short reason; int rewrite = 0, hdrlen = 0; @@ -3305,31 +2883,18 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, int asd = 0; int match = 0; int state_icmp = 0; -#ifdef __FreeBSD__ u_int16_t sport = 0, dport = 0; u_int16_t bproto_sum = 0, bip_sum = 0; -#else - u_int16_t sport, dport; - u_int16_t bproto_sum = 0, bip_sum; -#endif u_int8_t icmptype = 0, icmpcode = 0; + PF_RULES_RASSERT(); - if (direction == PF_IN && pf_check_congestion(ifq)) { - REASON_SET(&reason, PFRES_CONGEST); - return (PF_DROP); - } - -#ifdef __FreeBSD__ - if (inp != NULL) - pd->lookup.done = pf_socket_lookup(direction, pd, inp); - else if (V_debug_pfugidhack) { - PF_UNLOCK(); - DPFPRINTF(PF_DEBUG_MISC, ("pf: unlocked lookup\n")); - pd->lookup.done = pf_socket_lookup(direction, pd, inp); - PF_LOCK(); + if (inp != NULL) { + INP_LOCK_ASSERT(inp); + pd->lookup.uid = inp->inp_cred->cr_uid; + pd->lookup.gid = inp->inp_cred->cr_groups[0]; + pd->lookup.done = 1; } -#endif switch (pd->proto) { case IPPROTO_TCP: @@ -3383,12 +2948,10 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); /* check packet for BINAT/NAT/RDR */ - if ((nr = pf_get_translation(pd, m, off, direction, kif, &nsn, - &skw, &sks, &sk, &nk, saddr, daddr, sport, dport)) != NULL) { - if (nk == NULL || sk == NULL) { - REASON_SET(&reason, PFRES_MEMORY); - goto cleanup; - } + if ((nr = pf_get_translation(pd, m, off, direction, kif, &nsn, &sk, + &nk, saddr, daddr, sport, dport)) != NULL) { + KASSERT(sk != NULL, ("%s: null sk", __func__)); + KASSERT(nk != NULL, ("%s: null nk", __func__)); if (pd->ip_sum) bip_sum = *pd->ip_sum; @@ -3551,36 +3114,21 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, r = TAILQ_NEXT(r, entries); /* tcp/udp only. uid.op always 0 in other cases */ else if (r->uid.op && (pd->lookup.done || (pd->lookup.done = -#ifdef __FreeBSD__ - pf_socket_lookup(direction, pd, inp), 1)) && -#else - pf_socket_lookup(direction, pd), 1)) && -#endif + pf_socket_lookup(direction, pd, m), 1)) && !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], pd->lookup.uid)) r = TAILQ_NEXT(r, entries); /* tcp/udp only. gid.op always 0 in other cases */ else if (r->gid.op && (pd->lookup.done || (pd->lookup.done = -#ifdef __FreeBSD__ - pf_socket_lookup(direction, pd, inp), 1)) && -#else - pf_socket_lookup(direction, pd), 1)) && -#endif + pf_socket_lookup(direction, pd, m), 1)) && !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], pd->lookup.gid)) r = TAILQ_NEXT(r, entries); else if (r->prob && -#ifdef __FreeBSD__ r->prob <= arc4random()) -#else - r->prob <= arc4random_uniform(UINT_MAX - 1) + 1) -#endif r = TAILQ_NEXT(r, entries); -#ifdef __FreeBSD__ - else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag)) -#else - else if (r->match_tag && !pf_match_tag(m, r, &tag)) -#endif + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != IPPROTO_TCP || !pf_osfp_match( @@ -3617,8 +3165,8 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (r->log || (nr != NULL && nr->log)) { if (rewrite) m_copyback(m, off, hdrlen, pd->hdr.any); - PFLOG_PACKET(kif, h, m, af, direction, reason, r->log ? r : nr, - a, ruleset, pd); + PFLOG_PACKET(kif, m, af, direction, reason, r->log ? r : nr, a, + ruleset, pd, 1); } if ((r->action == PF_DROP) && @@ -3674,14 +3222,10 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, ack++; if (th->th_flags & TH_FIN) ack++; -#ifdef __FreeBSD__ pf_send_tcp(m, r, af, pd->dst, -#else - pf_send_tcp(r, af, pd->dst, -#endif pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, - r->return_ttl, 1, 0, pd->eh, kif->pfik_ifp); + r->return_ttl, 1, 0, kif->pfik_ifp); } } else if (pd->proto != IPPROTO_ICMP && af == AF_INET && r->return_icmp) @@ -3696,106 +3240,69 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (r->action == PF_DROP) goto cleanup; -#ifdef __FreeBSD__ - if (pf_tag_packet(m, tag, rtableid, pd->pf_mtag)) { -#else - if (pf_tag_packet(m, tag, rtableid)) { -#endif + if (tag > 0 && pf_tag_packet(m, pd, tag)) { REASON_SET(&reason, PFRES_MEMORY); goto cleanup; } + if (rtableid >= 0) + M_SETFIB(m, rtableid); if (!state_icmp && (r->keep_state || nr != NULL || (pd->flags & PFDESC_TCP_NORM))) { int action; - action = pf_create_state(r, nr, a, pd, nsn, skw, sks, nk, sk, m, - off, sport, dport, &rewrite, kif, sm, tag, bproto_sum, - bip_sum, hdrlen); + action = pf_create_state(r, nr, a, pd, nsn, nk, sk, m, off, + sport, dport, &rewrite, kif, sm, tag, bproto_sum, bip_sum, + hdrlen); if (action != PF_PASS) return (action); } else { -#ifdef __FreeBSD__ - if (sk != NULL) - pool_put(&V_pf_state_key_pl, sk); - if (nk != NULL) - pool_put(&V_pf_state_key_pl, nk); -#else if (sk != NULL) - pool_put(&pf_state_key_pl, sk); + uma_zfree(V_pf_state_key_z, sk); if (nk != NULL) - pool_put(&pf_state_key_pl, nk); -#endif + uma_zfree(V_pf_state_key_z, nk); } /* copy back packet headers if we performed NAT operations */ if (rewrite) m_copyback(m, off, hdrlen, pd->hdr.any); -#if NPFSYNC > 0 - if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) && -#ifdef __FreeBSD__ - direction == PF_OUT && pfsync_up_ptr != NULL && pfsync_up_ptr()) { -#else - direction == PF_OUT && pfsync_up()) { -#endif + if (*sm != NULL && !((*sm)->state_flags & PFSTATE_NOSYNC) && + direction == PF_OUT && + pfsync_defer_ptr != NULL && pfsync_defer_ptr(*sm, m)) /* * We want the state created, but we dont * want to send this in case a partner * firewall has to know about it to allow * replies through it. */ -#ifdef __FreeBSD__ - if (pfsync_defer_ptr != NULL && - pfsync_defer_ptr(*sm, m)) -#else - if (pfsync_defer(*sm, m)) -#endif - return (PF_DEFER); - } -#endif + return (PF_DEFER); return (PF_PASS); cleanup: -#ifdef __FreeBSD__ - if (sk != NULL) - pool_put(&V_pf_state_key_pl, sk); - if (nk != NULL) - pool_put(&V_pf_state_key_pl, nk); -#else if (sk != NULL) - pool_put(&pf_state_key_pl, sk); + uma_zfree(V_pf_state_key_z, sk); if (nk != NULL) - pool_put(&pf_state_key_pl, nk); -#endif + uma_zfree(V_pf_state_key_z, nk); return (PF_DROP); } -static __inline int +static int pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, - struct pf_pdesc *pd, struct pf_src_node *nsn, struct pf_state_key *skw, - struct pf_state_key *sks, struct pf_state_key *nk, struct pf_state_key *sk, - struct mbuf *m, int off, u_int16_t sport, u_int16_t dport, int *rewrite, - struct pfi_kif *kif, struct pf_state **sm, int tag, u_int16_t bproto_sum, - u_int16_t bip_sum, int hdrlen) + struct pf_pdesc *pd, struct pf_src_node *nsn, struct pf_state_key *nk, + struct pf_state_key *sk, struct mbuf *m, int off, u_int16_t sport, + u_int16_t dport, int *rewrite, struct pfi_kif *kif, struct pf_state **sm, + int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen) { struct pf_state *s = NULL; struct pf_src_node *sn = NULL; struct tcphdr *th = pd->hdr.tcp; -#ifdef __FreeBSD__ u_int16_t mss = V_tcp_mssdflt; -#else - u_int16_t mss = tcp_mssdflt; -#endif u_short reason; /* check maximums */ if (r->max_states && (r->states_cur >= r->max_states)) { -#ifdef __FreeBSD__ V_pf_status.lcounters[LCNT_STATES]++; -#else - pf_status.lcounters[LCNT_STATES]++; -#endif REASON_SET(&reason, PFRES_MAXSTATES); return (PF_DROP); } @@ -3812,11 +3319,7 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, REASON_SET(&reason, PFRES_SRCLIMIT); goto csfailed; } -#ifdef __FreeBSD__ - s = pool_get(&V_pf_state_pl, PR_NOWAIT | PR_ZERO); -#else - s = pool_get(&pf_state_pl, PR_NOWAIT | PR_ZERO); -#endif + s = uma_zalloc(V_pf_state_z, M_NOWAIT | M_ZERO); if (s == NULL) { REASON_SET(&reason, PFRES_MEMORY); goto csfailed; @@ -3829,8 +3332,6 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, s->state_flags |= PFSTATE_ALLOWOPTS; if (r->rule_flag & PFRULE_STATESLOPPY) s->state_flags |= PFSTATE_SLOPPY; - if (r->rule_flag & PFRULE_PFLOW) - s->state_flags |= PFSTATE_PFLOW; s->log = r->log & PF_LOG_ALL; s->sync_state = PFSYNC_S_NONE; if (nr != NULL) @@ -3888,8 +3389,8 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, s->timeout = PFTM_OTHER_FIRST_PACKET; } - s->creation = time_second; - s->expire = time_second; + s->creation = time_uptime; + s->expire = time_uptime; if (sn != NULL) { s->src_node = sn; @@ -3907,11 +3408,7 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, REASON_SET(&reason, PFRES_MEMORY); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); -#ifdef __FreeBSD__ - pool_put(&V_pf_state_pl, s); -#else - pool_put(&pf_state_pl, s); -#endif + uma_zfree(V_pf_state_z, s); return (PF_DROP); } if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub && @@ -3923,40 +3420,43 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, pf_normalize_tcp_cleanup(s); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); -#ifdef __FreeBSD__ - pool_put(&V_pf_state_pl, s); -#else - pool_put(&pf_state_pl, s); -#endif + uma_zfree(V_pf_state_z, s); return (PF_DROP); } } s->direction = pd->dir; - if (sk == NULL && pf_state_key_setup(pd, nr, &skw, &sks, &sk, &nk, - pd->src, pd->dst, sport, dport)) - goto csfailed; + /* + * sk/nk could already been setup by pf_get_translation(). + */ + if (nr == NULL) { + KASSERT((sk == NULL && nk == NULL), ("%s: nr %p sk %p, nk %p", + __func__, nr, sk, nk)); + sk = pf_state_key_setup(pd, pd->src, pd->dst, sport, dport); + if (sk == NULL) + goto csfailed; + nk = sk; + } else + KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", + __func__, nr, sk, nk)); - if (pf_state_insert(BOUND_IFACE(r, kif), skw, sks, s)) { + /* Swap sk/nk for PF_OUT. */ + if (pf_state_insert(BOUND_IFACE(r, kif), + (pd->dir == PF_IN) ? sk : nk, + (pd->dir == PF_IN) ? nk : sk, s)) { if (pd->proto == IPPROTO_TCP) pf_normalize_tcp_cleanup(s); REASON_SET(&reason, PFRES_STATEINS); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); -#ifdef __FreeBSD__ - pool_put(&V_pf_state_pl, s); -#else - pool_put(&pf_state_pl, s); -#endif + uma_zfree(V_pf_state_z, s); return (PF_DROP); } else *sm = s; pf_set_rt_ifp(s, pd->src); /* needs s->state_key set */ - if (tag > 0) { - pf_tag_ref(tag); + if (tag > 0) s->tag = tag; - } if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { s->src.state = PF_TCPS_PROXY_SRC; @@ -3984,13 +3484,9 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, mss = pf_calc_mss(pd->src, pd->af, rtid, mss); mss = pf_calc_mss(pd->dst, pd->af, rtid, mss); s->src.mss = mss; -#ifdef __FreeBSD__ pf_send_tcp(NULL, r, pd->af, pd->dst, pd->src, th->th_dport, -#else - pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport, -#endif th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL, NULL); + TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL); REASON_SET(&reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } @@ -3998,48 +3494,27 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, return (PF_PASS); csfailed: -#ifdef __FreeBSD__ - if (sk != NULL) - pool_put(&V_pf_state_key_pl, sk); - if (nk != NULL) - pool_put(&V_pf_state_key_pl, nk); -#else if (sk != NULL) - pool_put(&pf_state_key_pl, sk); + uma_zfree(V_pf_state_key_z, sk); if (nk != NULL) - pool_put(&pf_state_key_pl, nk); -#endif + uma_zfree(V_pf_state_key_z, nk); if (sn != NULL && sn->states == 0 && sn->expire == 0) { -#ifdef __FreeBSD__ - RB_REMOVE(pf_src_tree, &V_tree_src_tracking, sn); + pf_remove_src_node(sn); V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; V_pf_status.src_nodes--; - pool_put(&V_pf_src_tree_pl, sn); -#else - RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, sn); -#endif + uma_zfree(V_pf_sources_z, sn); } if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { -#ifdef __FreeBSD__ - RB_REMOVE(pf_src_tree, &V_tree_src_tracking, nsn); + pf_remove_src_node(nsn); V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; V_pf_status.src_nodes--; - pool_put(&V_pf_src_tree_pl, nsn); -#else - RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, nsn); -#endif + uma_zfree(V_pf_sources_z, nsn); } return (PF_DROP); } -int +static int pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, struct mbuf *m, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) @@ -4052,6 +3527,8 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, int asd = 0; int match = 0; + PF_RULES_RASSERT(); + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); while (r != NULL) { r->evaluations++; @@ -4086,11 +3563,8 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, else if (r->prob && r->prob <= (arc4random() % (UINT_MAX - 1) + 1)) r = TAILQ_NEXT(r, entries); -#ifdef __FreeBSD__ - else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag)) -#else - else if (r->match_tag && !pf_match_tag(m, r, &tag)) -#endif + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) r = TAILQ_NEXT(r, entries); else { if (r->anchor == NULL) { @@ -4116,17 +3590,13 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, REASON_SET(&reason, PFRES_MATCH); if (r->log) - PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset, - pd); + PFLOG_PACKET(kif, m, af, direction, reason, r, a, ruleset, pd, + 1); if (r->action != PF_PASS) return (PF_DROP); -#ifdef __FreeBSD__ - if (pf_tag_packet(m, tag, -1, pd->pf_mtag)) { -#else - if (pf_tag_packet(m, tag, -1)) { -#endif + if (tag > 0 && pf_tag_packet(m, pd, tag)) { REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } @@ -4134,7 +3604,7 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, return (PF_PASS); } -int +static int pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, struct pf_state **state, struct pfi_kif *kif, struct mbuf *m, int off, struct pf_pdesc *pd, u_short *reason, int *copyback) @@ -4334,7 +3804,7 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, src->state = dst->state = TCPS_TIME_WAIT; /* update expire time */ - (*state)->expire = time_second; + (*state)->expire = time_uptime; if (src->state >= TCPS_FIN_WAIT_2 && dst->state >= TCPS_FIN_WAIT_2) (*state)->timeout = PFTM_TCP_CLOSED; @@ -4381,23 +3851,14 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, * and keep updating the state TTL. */ -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: loose state match: "); pf_print_state(*state); pf_print_flags(th->th_flags); printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " "pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack, -#ifdef __FreeBSD__ pd->p_len, ackskew, (unsigned long long)(*state)->packets[0], (unsigned long long)(*state)->packets[1], -#else - pd->p_len, ackskew, (*state)->packets[0], - (*state)->packets[1], -#endif pd->dir == PF_IN ? "in" : "out", pd->dir == (*state)->direction ? "fwd" : "rev"); } @@ -4436,36 +3897,24 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, (*state)->src.state == TCPS_SYN_SENT) { /* Send RST for state mismatches during handshake */ if (!(th->th_flags & TH_RST)) -#ifdef __FreeBSD__ pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, -#else - pf_send_tcp((*state)->rule.ptr, pd->af, -#endif pd->dst, pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), 0, TH_RST, 0, 0, (*state)->rule.ptr->return_ttl, 1, 0, - pd->eh, kif->pfik_ifp); + kif->pfik_ifp); src->seqlo = 0; src->seqhi = 1; src->max_win = 1; -#ifdef __FreeBSD__ } else if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - } else if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: BAD state: "); pf_print_state(*state); pf_print_flags(th->th_flags); printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " "pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack, pd->p_len, ackskew, -#ifdef __FreeBSD__ (unsigned long long)(*state)->packets[0], (unsigned long long)(*state)->packets[1], -#else - (*state)->packets[0], (*state)->packets[1], -#endif pd->dir == PF_IN ? "in" : "out", pd->dir == (*state)->direction ? "fwd" : "rev"); printf("pf: State failure on: %c %c %c %c | %c %c\n", @@ -4484,7 +3933,7 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, return (PF_PASS); } -int +static int pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst, struct pf_state **state, struct pf_pdesc *pd, u_short *reason) { @@ -4536,7 +3985,7 @@ pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst, src->state = dst->state = TCPS_TIME_WAIT; /* update expire time */ - (*state)->expire = time_second; + (*state)->expire = time_uptime; if (src->state >= TCPS_FIN_WAIT_2 && dst->state >= TCPS_FIN_WAIT_2) (*state)->timeout = PFTM_TCP_CLOSED; @@ -4555,7 +4004,7 @@ pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst, return (PF_PASS); } -int +static int pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) @@ -4566,6 +4015,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, struct pf_state_peer *src, *dst; struct pf_state_key *sk; + bzero(&key, sizeof(key)); key.af = pd->af; key.proto = IPPROTO_TCP; if (direction == PF_IN) { /* wire side, straight */ @@ -4580,11 +4030,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, key.port[0] = th->th_dport; } -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); if (direction == (*state)->direction) { src = &(*state)->src; @@ -4606,15 +4052,10 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, REASON_SET(reason, PFRES_SYNPROXY); return (PF_DROP); } -#ifdef __FreeBSD__ pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, -#else - pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, -#endif pd->src, th->th_dport, th->th_sport, (*state)->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1, - 0, NULL, NULL); + TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1, 0, NULL); REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } else if (!(th->th_flags & TH_ACK) || @@ -4640,15 +4081,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, (*state)->src.max_win = MAX(ntohs(th->th_win), 1); if ((*state)->dst.seqhi == 1) (*state)->dst.seqhi = htonl(arc4random()); -#ifdef __FreeBSD__ pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, -#else - pf_send_tcp((*state)->rule.ptr, pd->af, -#endif &sk->addr[pd->sidx], &sk->addr[pd->didx], sk->port[pd->sidx], sk->port[pd->didx], (*state)->dst.seqhi, 0, TH_SYN, 0, - (*state)->src.mss, 0, 0, (*state)->tag, NULL, NULL); + (*state)->src.mss, 0, 0, (*state)->tag, NULL); REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } else if (((th->th_flags & (TH_SYN|TH_ACK)) != @@ -4659,25 +4096,16 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, } else { (*state)->dst.max_win = MAX(ntohs(th->th_win), 1); (*state)->dst.seqlo = ntohl(th->th_seq); -#ifdef __FreeBSD__ pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, -#else - pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, -#endif pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ntohl(th->th_seq) + 1, TH_ACK, (*state)->src.max_win, 0, 0, 0, - (*state)->tag, NULL, NULL); -#ifdef __FreeBSD__ + (*state)->tag, NULL); pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, -#else - pf_send_tcp((*state)->rule.ptr, pd->af, -#endif &sk->addr[pd->sidx], &sk->addr[pd->didx], sk->port[pd->sidx], sk->port[pd->didx], (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, - TH_ACK, (*state)->dst.max_win, 0, 0, 1, - 0, NULL, NULL); + TH_ACK, (*state)->dst.max_win, 0, 0, 1, 0, NULL); (*state)->src.seqdiff = (*state)->dst.seqhi - (*state)->src.seqlo; (*state)->dst.seqdiff = (*state)->src.seqhi - @@ -4697,11 +4125,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) && dst->state >= TCPS_FIN_WAIT_2 && src->state >= TCPS_FIN_WAIT_2) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: state reuse "); pf_print_state(*state); pf_print_flags(th->th_flags); @@ -4709,7 +4133,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, } /* XXX make sure it's the same direction ?? */ (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; - pf_unlink_state(*state); + pf_unlink_state(*state, PF_ENTER_LOCKED); *state = NULL; return (PF_DROP); } @@ -4743,16 +4167,12 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, /* Copyback sequence modulation or stateful scrub changes if needed */ if (copyback) -#ifdef __FreeBSD__ m_copyback(m, off, sizeof(*th), (caddr_t)th); -#else - m_copyback(m, off, sizeof(*th), th); -#endif return (PF_PASS); } -int +static int pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd) { @@ -4760,6 +4180,7 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, struct pf_state_key_cmp key; struct udphdr *uh = pd->hdr.udp; + bzero(&key, sizeof(key)); key.af = pd->af; key.proto = IPPROTO_UDP; if (direction == PF_IN) { /* wire side, straight */ @@ -4774,11 +4195,7 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, key.port[0] = uh->uh_dport; } -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); if (direction == (*state)->direction) { src = &(*state)->src; @@ -4795,7 +4212,7 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, dst->state = PFUDPS_MULTIPLE; /* update expire time */ - (*state)->expire = time_second; + (*state)->expire = time_uptime; if (src->state == PFUDPS_MULTIPLE && dst->state == PFUDPS_MULTIPLE) (*state)->timeout = PFTM_UDP_MULTIPLE; else @@ -4816,30 +4233,23 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->didx], nk->port[pd->didx], 1, pd->af); -#ifdef __FreeBSD__ m_copyback(m, off, sizeof(*uh), (caddr_t)uh); -#else - m_copyback(m, off, sizeof(*uh), uh); -#endif } return (PF_PASS); } -int +static int pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { struct pf_addr *saddr = pd->src, *daddr = pd->dst; -#ifdef __FreeBSD__ u_int16_t icmpid = 0, *icmpsum; -#else - u_int16_t icmpid, *icmpsum; -#endif u_int8_t icmptype; int state_icmp = 0; struct pf_state_key_cmp key; + bzero(&key, sizeof(key)); switch (pd->proto) { #ifdef INET case IPPROTO_ICMP: @@ -4887,13 +4297,9 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, PF_ACPY(&key.addr[0], pd->dst, key.af); } -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); - (*state)->expire = time_second; + (*state)->expire = time_uptime; (*state)->timeout = PFTM_ICMP_ERROR_REPLY; /* translate source/destination address, if necessary */ @@ -4926,10 +4332,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, } m_copyback(m, off, ICMP_MINLEN, -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp); + (caddr_t )pd->hdr.icmp); break; #endif /* INET */ #ifdef INET6 @@ -4946,12 +4349,8 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, &pd->hdr.icmp6->icmp6_cksum, &nk->addr[pd->didx], 0); - m_copyback(m, off, - sizeof(struct icmp6_hdr), -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp6); + m_copyback(m, off, sizeof(struct icmp6_hdr), + (caddr_t )pd->hdr.icmp6); break; #endif /* INET6 */ } @@ -4965,9 +4364,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, */ struct pf_pdesc pd2; -#ifdef __FreeBSD__ bzero(&pd2, sizeof pd2); -#endif #ifdef INET struct ip h2; #endif /* INET */ @@ -4975,13 +4372,8 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, struct ip6_hdr h2_6; int terminal = 0; #endif /* INET6 */ -#ifdef __FreeBSD__ int ipoff2 = 0; int off2 = 0; -#else - int ipoff2; - int off2; -#endif pd2.af = pd->af; /* Payload packet is from the opposite direction. */ @@ -5102,11 +4494,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, key.port[pd2.sidx] = th.th_sport; key.port[pd2.didx] = th.th_dport; -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); if (direction == (*state)->direction) { src = &(*state)->dst; @@ -5132,11 +4520,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, if (!((*state)->state_flags & PFSTATE_SLOPPY) && (!SEQ_GEQ(src->seqhi, seq) || !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)))) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: BAD ICMP %d:%d ", icmptype, pd->hdr.icmp->icmp_code); pf_print_host(pd->src, 0, pd->af); @@ -5149,11 +4533,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, REASON_SET(reason, PFRES_BADSTATE); return (PF_DROP); } else { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf: OK ICMP %d:%d ", icmptype, pd->hdr.icmp->icmp_code); pf_print_host(pd->src, 0, pd->af); @@ -5197,38 +4577,22 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp); + (caddr_t )pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), -#ifdef __FreeBSD__ - (caddr_t) -#endif - &h2); + (caddr_t )&h2); break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp6); + (caddr_t )pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), -#ifdef __FreeBSD__ - (caddr_t) -#endif - &h2_6); + (caddr_t )&h2_6); break; #endif /* INET6 */ } -#ifdef __FreeBSD__ m_copyback(m, off2, 8, (caddr_t)&th); -#else - m_copyback(m, off2, 8, &th); -#endif } return (PF_PASS); @@ -5252,11 +4616,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, key.port[pd2.sidx] = uh.uh_sport; key.port[pd2.didx] = uh.uh_dport; -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != @@ -5287,38 +4647,21 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp); -#ifdef __FreeBSD__ + (caddr_t )pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); -#else - m_copyback(m, ipoff2, sizeof(h2), &h2); -#endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp6); + (caddr_t )pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), -#ifdef __FreeBSD__ - (caddr_t) -#endif - &h2_6); + (caddr_t )&h2_6); break; #endif /* INET6 */ } -#ifdef __FreeBSD__ m_copyback(m, off2, sizeof(uh), (caddr_t)&uh); -#else - m_copyback(m, off2, sizeof(uh), &uh); -#endif } return (PF_PASS); break; @@ -5341,11 +4684,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); key.port[0] = key.port[1] = iih.icmp_id; -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != @@ -5372,15 +4711,9 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET); -#ifdef __FreeBSD__ m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); m_copyback(m, off2, ICMP_MINLEN, (caddr_t)&iih); -#else - m_copyback(m, off, ICMP_MINLEN, pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), &h2); - m_copyback(m, off2, ICMP_MINLEN, &iih); -#endif } return (PF_PASS); break; @@ -5404,11 +4737,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); key.port[0] = key.port[1] = iih.icmp6_id; -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != @@ -5435,19 +4764,11 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); -#ifdef __FreeBSD__ m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); m_copyback(m, off2, sizeof(struct icmp6_hdr), (caddr_t)&iih); -#else - m_copyback(m, off, sizeof(struct icmp6_hdr), - pd->hdr.icmp6); - m_copyback(m, ipoff2, sizeof(h2_6), &h2_6); - m_copyback(m, off2, sizeof(struct icmp6_hdr), - &iih); -#endif } return (PF_PASS); break; @@ -5460,11 +4781,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); key.port[0] = key.port[1] = 0; -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != @@ -5490,30 +4807,18 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, switch (pd2.af) { #ifdef INET case AF_INET: -#ifdef __FreeBSD__ m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); -#else - m_copyback(m, off, ICMP_MINLEN, - pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), &h2); -#endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), -#ifdef __FreeBSD__ - (caddr_t) -#endif - pd->hdr.icmp6); + (caddr_t )pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), -#ifdef __FreeBSD__ - (caddr_t) -#endif - &h2_6); + (caddr_t )&h2_6); break; #endif /* INET6 */ } @@ -5525,13 +4830,14 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, } } -int +static int pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; struct pf_state_key_cmp key; + bzero(&key, sizeof(key)); key.af = pd->af; key.proto = pd->proto; if (direction == PF_IN) { @@ -5544,11 +4850,7 @@ pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, key.port[1] = key.port[0] = 0; } -#ifdef __FreeBSD__ - STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); -#else - STATE_LOOKUP(kif, &key, direction, *state, m); -#endif + STATE_LOOKUP(kif, &key, direction, *state, pd); if (direction == (*state)->direction) { src = &(*state)->src; @@ -5565,7 +4867,7 @@ pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, dst->state = PFOTHERS_MULTIPLE; /* update expire time */ - (*state)->expire = time_second; + (*state)->expire = time_uptime; if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE) (*state)->timeout = PFTM_OTHER_MULTIPLE; else @@ -5575,17 +4877,10 @@ pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { struct pf_state_key *nk = (*state)->key[pd->didx]; -#ifdef __FreeBSD__ - KASSERT(nk, ("%s: nk is null", __FUNCTION__)); - KASSERT(pd, ("%s: pd is null", __FUNCTION__)); - KASSERT(pd->src, ("%s: pd->src is null", __FUNCTION__)); - KASSERT(pd->dst, ("%s: pd->dst is null", __FUNCTION__)); -#else - KASSERT(nk); - KASSERT(pd); - KASSERT(pd->src); - KASSERT(pd->dst); -#endif + KASSERT(nk, ("%s: nk is null", __func__)); + KASSERT(pd, ("%s: pd is null", __func__)); + KASSERT(pd->src, ("%s: pd->src is null", __func__)); + KASSERT(pd->dst, ("%s: pd->dst is null", __func__)); switch (pd->af) { #ifdef INET case AF_INET: @@ -5672,21 +4967,13 @@ int pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif, int rtableid) { -#ifdef __FreeBSD__ #ifdef RADIX_MPATH struct radix_node_head *rnh; -#endif #endif struct sockaddr_in *dst; int ret = 1; int check_mpath; -#ifndef __FreeBSD__ - extern int ipmultipath; -#endif #ifdef INET6 -#ifndef __FreeBSD__ - extern int ip6_multipath; -#endif struct sockaddr_in6 *dst6; struct route_in6 ro; #else @@ -5697,13 +4984,11 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif, struct ifnet *ifp; check_mpath = 0; -#ifdef __FreeBSD__ #ifdef RADIX_MPATH /* XXX: stick to table 0 for now */ rnh = rt_tables_get_rnh(0, af); if (rnh != NULL && rn_mpath_capable(rnh)) check_mpath = 1; -#endif #endif bzero(&ro, sizeof(ro)); switch (af) { @@ -5712,120 +4997,15 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif, dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = addr->v4; -#ifndef __FreeBSD__ - if (ipmultipath) - check_mpath = 1; -#endif break; #ifdef INET6 case AF_INET6: /* * Skip check for addresses with embedded interface scope, * as they would always match anyway. - */ - if (IN6_IS_SCOPE_EMBED(&addr->v6)) - goto out; - dst6 = (struct sockaddr_in6 *)&ro.ro_dst; - dst6->sin6_family = AF_INET6; - dst6->sin6_len = sizeof(*dst6); - dst6->sin6_addr = addr->v6; -#ifndef __FreeBSD__ - if (ip6_multipath) - check_mpath = 1; -#endif - break; -#endif /* INET6 */ - default: - return (0); - } - - /* Skip checks for ipsec interfaces */ - if (kif != NULL && kif->pfik_ifp->if_type == IFT_ENC) - goto out; - -#ifdef __FreeBSD__ - switch (af) { -#ifdef INET6 - case AF_INET6: - in6_rtalloc_ign(&ro, 0, rtableid); - break; -#endif -#ifdef INET - case AF_INET: - in_rtalloc_ign((struct route *)&ro, 0, rtableid); - break; -#endif - default: - rtalloc_ign((struct route *)&ro, 0); /* No/default FIB. */ - break; - } -#else /* ! __FreeBSD__ */ - rtalloc_noclone((struct route *)&ro, NO_CLONING); -#endif - - if (ro.ro_rt != NULL) { - /* No interface given, this is a no-route check */ - if (kif == NULL) - goto out; - - if (kif->pfik_ifp == NULL) { - ret = 0; - goto out; - } - - /* Perform uRPF check if passed input interface */ - ret = 0; - rn = (struct radix_node *)ro.ro_rt; - do { - rt = (struct rtentry *)rn; -#ifndef __FreeBSD__ /* CARPDEV */ - if (rt->rt_ifp->if_type == IFT_CARP) - ifp = rt->rt_ifp->if_carpdev; - else -#endif - ifp = rt->rt_ifp; - - if (kif->pfik_ifp == ifp) - ret = 1; -#ifdef __FreeBSD__ -#ifdef RADIX_MPATH - rn = rn_mpath_next(rn); -#endif -#else - rn = rn_mpath_next(rn, 0); -#endif - } while (check_mpath == 1 && rn != NULL && ret == 0); - } else - ret = 0; -out: - if (ro.ro_rt != NULL) - RTFREE(ro.ro_rt); - return (ret); -} - -int -pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw, - int rtableid) -{ - struct sockaddr_in *dst; -#ifdef INET6 - struct sockaddr_in6 *dst6; - struct route_in6 ro; -#else - struct route ro; -#endif - int ret = 0; - - bzero(&ro, sizeof(ro)); - switch (af) { - case AF_INET: - dst = satosin(&ro.ro_dst); - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = addr->v4; - break; -#ifdef INET6 - case AF_INET6: + */ + if (IN6_IS_SCOPE_EMBED(&addr->v6)) + goto out; dst6 = (struct sockaddr_in6 *)&ro.ro_dst; dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); @@ -5836,7 +5016,10 @@ pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw, return (0); } -#ifdef __FreeBSD__ + /* Skip checks for ipsec interfaces */ + if (kif != NULL && kif->pfik_ifp->if_type == IFT_ENC) + goto out; + switch (af) { #ifdef INET6 case AF_INET6: @@ -5849,327 +5032,221 @@ pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw, break; #endif default: - rtalloc_ign((struct route *)&ro, 0); + rtalloc_ign((struct route *)&ro, 0); /* No/default FIB. */ break; } -#else /* ! __FreeBSD__ */ - rtalloc_noclone((struct route *)&ro, NO_CLONING); -#endif if (ro.ro_rt != NULL) { -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ -#else - if (ro.ro_rt->rt_labelid == aw->v.rtlabel) - ret = 1; + /* No interface given, this is a no-route check */ + if (kif == NULL) + goto out; + + if (kif->pfik_ifp == NULL) { + ret = 0; + goto out; + } + + /* Perform uRPF check if passed input interface */ + ret = 0; + rn = (struct radix_node *)ro.ro_rt; + do { + rt = (struct rtentry *)rn; + ifp = rt->rt_ifp; + + if (kif->pfik_ifp == ifp) + ret = 1; +#ifdef RADIX_MPATH + rn = rn_mpath_next(rn); #endif + } while (check_mpath == 1 && rn != NULL && ret == 0); + } else + ret = 0; +out: + if (ro.ro_rt != NULL) RTFREE(ro.ro_rt); - } - return (ret); } #ifdef INET -void +static void pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct pf_state *s, struct pf_pdesc *pd) { struct mbuf *m0, *m1; - struct route iproute; - struct route *ro = NULL; - struct sockaddr_in *dst; + struct sockaddr_in dst; struct ip *ip; struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_src_node *sn = NULL; int error = 0; -#ifdef __FreeBSD__ int sw_csum; -#endif -#ifdef IPSEC - struct m_tag *mtag; -#endif /* IPSEC */ - if (m == NULL || *m == NULL || r == NULL || - (dir != PF_IN && dir != PF_OUT) || oifp == NULL) - panic("pf_route: invalid parameters"); + KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); + KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", + __func__)); -#ifdef __FreeBSD__ - if (pd->pf_mtag->routed++ > 3) { -#else - if ((*m)->m_pkthdr.pf.routed++ > 3) { -#endif + if ((pd->pf_mtag == NULL && + ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || + pd->pf_mtag->routed++ > 3) { m0 = *m; *m = NULL; - goto bad; + goto bad_locked; } if (r->rt == PF_DUPTO) { -#ifdef __FreeBSD__ - if ((m0 = m_dup(*m, M_DONTWAIT)) == NULL) -#else - if ((m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT)) == NULL) -#endif + if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { + if (s) + PF_STATE_UNLOCK(s); return; + } } else { - if ((r->rt == PF_REPLYTO) == (r->direction == dir)) + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { + if (s) + PF_STATE_UNLOCK(s); return; + } m0 = *m; } - if (m0->m_len < sizeof(struct ip)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_route: m0->m_len < sizeof(struct ip)\n")); - goto bad; - } - ip = mtod(m0, struct ip *); - ro = &iproute; - bzero((caddr_t)ro, sizeof(*ro)); - dst = satosin(&ro->ro_dst); - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = ip->ip_dst; + bzero(&dst, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(dst); + dst.sin_addr = ip->ip_dst; if (r->rt == PF_FASTROUTE) { -#ifdef __FreeBSD__ - in_rtalloc_ign(ro, 0, M_GETFIB(m0)); -#else - rtalloc(ro); -#endif - if (ro->ro_rt == 0) { -#ifdef __FreeBSD__ + struct rtentry *rt; + + if (s) + PF_STATE_UNLOCK(s); + rt = rtalloc1_fib(sintosa(&dst), 0, 0, M_GETFIB(m0)); + if (rt == NULL) { + RTFREE_LOCKED(rt); KMOD_IPSTAT_INC(ips_noroute); -#else - ipstat.ips_noroute++; -#endif + error = EHOSTUNREACH; goto bad; } - ifp = ro->ro_rt->rt_ifp; - ro->ro_rt->rt_use++; + ifp = rt->rt_ifp; + rt->rt_rmx.rmx_pksent++; - if (ro->ro_rt->rt_flags & RTF_GATEWAY) - dst = satosin(ro->ro_rt->rt_gateway); + if (rt->rt_flags & RTF_GATEWAY) + bcopy(satosin(rt->rt_gateway), &dst, sizeof(dst)); + RTFREE_LOCKED(rt); } else { if (TAILQ_EMPTY(&r->rpool.list)) { DPFPRINTF(PF_DEBUG_URGENT, - ("pf_route: TAILQ_EMPTY(&r->rpool.list)\n")); - goto bad; + ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); + goto bad_locked; } if (s == NULL) { pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, &naddr, NULL, &sn); if (!PF_AZERO(&naddr, AF_INET)) - dst->sin_addr.s_addr = naddr.v4.s_addr; + dst.sin_addr.s_addr = naddr.v4.s_addr; ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; } else { if (!PF_AZERO(&s->rt_addr, AF_INET)) - dst->sin_addr.s_addr = + dst.sin_addr.s_addr = s->rt_addr.v4.s_addr; ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; + PF_STATE_UNLOCK(s); } } if (ifp == NULL) goto bad; if (oifp != ifp) { -#ifdef __FreeBSD__ - PF_UNLOCK(); - if (pf_test(PF_OUT, ifp, &m0, NULL, NULL) != PF_PASS) { - PF_LOCK(); - goto bad; - } else if (m0 == NULL) { - PF_LOCK(); - goto done; - } - PF_LOCK(); -#else if (pf_test(PF_OUT, ifp, &m0, NULL) != PF_PASS) goto bad; else if (m0 == NULL) goto done; -#endif if (m0->m_len < sizeof(struct ip)) { DPFPRINTF(PF_DEBUG_URGENT, - ("pf_route: m0->m_len < sizeof(struct ip)\n")); + ("%s: m0->m_len < sizeof(struct ip)\n", __func__)); goto bad; } ip = mtod(m0, struct ip *); } -#ifdef __FreeBSD__ - /* Copied from FreeBSD 5.1-CURRENT ip_output. */ + if (ifp->if_flags & IFF_LOOPBACK) + m0->m_flags |= M_SKIP_FIREWALL; + + /* Back to host byte order. */ + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + + /* Copied from FreeBSD 10.0-CURRENT ip_output. */ m0->m_pkthdr.csum_flags |= CSUM_IP; sw_csum = m0->m_pkthdr.csum_flags & ~ifp->if_hwassist; if (sw_csum & CSUM_DELAY_DATA) { - /* - * XXX: in_delayed_cksum assumes HBO for ip->ip_len (at least) - */ - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); /* XXX: needed? */ in_delayed_cksum(m0); - HTONS(ip->ip_len); - HTONS(ip->ip_off); sw_csum &= ~CSUM_DELAY_DATA; } +#ifdef SCTP + if (sw_csum & CSUM_SCTP) { + sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2)); + sw_csum &= ~CSUM_SCTP; + } +#endif m0->m_pkthdr.csum_flags &= ifp->if_hwassist; - if (ntohs(ip->ip_len) <= ifp->if_mtu || - (ifp->if_hwassist & CSUM_FRAGMENT && - ((ip->ip_off & htons(IP_DF)) == 0))) { - /* - * ip->ip_len = htons(ip->ip_len); - * ip->ip_off = htons(ip->ip_off); - */ - ip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) { - /* From KAME */ - if (ip->ip_v == IPVERSION && - (ip->ip_hl << 2) == sizeof(*ip)) { - ip->ip_sum = in_cksum_hdr(ip); - } else { - ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); - } - } - PF_UNLOCK(); - error = (*ifp->if_output)(ifp, m0, sintosa(dst), ro); - PF_LOCK(); - goto done; - } -#else - /* Copied from ip_output. */ -#ifdef IPSEC /* - * If deferred crypto processing is needed, check that the - * interface supports it. + * If small enough for interface, or the interface will take + * care of the fragmentation for us, we can just send directly. */ - if ((mtag = m_tag_find(m0, PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED, NULL)) - != NULL && (ifp->if_capabilities & IFCAP_IPSEC) == 0) { - /* Notify IPsec to do its own crypto. */ - ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1)); - goto bad; - } -#endif /* IPSEC */ - - /* Catch routing changes wrt. hardware checksumming for TCP or UDP. */ - if (m0->m_pkthdr.csum_flags & M_TCPV4_CSUM_OUT) { - if (!(ifp->if_capabilities & IFCAP_CSUM_TCPv4) || - ifp->if_bridge != NULL) { - in_delayed_cksum(m0); - m0->m_pkthdr.csum_flags &= ~M_TCPV4_CSUM_OUT; /* Clr */ - } - } else if (m0->m_pkthdr.csum_flags & M_UDPV4_CSUM_OUT) { - if (!(ifp->if_capabilities & IFCAP_CSUM_UDPv4) || - ifp->if_bridge != NULL) { - in_delayed_cksum(m0); - m0->m_pkthdr.csum_flags &= ~M_UDPV4_CSUM_OUT; /* Clr */ - } - } - - if (ntohs(ip->ip_len) <= ifp->if_mtu) { + if (ip->ip_len <= ifp->if_mtu || + (m0->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0 || + ((ip->ip_off & IP_DF) == 0 && (ifp->if_hwassist & CSUM_FRAGMENT))) { + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); ip->ip_sum = 0; - if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && - ifp->if_bridge == NULL) { - m0->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT; -#ifdef __FreeBSD__ - KMOD_IPSTAT_INC(ips_outhwcsum); -#else - ipstat.ips_outhwcsum++; -#endif - } else + if (sw_csum & CSUM_DELAY_IP) ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); - /* Update relevant hardware checksum stats for TCP/UDP */ - if (m0->m_pkthdr.csum_flags & M_TCPV4_CSUM_OUT) - KMOD_TCPSTAT_INC(tcps_outhwcsum); - else if (m0->m_pkthdr.csum_flags & M_UDPV4_CSUM_OUT) - KMOD_UDPSTAT_INC(udps_outhwcsum); - error = (*ifp->if_output)(ifp, m0, sintosa(dst), NULL); + m0->m_flags &= ~(M_PROTOFLAGS); + error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); goto done; } -#endif - /* - * Too large for interface; fragment if possible. - * Must be able to put at least 8 bytes per fragment. - */ - if (ip->ip_off & htons(IP_DF)) { -#ifdef __FreeBSD__ + /* Balk when DF bit is set or the interface didn't support TSO. */ + if ((ip->ip_off & IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) { + error = EMSGSIZE; KMOD_IPSTAT_INC(ips_cantfrag); -#else - ipstat.ips_cantfrag++; -#endif if (r->rt != PF_DUPTO) { -#ifdef __FreeBSD__ - /* icmp_error() expects host byte ordering */ - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); - PF_UNLOCK(); - icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, - ifp->if_mtu); - PF_LOCK(); -#else icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, ifp->if_mtu); -#endif goto done; } else goto bad; } - m1 = m0; -#ifdef __FreeBSD__ - /* - * XXX: is cheaper + less error prone than own function - */ - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist, sw_csum); -#else - error = ip_fragment(m0, ifp, ifp->if_mtu); -#endif - if (error) { -#ifndef __FreeBSD__ /* ip_fragment does not do m_freem() on FreeBSD */ - m0 = NULL; -#endif + if (error) goto bad; - } - for (m0 = m1; m0; m0 = m1) { + for (; m0; m0 = m1) { m1 = m0->m_nextpkt; - m0->m_nextpkt = 0; -#ifdef __FreeBSD__ + m0->m_nextpkt = NULL; if (error == 0) { - PF_UNLOCK(); - error = (*ifp->if_output)(ifp, m0, sintosa(dst), - NULL); - PF_LOCK(); + m0->m_flags &= ~(M_PROTOFLAGS); + error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); } else -#else - if (error == 0) - error = (*ifp->if_output)(ifp, m0, sintosa(dst), - NULL); - else -#endif m_freem(m0); } if (error == 0) -#ifdef __FreeBSD__ KMOD_IPSTAT_INC(ips_fragmented); -#else - ipstat.ips_fragmented++; -#endif done: if (r->rt != PF_DUPTO) *m = NULL; - if (ro == &iproute && ro->ro_rt) - RTFREE(ro->ro_rt); return; +bad_locked: + if (s) + PF_STATE_UNLOCK(s); bad: m_freem(m0); goto done; @@ -6177,146 +5254,115 @@ bad: #endif /* INET */ #ifdef INET6 -void +static void pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct pf_state *s, struct pf_pdesc *pd) { struct mbuf *m0; - struct route_in6 ip6route; - struct route_in6 *ro; - struct sockaddr_in6 *dst; + struct sockaddr_in6 dst; struct ip6_hdr *ip6; struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_src_node *sn = NULL; - if (m == NULL || *m == NULL || r == NULL || - (dir != PF_IN && dir != PF_OUT) || oifp == NULL) - panic("pf_route6: invalid parameters"); + KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); + KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", + __func__)); -#ifdef __FreeBSD__ - if (pd->pf_mtag->routed++ > 3) { -#else - if ((*m)->m_pkthdr.pf.routed++ > 3) { -#endif + if ((pd->pf_mtag == NULL && + ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || + pd->pf_mtag->routed++ > 3) { m0 = *m; *m = NULL; - goto bad; + goto bad_locked; } if (r->rt == PF_DUPTO) { -#ifdef __FreeBSD__ - if ((m0 = m_dup(*m, M_DONTWAIT)) == NULL) -#else - if ((m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT)) == NULL) -#endif + if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { + if (s) + PF_STATE_UNLOCK(s); return; + } } else { - if ((r->rt == PF_REPLYTO) == (r->direction == dir)) + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { + if (s) + PF_STATE_UNLOCK(s); return; + } m0 = *m; } - if (m0->m_len < sizeof(struct ip6_hdr)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_route6: m0->m_len < sizeof(struct ip6_hdr)\n")); - goto bad; - } ip6 = mtod(m0, struct ip6_hdr *); - ro = &ip6route; - bzero((caddr_t)ro, sizeof(*ro)); - dst = (struct sockaddr_in6 *)&ro->ro_dst; - dst->sin6_family = AF_INET6; - dst->sin6_len = sizeof(*dst); - dst->sin6_addr = ip6->ip6_dst; + bzero(&dst, sizeof(dst)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(dst); + dst.sin6_addr = ip6->ip6_dst; /* Cheat. XXX why only in the v6 case??? */ if (r->rt == PF_FASTROUTE) { -#ifdef __FreeBSD__ + if (s) + PF_STATE_UNLOCK(s); m0->m_flags |= M_SKIP_FIREWALL; - PF_UNLOCK(); - ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); -#else - m0->m_pkthdr.pf.flags |= PF_TAG_GENERATED; ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); -#endif return; } if (TAILQ_EMPTY(&r->rpool.list)) { DPFPRINTF(PF_DEBUG_URGENT, - ("pf_route6: TAILQ_EMPTY(&r->rpool.list)\n")); - goto bad; + ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); + goto bad_locked; } if (s == NULL) { pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, &naddr, NULL, &sn); if (!PF_AZERO(&naddr, AF_INET6)) - PF_ACPY((struct pf_addr *)&dst->sin6_addr, + PF_ACPY((struct pf_addr *)&dst.sin6_addr, &naddr, AF_INET6); ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; } else { if (!PF_AZERO(&s->rt_addr, AF_INET6)) - PF_ACPY((struct pf_addr *)&dst->sin6_addr, + PF_ACPY((struct pf_addr *)&dst.sin6_addr, &s->rt_addr, AF_INET6); ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; } + + if (s) + PF_STATE_UNLOCK(s); + if (ifp == NULL) goto bad; if (oifp != ifp) { -#ifdef __FreeBSD__ - PF_UNLOCK(); - if (pf_test6(PF_OUT, ifp, &m0, NULL, NULL) != PF_PASS) { - PF_LOCK(); - goto bad; - } else if (m0 == NULL) { - PF_LOCK(); - goto done; - } - PF_LOCK(); -#else if (pf_test6(PF_OUT, ifp, &m0, NULL) != PF_PASS) goto bad; else if (m0 == NULL) goto done; -#endif if (m0->m_len < sizeof(struct ip6_hdr)) { DPFPRINTF(PF_DEBUG_URGENT, - ("pf_route6: m0->m_len < sizeof(struct ip6_hdr)\n")); + ("%s: m0->m_len < sizeof(struct ip6_hdr)\n", + __func__)); goto bad; } ip6 = mtod(m0, struct ip6_hdr *); } + if (ifp->if_flags & IFF_LOOPBACK) + m0->m_flags |= M_SKIP_FIREWALL; + /* * If the packet is too large for the outgoing interface, * send back an icmp6 error. */ - if (IN6_IS_SCOPE_EMBED(&dst->sin6_addr)) - dst->sin6_addr.s6_addr16[1] = htons(ifp->if_index); - if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) { -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - nd6_output(ifp, ifp, m0, dst, NULL); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - } else { + if (IN6_IS_SCOPE_EMBED(&dst.sin6_addr)) + dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) + nd6_output(ifp, ifp, m0, &dst, NULL); + else { in6_ifstat_inc(ifp, ifs6_in_toobig); -#ifdef __FreeBSD__ - if (r->rt != PF_DUPTO) { - PF_UNLOCK(); - icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); - PF_LOCK(); - } else -#else if (r->rt != PF_DUPTO) icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); else -#endif goto bad; } @@ -6325,13 +5371,15 @@ done: *m = NULL; return; +bad_locked: + if (s) + PF_STATE_UNLOCK(s); bad: m_freem(m0); goto done; } #endif /* INET6 */ -#ifdef __FreeBSD__ /* * FreeBSD supports cksum offloads for the following drivers. * em(4), fxp(4), ixgb(4), lge(4), ndis(4), nge(4), re(4), @@ -6350,7 +5398,7 @@ bad: * TCP/UDP layer. * Also, set csum_data to 0xffff to force cksum validation. */ -int +static int pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) { u_int16_t sum = 0; @@ -6370,7 +5418,7 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a } else { ip = mtod(m, struct ip *); sum = in_pseudo(ip->ip_src.s_addr, - ip->ip_dst.s_addr, htonl((u_short)len + + ip->ip_dst.s_addr, htonl((u_short)len + m->m_pkthdr.csum_data + IPPROTO_TCP)); } sum ^= 0xffff; @@ -6387,324 +5435,150 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a ip->ip_dst.s_addr, htonl((u_short)len + m->m_pkthdr.csum_data + IPPROTO_UDP)); } - sum ^= 0xffff; - ++hw_assist; - } - break; - case IPPROTO_ICMP: -#ifdef INET6 - case IPPROTO_ICMPV6: -#endif /* INET6 */ - break; - default: - return (1); - } - - if (!hw_assist) { - switch (af) { - case AF_INET: - if (p == IPPROTO_ICMP) { - if (m->m_len < off) - return (1); - m->m_data += off; - m->m_len -= off; - sum = in_cksum(m, len); - m->m_data -= off; - m->m_len += off; - } else { - if (m->m_len < sizeof(struct ip)) - return (1); - sum = in4_cksum(m, p, off, len); - } - break; -#ifdef INET6 - case AF_INET6: - if (m->m_len < sizeof(struct ip6_hdr)) - return (1); - sum = in6_cksum(m, p, off, len); - break; -#endif /* INET6 */ - default: - return (1); - } - } - if (sum) { - switch (p) { - case IPPROTO_TCP: - { - KMOD_TCPSTAT_INC(tcps_rcvbadsum); - break; - } - case IPPROTO_UDP: - { - KMOD_UDPSTAT_INC(udps_badsum); - break; - } -#ifdef INET - case IPPROTO_ICMP: - { - KMOD_ICMPSTAT_INC(icps_checksum); - break; - } -#endif -#ifdef INET6 - case IPPROTO_ICMPV6: - { - KMOD_ICMP6STAT_INC(icp6s_checksum); - break; - } -#endif /* INET6 */ - } - return (1); - } else { - if (p == IPPROTO_TCP || p == IPPROTO_UDP) { - m->m_pkthdr.csum_flags |= - (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); - m->m_pkthdr.csum_data = 0xffff; - } - } - return (0); -} -#else /* !__FreeBSD__ */ - -/* - * check protocol (tcp/udp/icmp/icmp6) checksum and set mbuf flag - * off is the offset where the protocol header starts - * len is the total length of protocol header plus payload - * returns 0 when the checksum is valid, otherwise returns 1. - */ -int -pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, - sa_family_t af) -{ - u_int16_t flag_ok, flag_bad; - u_int16_t sum; - - switch (p) { - case IPPROTO_TCP: - flag_ok = M_TCP_CSUM_IN_OK; - flag_bad = M_TCP_CSUM_IN_BAD; - break; - case IPPROTO_UDP: - flag_ok = M_UDP_CSUM_IN_OK; - flag_bad = M_UDP_CSUM_IN_BAD; - break; - case IPPROTO_ICMP: -#ifdef INET6 - case IPPROTO_ICMPV6: -#endif /* INET6 */ - flag_ok = flag_bad = 0; - break; - default: - return (1); - } - if (m->m_pkthdr.csum_flags & flag_ok) - return (0); - if (m->m_pkthdr.csum_flags & flag_bad) - return (1); - if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) - return (1); - if (m->m_pkthdr.len < off + len) - return (1); - switch (af) { -#ifdef INET - case AF_INET: - if (p == IPPROTO_ICMP) { - if (m->m_len < off) - return (1); - m->m_data += off; - m->m_len -= off; - sum = in_cksum(m, len); - m->m_data -= off; - m->m_len += off; - } else { - if (m->m_len < sizeof(struct ip)) - return (1); - sum = in4_cksum(m, p, off, len); + sum ^= 0xffff; + ++hw_assist; } break; -#endif /* INET */ + case IPPROTO_ICMP: #ifdef INET6 - case AF_INET6: - if (m->m_len < sizeof(struct ip6_hdr)) - return (1); - sum = in6_cksum(m, p, off, len); - break; + case IPPROTO_ICMPV6: #endif /* INET6 */ + break; default: return (1); } + + if (!hw_assist) { + switch (af) { + case AF_INET: + if (p == IPPROTO_ICMP) { + if (m->m_len < off) + return (1); + m->m_data += off; + m->m_len -= off; + sum = in_cksum(m, len); + m->m_data -= off; + m->m_len += off; + } else { + if (m->m_len < sizeof(struct ip)) + return (1); + sum = in4_cksum(m, p, off, len); + } + break; +#ifdef INET6 + case AF_INET6: + if (m->m_len < sizeof(struct ip6_hdr)) + return (1); + sum = in6_cksum(m, p, off, len); + break; +#endif /* INET6 */ + default: + return (1); + } + } if (sum) { - m->m_pkthdr.csum_flags |= flag_bad; switch (p) { case IPPROTO_TCP: + { KMOD_TCPSTAT_INC(tcps_rcvbadsum); break; + } case IPPROTO_UDP: + { KMOD_UDPSTAT_INC(udps_badsum); break; + } #ifdef INET case IPPROTO_ICMP: + { KMOD_ICMPSTAT_INC(icps_checksum); break; + } #endif #ifdef INET6 case IPPROTO_ICMPV6: + { KMOD_ICMP6STAT_INC(icp6s_checksum); break; + } #endif /* INET6 */ } return (1); + } else { + if (p == IPPROTO_TCP || p == IPPROTO_UDP) { + m->m_pkthdr.csum_flags |= + (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + m->m_pkthdr.csum_data = 0xffff; + } } - m->m_pkthdr.csum_flags |= flag_ok; return (0); } -#endif - -#ifndef __FreeBSD__ -struct pf_divert * -pf_find_divert(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) - return (NULL); - - return ((struct pf_divert *)(mtag + 1)); -} - -struct pf_divert * -pf_get_divert(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) { - mtag = m_tag_get(PACKET_TAG_PF_DIVERT, sizeof(struct pf_divert), - M_NOWAIT); - if (mtag == NULL) - return (NULL); - bzero(mtag + 1, sizeof(struct pf_divert)); - m_tag_prepend(m, mtag); - } - return ((struct pf_divert *)(mtag + 1)); -} -#endif #ifdef INET int -#ifdef __FreeBSD__ -pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, - struct ether_header *eh, struct inpcb *inp) -#else -pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, - struct ether_header *eh) -#endif +pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) { struct pfi_kif *kif; u_short action, reason = 0, log = 0; struct mbuf *m = *m0; -#ifdef __FreeBSD__ struct ip *h = NULL; struct m_tag *ipfwtag; struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; -#else - struct ip *h; - struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; -#endif struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; int off, dirndx, pqid = 0; -#ifdef __FreeBSD__ - PF_LOCK(); + M_ASSERTPKTHDR(m); + if (!V_pf_status.running) - { - PF_UNLOCK(); return (PF_PASS); - } -#else - if (!pf_status.running) - return (PF_PASS); -#endif memset(&pd, 0, sizeof(pd)); -#ifdef __FreeBSD__ - if ((pd.pf_mtag = pf_get_mtag(m)) == NULL) { - PF_UNLOCK(); - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_test: pf_get_mtag returned NULL\n")); - return (PF_DROP); - } -#endif -#ifndef __FreeBSD__ - if (ifp->if_type == IFT_CARP && ifp->if_carpdev) - kif = (struct pfi_kif *)ifp->if_carpdev->if_pf_kif; - else -#endif - kif = (struct pfi_kif *)ifp->if_pf_kif; + + kif = (struct pfi_kif *)ifp->if_pf_kif; if (kif == NULL) { -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif DPFPRINTF(PF_DEBUG_URGENT, ("pf_test: kif == NULL, if_xname %s\n", ifp->if_xname)); return (PF_DROP); } if (kif->pfik_flags & PFI_IFLAG_SKIP) -#ifdef __FreeBSD__ - { - PF_UNLOCK(); -#endif return (PF_PASS); -#ifdef __FreeBSD__ - } -#endif -#ifdef __FreeBSD__ - M_ASSERTPKTHDR(m); -#else -#ifdef DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) - panic("non-M_PKTHDR is passed to pf_test"); -#endif /* DIAGNOSTIC */ -#endif + if (m->m_flags & M_SKIP_FIREWALL) + return (PF_PASS); - if (m->m_pkthdr.len < (int)sizeof(*h)) { + if (m->m_pkthdr.len < (int)sizeof(struct ip)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); log = 1; goto done; } -#ifdef __FreeBSD__ - if (m->m_flags & M_SKIP_FIREWALL) { - PF_UNLOCK(); - return (PF_PASS); - } -#else - if (m->m_pkthdr.pf.flags & PF_TAG_GENERATED) - return (PF_PASS); -#endif - -#ifdef __FreeBSD__ + pd.pf_mtag = pf_find_mtag(m); + + PF_RULES_RLOCK(); + if (ip_divert_ptr != NULL && ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + goto done; + } pd.pf_mtag->flags |= PF_PACKET_LOOPED; m_tag_delete(m, ipfwtag); } - if (pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { + if (pd.pf_mtag && pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { m->m_flags |= M_FASTFWD_OURS; pd.pf_mtag->flags &= ~PF_FASTFWD_OURS_PRESENT; } - } else -#endif - /* We do IP header normalization and packet reassembly here */ - if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { + } else if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { + /* We do IP header normalization and packet reassembly here */ action = PF_DROP; goto done; } @@ -6712,7 +5586,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, h = mtod(m, struct ip *); off = h->ip_hl << 2; - if (off < (int)sizeof(*h)) { + if (off < (int)sizeof(struct ip)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); log = 1; @@ -6731,7 +5605,6 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, pd.af = AF_INET; pd.tos = h->ip_tos; pd.tot_len = ntohs(h->ip_len); - pd.eh = eh; /* handle fragments that didn't get reassembled by normalization */ if (h->ip_off & htons(IP_MF | IP_OFFMASK)) { @@ -6760,25 +5633,14 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, &ipintrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } @@ -6800,25 +5662,14 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, &ipintrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } @@ -6834,25 +5685,14 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, &ipintrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } @@ -6868,29 +5708,19 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, default: action = pf_test_state_other(&s, dir, kif, m, &pd); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, m, off, h, - &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, m, off, h, - &pd, &a, &ruleset, &ipintrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } done: + PF_RULES_RUNLOCK(); if (action == PF_PASS && h->ip_hl > 5 && !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) { action = PF_DROP; @@ -6900,23 +5730,20 @@ done: ("pf: dropping packet with ip options\n")); } - if ((s && s->tag) || r->rtableid >= 0) -#ifdef __FreeBSD__ - pf_tag_packet(m, s ? s->tag : 0, r->rtableid, pd.pf_mtag); -#else - pf_tag_packet(m, s ? s->tag : 0, r->rtableid); -#endif - - if (dir == PF_IN && s && s->key[PF_SK_STACK]) -#ifdef __FreeBSD__ - pd.pf_mtag->statekey = s->key[PF_SK_STACK]; -#else - m->m_pkthdr.pf.statekey = s->key[PF_SK_STACK]; -#endif + if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (r->rtableid >= 0) + M_SETFIB(m, r->rtableid); #ifdef ALTQ if (action == PF_PASS && r->qid) { -#ifdef __FreeBSD__ + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } if (pqid || (pd.tos & IPTOS_LOWDELAY)) pd.pf_mtag->qid = r->pqid; else @@ -6924,14 +5751,6 @@ done: /* add hints for ecn */ pd.pf_mtag->hdr = h; -#else - if (pqid || (pd.tos & IPTOS_LOWDELAY)) - m->m_pkthdr.pf.qid = r->pqid; - else - m->m_pkthdr.pf.qid = r->qid; - /* add hints for ecn */ - m->m_pkthdr.pf.hdr = h; -#endif } #endif /* ALTQ */ @@ -6945,35 +5764,37 @@ done: (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) -#ifdef __FreeBSD__ m->m_flags |= M_SKIP_FIREWALL; -#else - m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST; -#endif -#ifdef __FreeBSD__ - if (action == PF_PASS && r->divert.port && - ip_divert_ptr != NULL && !PACKET_LOOPED()) { + if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && + !PACKET_LOOPED(&pd)) { ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, - sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); + sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); if (ipfwtag != NULL) { ((struct ipfw_rule_ref *)(ipfwtag+1))->info = ntohs(r->divert.port); ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; - m_tag_prepend(m, ipfwtag); - - PF_UNLOCK(); + if (s) + PF_STATE_UNLOCK(s); + m_tag_prepend(m, ipfwtag); if (m->m_flags & M_FASTFWD_OURS) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate tag\n")); + } pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; m->m_flags &= ~M_FASTFWD_OURS; } - - ip_divert_ptr(*m0, - dir == PF_IN ? DIR_IN : DIR_OUT); + ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); *m0 = NULL; + return (action); } else { /* XXX: ipfw has the same behaviour! */ @@ -6984,17 +5805,6 @@ done: ("pf: failed to allocate divert tag\n")); } } -#else - if (dir == PF_IN && action == PF_PASS && r->divert.port) { - struct pf_divert *divert; - - if ((divert = pf_get_divert(m))) { - m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED; - divert->port = r->divert.port; - divert->addr.ipv4 = r->divert.addr.v4; - } - } -#endif if (log) { struct pf_rule *lr; @@ -7004,8 +5814,8 @@ done: lr = s->nat_rule.ptr; else lr = r; - PFLOG_PACKET(kif, h, m, AF_INET, dir, reason, lr, a, ruleset, - &pd); + PFLOG_PACKET(kif, m, AF_INET, dir, reason, lr, a, ruleset, &pd, + (s == NULL)); } kif->pfik_bytes[0][dir == PF_OUT][action != PF_PASS] += pd.tot_len; @@ -7038,11 +5848,7 @@ done: } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; -#ifdef __FreeBSD__ if (nr != NULL && r == &V_pf_default_rule) -#else - if (nr != NULL && r == &pf_default_rule) -#endif tr = nr; if (tr->src.addr.type == PF_ADDR_TABLE) pfr_update_stats(tr->src.addr.p.tbl, @@ -7068,96 +5874,53 @@ done: action = PF_PASS; break; default: - /* pf_route can free the mbuf causing *m0 to become NULL */ - if (r->rt) + /* pf_route() returns unlocked. */ + if (r->rt) { pf_route(m0, r, dir, kif->pfik_ifp, s, &pd); + return (action); + } break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif + if (s) + PF_STATE_UNLOCK(s); + return (action); } #endif /* INET */ #ifdef INET6 int -#ifdef __FreeBSD__ -pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, - struct ether_header *eh, struct inpcb *inp) -#else -pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, - struct ether_header *eh) -#endif +pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) { struct pfi_kif *kif; u_short action, reason = 0, log = 0; struct mbuf *m = *m0, *n = NULL; -#ifdef __FreeBSD__ struct ip6_hdr *h = NULL; struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; -#else - struct ip6_hdr *h; - struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; -#endif struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; int off, terminal = 0, dirndx, rh_cnt = 0; -#ifdef __FreeBSD__ - PF_LOCK(); - if (!V_pf_status.running) { - PF_UNLOCK(); - return (PF_PASS); - } -#else - if (!pf_status.running) + M_ASSERTPKTHDR(m); + + if (!V_pf_status.running) return (PF_PASS); -#endif memset(&pd, 0, sizeof(pd)); -#ifdef __FreeBSD__ - if ((pd.pf_mtag = pf_get_mtag(m)) == NULL) { - PF_UNLOCK(); - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_test: pf_get_mtag returned NULL\n")); - return (PF_DROP); - } -#endif -#ifndef __FreeBSD__ - if (ifp->if_type == IFT_CARP && ifp->if_carpdev) - kif = (struct pfi_kif *)ifp->if_carpdev->if_pf_kif; - else -#endif - kif = (struct pfi_kif *)ifp->if_pf_kif; + pd.pf_mtag = pf_find_mtag(m); + + if (pd.pf_mtag && pd.pf_mtag->flags & PF_TAG_GENERATED) + return (PF_PASS); + kif = (struct pfi_kif *)ifp->if_pf_kif; if (kif == NULL) { -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif DPFPRINTF(PF_DEBUG_URGENT, ("pf_test6: kif == NULL, if_xname %s\n", ifp->if_xname)); return (PF_DROP); } if (kif->pfik_flags & PFI_IFLAG_SKIP) -#ifdef __FreeBSD__ - { - PF_UNLOCK(); -#endif return (PF_PASS); -#ifdef __FreeBSD__ - } -#endif - -#ifdef __FreeBSD__ - M_ASSERTPKTHDR(m); -#else -#ifdef DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) - panic("non-M_PKTHDR is passed to pf_test6"); -#endif /* DIAGNOSTIC */ -#endif if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; @@ -7166,16 +5929,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, goto done; } -#ifdef __FreeBSD__ - if (pd.pf_mtag->flags & PF_TAG_GENERATED) { - PF_UNLOCK(); -#else - if (m->m_pkthdr.pf.flags & PF_TAG_GENERATED) -#endif - return (PF_PASS); -#ifdef __FreeBSD__ - } -#endif + PF_RULES_RLOCK(); /* We do IP header normalization and packet reassembly here */ if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { @@ -7208,7 +5962,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, pd.af = AF_INET6; pd.tos = 0; pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); - pd.eh = eh; off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr); pd.proto = h->ip6_nxt; @@ -7300,25 +6053,14 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, &ip6intrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } @@ -7340,25 +6082,14 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, &ip6intrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } @@ -7381,54 +6112,33 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, &ip6intrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } default: action = pf_test_state_other(&s, dir, kif, m, &pd); if (action == PF_PASS) { -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_update_state_ptr != NULL) pfsync_update_state_ptr(s); -#else - pfsync_update_state(s); -#endif -#endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) -#ifdef __FreeBSD__ - action = pf_test_rule(&r, &s, dir, kif, m, off, h, - &pd, &a, &ruleset, NULL, inp); -#else - action = pf_test_rule(&r, &s, dir, kif, m, off, h, - &pd, &a, &ruleset, &ip6intrq); -#endif + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); break; } done: + PF_RULES_RUNLOCK(); if (n != m) { m_freem(n); n = NULL; @@ -7444,37 +6154,26 @@ done: ("pf: dropping packet with dangerous v6 headers\n")); } - if ((s && s->tag) || r->rtableid >= 0) -#ifdef __FreeBSD__ - pf_tag_packet(m, s ? s->tag : 0, r->rtableid, pd.pf_mtag); -#else - pf_tag_packet(m, s ? s->tag : 0, r->rtableid); -#endif - - if (dir == PF_IN && s && s->key[PF_SK_STACK]) -#ifdef __FreeBSD__ - pd.pf_mtag->statekey = s->key[PF_SK_STACK]; -#else - m->m_pkthdr.pf.statekey = s->key[PF_SK_STACK]; -#endif + if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (r->rtableid >= 0) + M_SETFIB(m, r->rtableid); #ifdef ALTQ if (action == PF_PASS && r->qid) { -#ifdef __FreeBSD__ + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } if (pd.tos & IPTOS_LOWDELAY) pd.pf_mtag->qid = r->pqid; else pd.pf_mtag->qid = r->qid; /* add hints for ecn */ pd.pf_mtag->hdr = h; -#else - if (pd.tos & IPTOS_LOWDELAY) - m->m_pkthdr.pf.qid = r->pqid; - else - m->m_pkthdr.pf.qid = r->qid; - /* add hints for ecn */ - m->m_pkthdr.pf.hdr = h; -#endif } #endif /* ALTQ */ @@ -7483,27 +6182,11 @@ done: (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && IN6_IS_ADDR_LOOPBACK(&pd.dst->v6)) -#ifdef __FreeBSD__ m->m_flags |= M_SKIP_FIREWALL; -#else - m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST; -#endif -#ifdef __FreeBSD__ /* XXX: Anybody working on it?! */ if (r->divert.port) printf("pf: divert(9) is not supported for IPv6\n"); -#else - if (dir == PF_IN && action == PF_PASS && r->divert.port) { - struct pf_divert *divert; - - if ((divert = pf_get_divert(m))) { - m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED; - divert->port = r->divert.port; - divert->addr.ipv6 = r->divert.addr.v6; - } - } -#endif if (log) { struct pf_rule *lr; @@ -7513,8 +6196,8 @@ done: lr = s->nat_rule.ptr; else lr = r; - PFLOG_PACKET(kif, h, m, AF_INET6, dir, reason, lr, a, ruleset, - &pd); + PFLOG_PACKET(kif, m, AF_INET6, dir, reason, lr, a, ruleset, + &pd, (s == NULL)); } kif->pfik_bytes[1][dir == PF_OUT][action != PF_PASS] += pd.tot_len; @@ -7547,11 +6230,7 @@ done: } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; -#ifdef __FreeBSD__ if (nr != NULL && r == &V_pf_default_rule) -#else - if (nr != NULL && r == &pf_default_rule) -#endif tr = nr; if (tr->src.addr.type == PF_ADDR_TABLE) pfr_update_stats(tr->src.addr.p.tbl, @@ -7575,46 +6254,17 @@ done: action = PF_PASS; break; default: - /* pf_route6 can free the mbuf causing *m0 to become NULL */ - if (r->rt) + /* pf_route6() returns unlocked. */ + if (r->rt) { pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd); + return (action); + } break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif + if (s) + PF_STATE_UNLOCK(s); + return (action); } #endif /* INET6 */ - -int -pf_check_congestion(struct ifqueue *ifq) -{ -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ - return (0); -#else - if (ifq->ifq_congestion) - return (1); - else - return (0); -#endif -} - -/* - * must be called whenever any addressing information such as - * address, port, protocol has changed - */ -void -pf_pkt_addr_changed(struct mbuf *m) -{ -#ifdef __FreeBSD__ - struct pf_mtag *pf_tag; - - if ((pf_tag = pf_find_mtag(m)) != NULL) - pf_tag->statekey = NULL; -#else - m->m_pkthdr.pf.statekey = NULL; -#endif -} diff --git a/sys/contrib/pf/net/pf_if.c b/sys/contrib/pf/net/pf_if.c index b4491b8e44532..c010b654c0d02 100644 --- a/sys/contrib/pf/net/pf_if.c +++ b/sys/contrib/pf/net/pf_if.c @@ -32,137 +32,90 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#if defined(__FreeBSD__) -#include "opt_inet.h" -#include "opt_inet6.h" - #include + __FBSDID("$FreeBSD$"); -#endif + +#include "opt_inet.h" +#include "opt_inet6.h" #include -#include -#ifdef __FreeBSD__ -#include -#endif -#include -#include -#include -#include #include -#ifndef __FreeBSD__ -#include -#endif -#include -#ifndef __FreeBSD__ -#include -#endif +#include #include -#include -#ifdef __FreeBSD__ -#include -#endif - -#include -#include -#include -#include -#include - #include +#include -#ifdef INET6 -#include -#endif /* INET6 */ - -#ifdef __FreeBSD__ VNET_DEFINE(struct pfi_kif *, pfi_all); -VNET_DEFINE(uma_zone_t, pfi_addr_pl); -VNET_DEFINE(struct pfi_ifhead, pfi_ifs); -#define V_pfi_ifs VNET(pfi_ifs) -VNET_DEFINE(long, pfi_update); -#define V_pfi_update VNET(pfi_update) -VNET_DEFINE(struct pfr_addr *, pfi_buffer); +static VNET_DEFINE(long, pfi_update); +#define V_pfi_update VNET(pfi_update) +#define PFI_BUFFER_MAX 0x10000 + +static VNET_DEFINE(struct pfr_addr *, pfi_buffer); +static VNET_DEFINE(int, pfi_buffer_cnt); +static VNET_DEFINE(int, pfi_buffer_max); #define V_pfi_buffer VNET(pfi_buffer) -VNET_DEFINE(int, pfi_buffer_cnt); #define V_pfi_buffer_cnt VNET(pfi_buffer_cnt) -VNET_DEFINE(int, pfi_buffer_max); #define V_pfi_buffer_max VNET(pfi_buffer_max) -#else -struct pfi_kif *pfi_all = NULL; -struct pool pfi_addr_pl; -struct pfi_ifhead pfi_ifs; -long pfi_update = 1; -struct pfr_addr *pfi_buffer; -int pfi_buffer_cnt; -int pfi_buffer_max; -#endif -#ifdef __FreeBSD__ + eventhandler_tag pfi_attach_cookie; eventhandler_tag pfi_detach_cookie; eventhandler_tag pfi_attach_group_cookie; eventhandler_tag pfi_change_group_cookie; eventhandler_tag pfi_detach_group_cookie; eventhandler_tag pfi_ifaddr_event_cookie; -#endif - -void pfi_kif_update(struct pfi_kif *); -void pfi_dynaddr_update(struct pfi_dynaddr *dyn); -void pfi_table_update(struct pfr_ktable *, struct pfi_kif *, - int, int); -void pfi_kifaddr_update(void *); -void pfi_instance_add(struct ifnet *, int, int); -void pfi_address_add(struct sockaddr *, int, int); -int pfi_if_compare(struct pfi_kif *, struct pfi_kif *); -int pfi_skip_if(const char *, struct pfi_kif *); -int pfi_unmask(void *); -#ifdef __FreeBSD__ -void pfi_attach_ifnet_event(void * __unused, struct ifnet *); -void pfi_detach_ifnet_event(void * __unused, struct ifnet *); -void pfi_attach_group_event(void *, struct ifg_group *); -void pfi_change_group_event(void *, char *); -void pfi_detach_group_event(void *, struct ifg_group *); -void pfi_ifaddr_event(void * __unused, struct ifnet *); -#endif -RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); -RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); - -#define PFI_BUFFER_MAX 0x10000 -#define PFI_MTYPE M_IFADDR +static void pfi_attach_ifnet(struct ifnet *); +static void pfi_attach_ifgroup(struct ifg_group *); + +static void pfi_kif_update(struct pfi_kif *); +static void pfi_dynaddr_update(struct pfi_dynaddr *dyn); +static void pfi_table_update(struct pfr_ktable *, struct pfi_kif *, int, + int); +static void pfi_instance_add(struct ifnet *, int, int); +static void pfi_address_add(struct sockaddr *, int, int); +static int pfi_if_compare(struct pfi_kif *, struct pfi_kif *); +static int pfi_skip_if(const char *, struct pfi_kif *); +static int pfi_unmask(void *); +static void pfi_attach_ifnet_event(void * __unused, struct ifnet *); +static void pfi_detach_ifnet_event(void * __unused, struct ifnet *); +static void pfi_attach_group_event(void *, struct ifg_group *); +static void pfi_change_group_event(void *, char *); +static void pfi_detach_group_event(void *, struct ifg_group *); +static void pfi_ifaddr_event(void * __unused, struct ifnet *); + +RB_HEAD(pfi_ifhead, pfi_kif); +static RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); +static RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); +static VNET_DEFINE(struct pfi_ifhead, pfi_ifs); +#define V_pfi_ifs VNET(pfi_ifs) + +#define PFI_BUFFER_MAX 0x10000 +MALLOC_DEFINE(PFI_MTYPE, "pf_ifnet", "pf(4) interface database"); + +LIST_HEAD(pfi_list, pfi_kif); +static VNET_DEFINE(struct pfi_list, pfi_unlinked_kifs); +#define V_pfi_unlinked_kifs VNET(pfi_unlinked_kifs) +static struct mtx pfi_unlnkdkifs_mtx; void pfi_initialize(void) { -#ifdef __FreeBSD__ - if (V_pfi_all != NULL) /* already initialized */ -#else - if (pfi_all != NULL) /* already initialized */ -#endif - return; + struct ifg_group *ifg; + struct ifnet *ifp; + struct pfi_kif *kif; -#ifndef __FreeBSD__ - pool_init(&V_pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, 0, 0, - "pfiaddrpl", &pool_allocator_nointr); -#endif -#ifdef __FreeBSD__ V_pfi_buffer_max = 64; V_pfi_buffer = malloc(V_pfi_buffer_max * sizeof(*V_pfi_buffer), PFI_MTYPE, M_WAITOK); - if ((V_pfi_all = pfi_kif_get(IFG_ALL)) == NULL) -#else - pfi_buffer_max = 64; - pfi_buffer = malloc(pfi_buffer_max * sizeof(*pfi_buffer), - PFI_MTYPE, M_WAITOK); + mtx_init(&pfi_unlnkdkifs_mtx, "pf unlinked interfaces", NULL, MTX_DEF); - if ((pfi_all = pfi_kif_get(IFG_ALL)) == NULL) -#endif - panic("pfi_kif_get for pfi_all failed"); -#ifdef __FreeBSD__ - struct ifg_group *ifg; - struct ifnet *ifp; + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + PF_RULES_WLOCK(); + V_pfi_all = pfi_kif_attach(kif, IFG_ALL); + PF_RULES_WUNLOCK(); IFNET_RLOCK(); TAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) @@ -183,144 +136,131 @@ pfi_initialize(void) pfi_detach_group_event, curvnet, EVENTHANDLER_PRI_ANY); pfi_ifaddr_event_cookie = EVENTHANDLER_REGISTER(ifaddr_event, pfi_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY); -#endif } -#ifdef __FreeBSD__ void pfi_cleanup(void) { struct pfi_kif *p; - PF_UNLOCK(); EVENTHANDLER_DEREGISTER(ifnet_arrival_event, pfi_attach_cookie); EVENTHANDLER_DEREGISTER(ifnet_departure_event, pfi_detach_cookie); EVENTHANDLER_DEREGISTER(group_attach_event, pfi_attach_group_cookie); EVENTHANDLER_DEREGISTER(group_change_event, pfi_change_group_cookie); EVENTHANDLER_DEREGISTER(group_detach_event, pfi_detach_group_cookie); EVENTHANDLER_DEREGISTER(ifaddr_event, pfi_ifaddr_event_cookie); - PF_LOCK(); V_pfi_all = NULL; while ((p = RB_MIN(pfi_ifhead, &V_pfi_ifs))) { - if (p->pfik_rules || p->pfik_states) { - printf("pfi_cleanup: dangling refs for %s\n", - p->pfik_name); - } - RB_REMOVE(pfi_ifhead, &V_pfi_ifs, p); free(p, PFI_MTYPE); } + while ((p = LIST_FIRST(&V_pfi_unlinked_kifs))) { + LIST_REMOVE(p, pfik_list); + free(p, PFI_MTYPE); + } + + mtx_destroy(&pfi_unlnkdkifs_mtx); + free(V_pfi_buffer, PFI_MTYPE); } -#endif struct pfi_kif * -pfi_kif_get(const char *kif_name) +pfi_kif_find(const char *kif_name) { - struct pfi_kif *kif; - struct pfi_kif_cmp s; + struct pfi_kif_cmp s; + + PF_RULES_ASSERT(); bzero(&s, sizeof(s)); strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name)); -#ifdef __FreeBSD__ - if ((kif = RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&s)) != NULL) -#else - if ((kif = RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&s)) != NULL) -#endif - return (kif); - /* create new one */ -#ifdef __FreeBSD__ - if ((kif = malloc(sizeof(*kif), PFI_MTYPE, M_NOWAIT | M_ZERO)) == NULL) -#else - if ((kif = malloc(sizeof(*kif), PFI_MTYPE, M_DONTWAIT|M_ZERO)) == NULL) -#endif - return (NULL); + return (RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&s)); +} +struct pfi_kif * +pfi_kif_attach(struct pfi_kif *kif, const char *kif_name) +{ + struct pfi_kif *kif1; + + PF_RULES_WASSERT(); + KASSERT(kif != NULL, ("%s: null kif", __func__)); + + kif1 = pfi_kif_find(kif_name); + if (kif1 != NULL) { + free(kif, PFI_MTYPE); + return (kif1); + } + + bzero(kif, sizeof(*kif)); strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name)); -#ifdef __FreeBSD__ /* * It seems that the value of time_second is in unintialzied state * when pf sets interface statistics clear time in boot phase if pf * was statically linked to kernel. Instead of setting the bogus * time value have pfi_get_ifaces handle this case. In - * pfi_get_ifaces it uses boottime.tv_sec if it sees the time is 0. + * pfi_get_ifaces it uses time_second if it sees the time is 0. */ kif->pfik_tzero = time_second > 1 ? time_second : 0; -#else - kif->pfik_tzero = time_second; -#endif TAILQ_INIT(&kif->pfik_dynaddrs); -#ifdef __FreeBSD__ RB_INSERT(pfi_ifhead, &V_pfi_ifs, kif); -#else - RB_INSERT(pfi_ifhead, &pfi_ifs, kif); -#endif return (kif); } void -pfi_kif_ref(struct pfi_kif *kif, enum pfi_kif_refs what) +pfi_kif_ref(struct pfi_kif *kif) { - switch (what) { - case PFI_KIF_REF_RULE: - kif->pfik_rules++; - break; - case PFI_KIF_REF_STATE: - kif->pfik_states++; - break; - default: - panic("pfi_kif_ref with unknown type"); - } + + PF_RULES_WASSERT(); + kif->pfik_rulerefs++; } void -pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what) +pfi_kif_unref(struct pfi_kif *kif) { - if (kif == NULL) - return; - switch (what) { - case PFI_KIF_REF_NONE: - break; - case PFI_KIF_REF_RULE: - if (kif->pfik_rules <= 0) { - printf("pfi_kif_unref: rules refcount <= 0\n"); - return; - } - kif->pfik_rules--; - break; - case PFI_KIF_REF_STATE: - if (kif->pfik_states <= 0) { - printf("pfi_kif_unref: state refcount <= 0\n"); - return; - } - kif->pfik_states--; - break; - default: - panic("pfi_kif_unref with unknown type"); - } + PF_RULES_WASSERT(); + KASSERT(kif->pfik_rulerefs > 0, ("%s: %p has zero refs", __func__, kif)); -#ifdef __FreeBSD__ - if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == V_pfi_all) -#else - if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == pfi_all) -#endif + kif->pfik_rulerefs--; + + if (kif->pfik_rulerefs > 0) return; - if (kif->pfik_rules || kif->pfik_states) + /* kif referencing an existing ifnet or group should exist. */ + if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == V_pfi_all) return; -#ifdef __FreeBSD__ RB_REMOVE(pfi_ifhead, &V_pfi_ifs, kif); -#else - RB_REMOVE(pfi_ifhead, &pfi_ifs, kif); -#endif - free(kif, PFI_MTYPE); + + kif->pfik_flags |= PFI_IFLAG_REFS; + + mtx_lock(&pfi_unlnkdkifs_mtx); + LIST_INSERT_HEAD(&V_pfi_unlinked_kifs, kif, pfik_list); + mtx_unlock(&pfi_unlnkdkifs_mtx); +} + +void +pfi_kif_purge(void) +{ + struct pfi_kif *kif, *kif1; + + /* + * Do naive mark-and-sweep garbage collecting of old kifs. + * Reference flag is raised by pf_purge_expired_states(). + */ + mtx_lock(&pfi_unlnkdkifs_mtx); + LIST_FOREACH_SAFE(kif, &V_pfi_unlinked_kifs, pfik_list, kif1) { + if (!(kif->pfik_flags & PFI_IFLAG_REFS)) { + LIST_REMOVE(kif, pfik_list); + free(kif, PFI_MTYPE); + } else + kif->pfik_flags &= ~PFI_IFLAG_REFS; + } + mtx_unlock(&pfi_unlnkdkifs_mtx); } int @@ -332,6 +272,7 @@ pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif) return (1); if (rule_kif->pfik_group != NULL) + /* XXXGL: locking? */ TAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next) if (p->ifgl_group == rule_kif->pfik_group) return (1); @@ -339,125 +280,38 @@ pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif) return (0); } -void +static void pfi_attach_ifnet(struct ifnet *ifp) { - struct pfi_kif *kif; - int s; + struct pfi_kif *kif; + + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - pfi_initialize(); - s = splsoftnet(); -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); V_pfi_update++; -#else - pfi_update++; -#endif - if ((kif = pfi_kif_get(ifp->if_xname)) == NULL) - panic("pfi_kif_get failed"); + kif = pfi_kif_attach(kif, ifp->if_xname); kif->pfik_ifp = ifp; - ifp->if_pf_kif = (caddr_t)kif; - -#ifndef __FreeBSD__ - if ((kif->pfik_ah_cookie = hook_establish(ifp->if_addrhooks, 1, - pfi_kifaddr_update, kif)) == NULL) - panic("pfi_attach_ifnet: cannot allocate '%s' address hook", - ifp->if_xname); -#endif + ifp->if_pf_kif = kif; pfi_kif_update(kif); - - splx(s); + PF_RULES_WUNLOCK(); } -void -pfi_detach_ifnet(struct ifnet *ifp) -{ - int s; - struct pfi_kif *kif; - - if ((kif = (struct pfi_kif *)ifp->if_pf_kif) == NULL) - return; - - s = splsoftnet(); -#ifdef __FreeBSD__ - V_pfi_update++; -#else - pfi_update++; -#endif -#ifndef __FreeBSD__ - hook_disestablish(ifp->if_addrhooks, kif->pfik_ah_cookie); -#endif - pfi_kif_update(kif); - - kif->pfik_ifp = NULL; - ifp->if_pf_kif = NULL; - pfi_kif_unref(kif, PFI_KIF_REF_NONE); - splx(s); -} - -void +static void pfi_attach_ifgroup(struct ifg_group *ifg) { - struct pfi_kif *kif; - int s; + struct pfi_kif *kif; - pfi_initialize(); - s = splsoftnet(); -#ifdef __FreeBSD__ - V_pfi_update++; -#else - pfi_update++; -#endif - if ((kif = pfi_kif_get(ifg->ifg_group)) == NULL) - panic("pfi_kif_get failed"); - - kif->pfik_group = ifg; - ifg->ifg_pf_kif = (caddr_t)kif; - - splx(s); -} - -void -pfi_detach_ifgroup(struct ifg_group *ifg) -{ - int s; - struct pfi_kif *kif; - - if ((kif = (struct pfi_kif *)ifg->ifg_pf_kif) == NULL) - return; - - s = splsoftnet(); -#ifdef __FreeBSD__ - V_pfi_update++; -#else - pfi_update++; -#endif - - kif->pfik_group = NULL; - ifg->ifg_pf_kif = NULL; - pfi_kif_unref(kif, PFI_KIF_REF_NONE); - splx(s); -} - -void -pfi_group_change(const char *group) -{ - struct pfi_kif *kif; - int s; + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - s = splsoftnet(); -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); V_pfi_update++; -#else - pfi_update++; -#endif - if ((kif = pfi_kif_get(group)) == NULL) - panic("pfi_kif_get failed"); - - pfi_kif_update(kif); + kif = pfi_kif_attach(kif, ifg->ifg_group); - splx(s); + kif->pfik_group = ifg; + ifg->ifg_pf_kif = kif; + PF_RULES_WUNLOCK(); } int @@ -501,28 +355,27 @@ pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) struct pfi_dynaddr *dyn; char tblname[PF_TABLE_NAME_SIZE]; struct pf_ruleset *ruleset = NULL; - int s, rv = 0; + struct pfi_kif *kif; + int rv = 0; - if (aw->type != PF_ADDR_DYNIFTL) - return (0); -#ifdef __FreeBSD__ - if ((dyn = pool_get(&V_pfi_addr_pl, PR_NOWAIT | PR_ZERO)) -#else - if ((dyn = pool_get(&pfi_addr_pl, PR_WAITOK | PR_LIMITFAIL | PR_ZERO)) -#endif - == NULL) - return (1); + PF_RULES_WASSERT(); + KASSERT(aw->type == PF_ADDR_DYNIFTL, ("%s: type %u", + __func__, aw->type)); + KASSERT(aw->p.dyn == NULL, ("%s: dyn is %p", __func__, aw->p.dyn)); + + if ((dyn = malloc(sizeof(*dyn), PFI_MTYPE, M_NOWAIT | M_ZERO)) == NULL) + return (ENOMEM); + + if ((kif = malloc(sizeof(*kif), PFI_MTYPE, M_NOWAIT)) == NULL) { + free(dyn, PFI_MTYPE); + return (ENOMEM); + } - s = splsoftnet(); if (!strcmp(aw->v.ifname, "self")) - dyn->pfid_kif = pfi_kif_get(IFG_ALL); + dyn->pfid_kif = pfi_kif_attach(kif, IFG_ALL); else - dyn->pfid_kif = pfi_kif_get(aw->v.ifname); - if (dyn->pfid_kif == NULL) { - rv = 1; - goto _bad; - } - pfi_kif_ref(dyn->pfid_kif, PFI_KIF_REF_RULE); + dyn->pfid_kif = pfi_kif_attach(kif, aw->v.ifname); + pfi_kif_ref(dyn->pfid_kif); dyn->pfid_net = pfi_unmask(&aw->v.a.mask); if (af == AF_INET && dyn->pfid_net == 32) @@ -540,12 +393,12 @@ pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) snprintf(tblname + strlen(tblname), sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net); if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == NULL) { - rv = 1; + rv = ENOMEM; goto _bad; } - if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, 1)) == NULL) { - rv = 1; + if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname)) == NULL) { + rv = ENOMEM; goto _bad; } @@ -556,7 +409,7 @@ pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); aw->p.dyn = dyn; pfi_kif_update(dyn->pfid_kif); - splx(s); + return (0); _bad: @@ -565,108 +418,92 @@ _bad: if (ruleset != NULL) pf_remove_if_empty_ruleset(ruleset); if (dyn->pfid_kif != NULL) - pfi_kif_unref(dyn->pfid_kif, PFI_KIF_REF_RULE); -#ifdef __FreeBSD__ - pool_put(&V_pfi_addr_pl, dyn); -#else - pool_put(&pfi_addr_pl, dyn); -#endif - splx(s); + pfi_kif_unref(dyn->pfid_kif); + free(dyn, PFI_MTYPE); + return (rv); } -void +static void pfi_kif_update(struct pfi_kif *kif) { struct ifg_list *ifgl; struct pfi_dynaddr *p; + PF_RULES_WASSERT(); + /* update all dynaddr */ TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry) pfi_dynaddr_update(p); /* again for all groups kif is member of */ - if (kif->pfik_ifp != NULL) + if (kif->pfik_ifp != NULL) { + IF_ADDR_RLOCK(kif->pfik_ifp); TAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next) pfi_kif_update((struct pfi_kif *) ifgl->ifgl_group->ifg_pf_kif); + IF_ADDR_RUNLOCK(kif->pfik_ifp); + } } -void +static void pfi_dynaddr_update(struct pfi_dynaddr *dyn) { struct pfi_kif *kif; struct pfr_ktable *kt; - if (dyn == NULL || dyn->pfid_kif == NULL || dyn->pfid_kt == NULL) - panic("pfi_dynaddr_update"); + PF_RULES_WASSERT(); + KASSERT(dyn && dyn->pfid_kif && dyn->pfid_kt, + ("%s: bad argument", __func__)); kif = dyn->pfid_kif; kt = dyn->pfid_kt; -#ifdef __FreeBSD__ if (kt->pfrkt_larg != V_pfi_update) { -#else - if (kt->pfrkt_larg != pfi_update) { -#endif /* this table needs to be brought up-to-date */ pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags); -#ifdef __FreeBSD__ kt->pfrkt_larg = V_pfi_update; -#else - kt->pfrkt_larg = pfi_update; -#endif } pfr_dynaddr_update(kt, dyn); } -void +static void pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags) { int e, size2 = 0; struct ifg_member *ifgm; -#ifdef __FreeBSD__ V_pfi_buffer_cnt = 0; -#else - pfi_buffer_cnt = 0; -#endif if (kif->pfik_ifp != NULL) pfi_instance_add(kif->pfik_ifp, net, flags); - else if (kif->pfik_group != NULL) + else if (kif->pfik_group != NULL) { + IFNET_RLOCK(); TAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next) pfi_instance_add(ifgm->ifgm_ifp, net, flags); + IFNET_RUNLOCK(); + } -#ifdef __FreeBSD__ if ((e = pfr_set_addrs(&kt->pfrkt_t, V_pfi_buffer, V_pfi_buffer_cnt, &size2, NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK))) - printf("pfi_table_update: cannot set %d new addresses " - "into table %s: %d\n", V_pfi_buffer_cnt, kt->pfrkt_name, e); -#else - if ((e = pfr_set_addrs(&kt->pfrkt_t, pfi_buffer, pfi_buffer_cnt, &size2, - NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK))) - printf("pfi_table_update: cannot set %d new addresses " - "into table %s: %d\n", pfi_buffer_cnt, kt->pfrkt_name, e); -#endif + printf("%s: cannot set %d new addresses into table %s: %d\n", + __func__, V_pfi_buffer_cnt, kt->pfrkt_name, e); } -void +static void pfi_instance_add(struct ifnet *ifp, int net, int flags) { struct ifaddr *ia; int got4 = 0, got6 = 0; int net2, af; - if (ifp == NULL) - return; - TAILQ_FOREACH(ia, &ifp->if_addrlist, ifa_list) { + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_list) { if (ia->ifa_addr == NULL) continue; af = ia->ifa_addr->sa_family; if (af != AF_INET && af != AF_INET6) continue; -#ifdef __FreeBSD__ /* * XXX: For point-to-point interfaces, (ifname:0) and IPv4, * jump over addresses without a proper route to work @@ -677,7 +514,6 @@ pfi_instance_add(struct ifnet *ifp, int net, int flags) !(ia->ifa_flags & IFA_ROUTE) && (flags & PFI_AFLAG_NOALIAS) && (af == AF_INET)) continue; -#endif if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6) continue; if ((flags & PFI_AFLAG_BROADCAST) && @@ -718,67 +554,39 @@ pfi_instance_add(struct ifnet *ifp, int net, int flags) else pfi_address_add(ia->ifa_addr, af, net2); } + IF_ADDR_RUNLOCK(ifp); } -void +static void pfi_address_add(struct sockaddr *sa, int af, int net) { struct pfr_addr *p; int i; -#ifdef __FreeBSD__ if (V_pfi_buffer_cnt >= V_pfi_buffer_max) { int new_max = V_pfi_buffer_max * 2; -#else - if (pfi_buffer_cnt >= pfi_buffer_max) { - int new_max = pfi_buffer_max * 2; -#endif if (new_max > PFI_BUFFER_MAX) { - printf("pfi_address_add: address buffer full (%d/%d)\n", -#ifdef __FreeBSD__ + printf("%s: address buffer full (%d/%d)\n", __func__, V_pfi_buffer_cnt, PFI_BUFFER_MAX); -#else - pfi_buffer_cnt, PFI_BUFFER_MAX); -#endif return; } p = malloc(new_max * sizeof(*V_pfi_buffer), PFI_MTYPE, -#ifdef __FreeBSD__ M_NOWAIT); -#else - M_DONTWAIT); -#endif if (p == NULL) { - printf("pfi_address_add: no memory to grow buffer " -#ifdef __FreeBSD__ - "(%d/%d)\n", V_pfi_buffer_cnt, PFI_BUFFER_MAX); -#else - "(%d/%d)\n", pfi_buffer_cnt, PFI_BUFFER_MAX); -#endif + printf("%s: no memory to grow buffer (%d/%d)\n", + __func__, V_pfi_buffer_cnt, PFI_BUFFER_MAX); return; } -#ifdef __FreeBSD__ memcpy(V_pfi_buffer, p, V_pfi_buffer_cnt * sizeof(*V_pfi_buffer)); /* no need to zero buffer */ free(V_pfi_buffer, PFI_MTYPE); V_pfi_buffer = p; V_pfi_buffer_max = new_max; -#else - memcpy(pfi_buffer, p, pfi_buffer_cnt * sizeof(*pfi_buffer)); - /* no need to zero buffer */ - free(pfi_buffer, PFI_MTYPE); - pfi_buffer = p; - pfi_buffer_max = new_max; -#endif } if (af == AF_INET && net > 32) net = 128; -#ifdef __FreeBSD__ p = V_pfi_buffer + V_pfi_buffer_cnt++; -#else - p = pfi_buffer + pfi_buffer_cnt++; -#endif bzero(p, sizeof(*p)); p->pfra_af = af; p->pfra_net = net; @@ -797,55 +605,31 @@ pfi_address_add(struct sockaddr *sa, int af, int net) } void -pfi_dynaddr_remove(struct pf_addr_wrap *aw) +pfi_dynaddr_remove(struct pfi_dynaddr *dyn) { - int s; - if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL || - aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL) - return; + KASSERT(dyn->pfid_kif != NULL, ("%s: null pfid_kif", __func__)); + KASSERT(dyn->pfid_kt != NULL, ("%s: null pfid_kt", __func__)); - s = splsoftnet(); - TAILQ_REMOVE(&aw->p.dyn->pfid_kif->pfik_dynaddrs, aw->p.dyn, entry); - pfi_kif_unref(aw->p.dyn->pfid_kif, PFI_KIF_REF_RULE); - aw->p.dyn->pfid_kif = NULL; - pfr_detach_table(aw->p.dyn->pfid_kt); - aw->p.dyn->pfid_kt = NULL; -#ifdef __FreeBSD__ - pool_put(&V_pfi_addr_pl, aw->p.dyn); -#else - pool_put(&pfi_addr_pl, aw->p.dyn); -#endif - aw->p.dyn = NULL; - splx(s); + TAILQ_REMOVE(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); + pfi_kif_unref(dyn->pfid_kif); + pfr_detach_table(dyn->pfid_kt); + free(dyn, PFI_MTYPE); } void pfi_dynaddr_copyout(struct pf_addr_wrap *aw) { - if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL || - aw->p.dyn->pfid_kif == NULL) - return; - aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6; -} -void -pfi_kifaddr_update(void *v) -{ - int s; - struct pfi_kif *kif = (struct pfi_kif *)v; + KASSERT(aw->type == PF_ADDR_DYNIFTL, + ("%s: type %u", __func__, aw->type)); - s = splsoftnet(); -#ifdef __FreeBSD__ - V_pfi_update++; -#else - pfi_update++; -#endif - pfi_kif_update(kif); - splx(s); + if (aw->p.dyn == NULL || aw->p.dyn->pfid_kif == NULL) + return; + aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6; } -int +static int pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q) { return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ)); @@ -858,19 +642,13 @@ pfi_update_status(const char *name, struct pf_status *pfs) struct pfi_kif_cmp key; struct ifg_member p_member, *ifgm; TAILQ_HEAD(, ifg_member) ifg_members; - int i, j, k, s; + int i, j, k; strlcpy(key.pfik_name, name, sizeof(key.pfik_name)); - s = splsoftnet(); -#ifdef __FreeBSD__ p = RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&key); -#else - p = RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&key); -#endif - if (p == NULL) { - splx(s); + if (p == NULL) return; - } + if (p->pfik_group != NULL) { bcopy(&p->pfik_group->ifg_members, &ifg_members, sizeof(ifg_members)); @@ -906,56 +684,29 @@ pfi_update_status(const char *name, struct pf_status *pfs) p->pfik_bytes[i][j][k]; } } - splx(s); } -int +void pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size) { struct pfi_kif *p, *nextp; - int s, n = 0; -#ifdef __FreeBSD__ - int error; -#endif + int n = 0; - s = splsoftnet(); -#ifdef __FreeBSD__ for (p = RB_MIN(pfi_ifhead, &V_pfi_ifs); p; p = nextp) { nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); -#else - for (p = RB_MIN(pfi_ifhead, &pfi_ifs); p; p = nextp) { - nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p); -#endif if (pfi_skip_if(name, p)) continue; - if (*size > n++) { - if (!p->pfik_tzero) - p->pfik_tzero = time_second; - pfi_kif_ref(p, PFI_KIF_REF_RULE); -#ifdef __FreeBSD__ - PF_COPYOUT(p, buf++, sizeof(*buf), error); - if (error) { -#else - if (copyout(p, buf++, sizeof(*buf))) { -#endif - pfi_kif_unref(p, PFI_KIF_REF_RULE); - splx(s); - return (EFAULT); - } -#ifdef __FreeBSD__ - nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); -#else - nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p); -#endif - pfi_kif_unref(p, PFI_KIF_REF_RULE); - } + if (*size <= n++) + break; + if (!p->pfik_tzero) + p->pfik_tzero = time_second; + bcopy(p, buf++, sizeof(*buf)); + nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); } - splx(s); *size = n; - return (0); } -int +static int pfi_skip_if(const char *filter, struct pfi_kif *p) { int n; @@ -978,19 +729,12 @@ int pfi_set_flags(const char *name, int flags) { struct pfi_kif *p; - int s; - s = splsoftnet(); -#ifdef __FreeBSD__ RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) { -#else - RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { -#endif if (pfi_skip_if(name, p)) continue; p->pfik_flags |= flags; } - splx(s); return (0); } @@ -998,24 +742,17 @@ int pfi_clear_flags(const char *name, int flags) { struct pfi_kif *p; - int s; - s = splsoftnet(); -#ifdef __FreeBSD__ RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) { -#else - RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { -#endif if (pfi_skip_if(name, p)) continue; p->pfik_flags &= ~flags; } - splx(s); return (0); } /* from pf_print_state.c */ -int +static int pfi_unmask(void *addr) { struct pf_addr *m = addr; @@ -1034,77 +771,89 @@ pfi_unmask(void *addr) return (b); } -#ifdef __FreeBSD__ -void +static void pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) { CURVNET_SET(ifp->if_vnet); - PF_LOCK(); pfi_attach_ifnet(ifp); #ifdef ALTQ + PF_RULES_WLOCK(); pf_altq_ifnet_event(ifp, 0); + PF_RULES_WUNLOCK(); #endif - PF_UNLOCK(); CURVNET_RESTORE(); } -void +static void pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) { + struct pfi_kif *kif = (struct pfi_kif *)ifp->if_pf_kif; CURVNET_SET(ifp->if_vnet); - PF_LOCK(); - pfi_detach_ifnet(ifp); + PF_RULES_WLOCK(); + V_pfi_update++; + pfi_kif_update(kif); + + kif->pfik_ifp = NULL; + ifp->if_pf_kif = NULL; #ifdef ALTQ pf_altq_ifnet_event(ifp, 1); #endif - PF_UNLOCK(); + PF_RULES_WUNLOCK(); CURVNET_RESTORE(); } -void +static void pfi_attach_group_event(void *arg , struct ifg_group *ifg) { CURVNET_SET((struct vnet *)arg); - PF_LOCK(); pfi_attach_ifgroup(ifg); - PF_UNLOCK(); CURVNET_RESTORE(); } -void +static void pfi_change_group_event(void *arg, char *gname) { + struct pfi_kif *kif; + + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); CURVNET_SET((struct vnet *)arg); - PF_LOCK(); - pfi_group_change(gname); - PF_UNLOCK(); + PF_RULES_WLOCK(); + V_pfi_update++; + kif = pfi_kif_attach(kif, gname); + pfi_kif_update(kif); + PF_RULES_WUNLOCK(); CURVNET_RESTORE(); } -void +static void pfi_detach_group_event(void *arg, struct ifg_group *ifg) { + struct pfi_kif *kif = (struct pfi_kif *)ifg->ifg_pf_kif; CURVNET_SET((struct vnet *)arg); - PF_LOCK(); - pfi_detach_ifgroup(ifg); - PF_UNLOCK(); + PF_RULES_WLOCK(); + V_pfi_update++; + + kif->pfik_group = NULL; + ifg->ifg_pf_kif = NULL; + PF_RULES_WUNLOCK(); CURVNET_RESTORE(); } -void +static void pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp) { CURVNET_SET(ifp->if_vnet); - PF_LOCK(); - if (ifp && ifp->if_pf_kif) - pfi_kifaddr_update(ifp->if_pf_kif); - PF_UNLOCK(); + PF_RULES_WLOCK(); + if (ifp && ifp->if_pf_kif) { + V_pfi_update++; + pfi_kif_update(ifp->if_pf_kif); + } + PF_RULES_WUNLOCK(); CURVNET_RESTORE(); } -#endif /* __FreeBSD__ */ diff --git a/sys/contrib/pf/net/pf_ioctl.c b/sys/contrib/pf/net/pf_ioctl.c index 6b5d8f548bbf5..032f051277159 100644 --- a/sys/contrib/pf/net/pf_ioctl.c +++ b/sys/contrib/pf/net/pf_ioctl.c @@ -35,7 +35,6 @@ * */ -#ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); @@ -44,187 +43,116 @@ __FBSDID("$FreeBSD$"); #include "opt_bpf.h" #include "opt_pf.h" -#define NPFSYNC 1 - -#ifdef DEV_PFLOG -#define NPFLOG DEV_PFLOG -#else -#define NPFLOG 0 -#endif - -#else /* !__FreeBSD__ */ -#include "pfsync.h" -#include "pflog.h" -#endif /* __FreeBSD__ */ - #include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include -#include -#ifdef __FreeBSD__ -#include +#include +#include #include +#include +#include +#include #include -#include #include +#include +#include #include -#else -#include -#include -#endif -#include -#include -#include -#ifndef __FreeBSD__ -#include -#include -#endif +#include +#include #include -#include -#ifdef __FreeBSD__ -#include -#endif #include +#include +#include +#include +#include #include -#include -#include #include #include #include -#ifdef __FreeBSD__ -#include -#else -#include -#include -#endif -#include - -#include - -#if NPFLOG > 0 -#include -#endif /* NPFLOG > 0 */ - #ifdef INET6 #include -#include #endif /* INET6 */ #ifdef ALTQ #include #endif -#ifdef __FreeBSD__ -#include -#include -#include -#include -#endif /* __FreeBSD__ */ - -#ifdef __FreeBSD__ -void init_zone_var(void); -void cleanup_pf_zone(void); -int pfattach(void); -#else -void pfattach(int); -void pf_thread_create(void *); -int pfopen(dev_t, int, int, struct proc *); -int pfclose(dev_t, int, int, struct proc *); -#endif -struct pf_pool *pf_get_pool(char *, u_int32_t, u_int8_t, u_int32_t, +static int pfattach(void); +static struct pf_pool *pf_get_pool(char *, u_int32_t, u_int8_t, u_int32_t, u_int8_t, u_int8_t, u_int8_t); -void pf_mv_pool(struct pf_palist *, struct pf_palist *); -void pf_empty_pool(struct pf_palist *); -#ifdef __FreeBSD__ -int pfioctl(struct cdev *, u_long, caddr_t, int, struct thread *); -#else -int pfioctl(dev_t, u_long, caddr_t, int, struct proc *); -#endif +static void pf_mv_pool(struct pf_palist *, struct pf_palist *); +static void pf_empty_pool(struct pf_palist *); +static int pfioctl(struct cdev *, u_long, caddr_t, int, + struct thread *); #ifdef ALTQ -int pf_begin_altq(u_int32_t *); -int pf_rollback_altq(u_int32_t); -int pf_commit_altq(u_int32_t); -int pf_enable_altq(struct pf_altq *); -int pf_disable_altq(struct pf_altq *); +static int pf_begin_altq(u_int32_t *); +static int pf_rollback_altq(u_int32_t); +static int pf_commit_altq(u_int32_t); +static int pf_enable_altq(struct pf_altq *); +static int pf_disable_altq(struct pf_altq *); +static u_int32_t pf_qname2qid(char *); +static void pf_qid_unref(u_int32_t); #endif /* ALTQ */ -int pf_begin_rules(u_int32_t *, int, const char *); -int pf_rollback_rules(u_int32_t, int, char *); -int pf_setup_pfsync_matching(struct pf_ruleset *); -void pf_hash_rule(MD5_CTX *, struct pf_rule *); -void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *); -int pf_commit_rules(u_int32_t, int, char *); -int pf_addr_setup(struct pf_ruleset *, +static int pf_begin_rules(u_int32_t *, int, const char *); +static int pf_rollback_rules(u_int32_t, int, char *); +static int pf_setup_pfsync_matching(struct pf_ruleset *); +static void pf_hash_rule(MD5_CTX *, struct pf_rule *); +static void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *); +static int pf_commit_rules(u_int32_t, int, char *); +static int pf_addr_setup(struct pf_ruleset *, struct pf_addr_wrap *, sa_family_t); -void pf_addr_copyout(struct pf_addr_wrap *); - -#define TAGID_MAX 50000 +static void pf_addr_copyout(struct pf_addr_wrap *); -#ifdef __FreeBSD__ -VNET_DEFINE(struct pf_rule, pf_default_rule); -VNET_DEFINE(struct sx, pf_consistency_lock); +VNET_DEFINE(struct pf_rule, pf_default_rule); #ifdef ALTQ static VNET_DEFINE(int, pf_altq_running); #define V_pf_altq_running VNET(pf_altq_running) #endif -TAILQ_HEAD(pf_tags, pf_tagname); +#define TAGID_MAX 50000 +struct pf_tagname { + TAILQ_ENTRY(pf_tagname) entries; + char name[PF_TAG_NAME_SIZE]; + uint16_t tag; + int ref; +}; +TAILQ_HEAD(pf_tags, pf_tagname); #define V_pf_tags VNET(pf_tags) VNET_DEFINE(struct pf_tags, pf_tags); #define V_pf_qids VNET(pf_qids) VNET_DEFINE(struct pf_tags, pf_qids); - -#else /* !__FreeBSD__ */ -struct pf_rule pf_default_rule; -struct rwlock pf_consistency_lock = RWLOCK_INITIALIZER("pfcnslk"); -#ifdef ALTQ -static int pf_altq_running; -#endif - -TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags), - pf_qids = TAILQ_HEAD_INITIALIZER(pf_qids); -#endif /* __FreeBSD__ */ +static MALLOC_DEFINE(M_PFTAG, "pf_tag", "pf(4) tag names"); +static MALLOC_DEFINE(M_PFALTQ, "pf_altq", "pf(4) altq configuration db"); +static MALLOC_DEFINE(M_PFRULE, "pf_rule", "pf(4) rules"); #if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE) #error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE #endif -u_int16_t tagname2tag(struct pf_tags *, char *); -void tag2tagname(struct pf_tags *, u_int16_t, char *); -void tag_unref(struct pf_tags *, u_int16_t); -int pf_rtlabel_add(struct pf_addr_wrap *); -void pf_rtlabel_remove(struct pf_addr_wrap *); -void pf_rtlabel_copyout(struct pf_addr_wrap *); +static u_int16_t tagname2tag(struct pf_tags *, char *); +static u_int16_t pf_tagname2tag(char *); +static void tag_unref(struct pf_tags *, u_int16_t); -#ifdef __FreeBSD__ #define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x -#else -#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x -#endif -#ifdef __FreeBSD__ struct cdev *pf_dev; - + /* * XXX - These are new and need to be checked when moveing to a new version */ static void pf_clear_states(void); static int pf_clear_tables(void); -static void pf_clear_srcnodes(void); -/* - * XXX - These are new and need to be checked when moveing to a new version - */ - +static void pf_clear_srcnodes(struct pf_src_node *); +static void pf_tbladdr_copyout(struct pf_addr_wrap *); + /* * Wrapper functions for pfil(9) hooks */ @@ -240,7 +168,7 @@ static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, struct inpcb *inp); #endif - + static int hook_pf(void); static int dehook_pf(void); static int shutdown_pf(void); @@ -256,7 +184,8 @@ static struct cdevsw pf_cdevsw = { static volatile VNET_DEFINE(int, pf_pfil_hooked); #define V_pf_pfil_hooked VNET(pf_pfil_hooked) VNET_DEFINE(int, pf_end_threads); -struct mtx pf_task_mtx; + +struct rwlock pf_rules_lock; /* pfsync */ pfsync_state_import_t *pfsync_state_import_ptr = NULL; @@ -264,126 +193,27 @@ pfsync_insert_state_t *pfsync_insert_state_ptr = NULL; pfsync_update_state_t *pfsync_update_state_ptr = NULL; pfsync_delete_state_t *pfsync_delete_state_ptr = NULL; pfsync_clear_states_t *pfsync_clear_states_ptr = NULL; -pfsync_state_in_use_t *pfsync_state_in_use_ptr = NULL; pfsync_defer_t *pfsync_defer_ptr = NULL; -pfsync_up_t *pfsync_up_ptr = NULL; -/* pflow */ -export_pflow_t *export_pflow_ptr = NULL; /* pflog */ pflog_packet_t *pflog_packet_ptr = NULL; -VNET_DEFINE(int, debug_pfugidhack); -SYSCTL_VNET_INT(_debug, OID_AUTO, pfugidhack, CTLFLAG_RW, - &VNET_NAME(debug_pfugidhack), 0, - "Enable/disable pf user/group rules mpsafe hack"); - -static void -init_pf_mutex(void) -{ - - mtx_init(&pf_task_mtx, "pf task mtx", NULL, MTX_DEF); -} - -static void -destroy_pf_mutex(void) -{ - - mtx_destroy(&pf_task_mtx); -} -void -init_zone_var(void) -{ - V_pf_src_tree_pl = V_pf_rule_pl = NULL; - V_pf_state_pl = V_pf_state_key_pl = V_pf_state_item_pl = NULL; - V_pf_altq_pl = V_pf_pooladdr_pl = NULL; - V_pf_frent_pl = V_pf_frag_pl = V_pf_cache_pl = V_pf_cent_pl = NULL; - V_pf_state_scrub_pl = NULL; - V_pfr_ktable_pl = V_pfr_kentry_pl = V_pfr_kcounters_pl = NULL; -} - -void -cleanup_pf_zone(void) -{ - UMA_DESTROY(V_pf_src_tree_pl); - UMA_DESTROY(V_pf_rule_pl); - UMA_DESTROY(V_pf_state_pl); - UMA_DESTROY(V_pf_state_key_pl); - UMA_DESTROY(V_pf_state_item_pl); - UMA_DESTROY(V_pf_altq_pl); - UMA_DESTROY(V_pf_pooladdr_pl); - UMA_DESTROY(V_pf_frent_pl); - UMA_DESTROY(V_pf_frag_pl); - UMA_DESTROY(V_pf_cache_pl); - UMA_DESTROY(V_pf_cent_pl); - UMA_DESTROY(V_pfr_ktable_pl); - UMA_DESTROY(V_pfr_kentry_pl); - UMA_DESTROY(V_pfr_kcounters_pl); - UMA_DESTROY(V_pf_state_scrub_pl); - UMA_DESTROY(V_pfi_addr_pl); -} - -int +static int pfattach(void) { u_int32_t *my_timeout = V_pf_default_rule.timeout; - int error = 1; + int error; - do { - UMA_CREATE(V_pf_src_tree_pl, struct pf_src_node, "pfsrctrpl"); - UMA_CREATE(V_pf_rule_pl, struct pf_rule, "pfrulepl"); - UMA_CREATE(V_pf_state_pl, struct pf_state, "pfstatepl"); - UMA_CREATE(V_pf_state_key_pl, struct pf_state, "pfstatekeypl"); - UMA_CREATE(V_pf_state_item_pl, struct pf_state, "pfstateitempl"); - UMA_CREATE(V_pf_altq_pl, struct pf_altq, "pfaltqpl"); - UMA_CREATE(V_pf_pooladdr_pl, struct pf_pooladdr, "pfpooladdrpl"); - UMA_CREATE(V_pfr_ktable_pl, struct pfr_ktable, "pfrktable"); - UMA_CREATE(V_pfr_kentry_pl, struct pfr_kentry, "pfrkentry"); - UMA_CREATE(V_pfr_kcounters_pl, struct pfr_kcounters, "pfrkcounters"); - UMA_CREATE(V_pf_frent_pl, struct pf_frent, "pffrent"); - UMA_CREATE(V_pf_frag_pl, struct pf_fragment, "pffrag"); - UMA_CREATE(V_pf_cache_pl, struct pf_fragment, "pffrcache"); - UMA_CREATE(V_pf_cent_pl, struct pf_frcache, "pffrcent"); - UMA_CREATE(V_pf_state_scrub_pl, struct pf_state_scrub, - "pfstatescrub"); - UMA_CREATE(V_pfi_addr_pl, struct pfi_dynaddr, "pfiaddrpl"); - error = 0; - } while(0); - if (error) { - cleanup_pf_zone(); - return (error); - } + pf_initialize(); pfr_initialize(); pfi_initialize(); - if ( (error = pf_osfp_initialize()) ) { - cleanup_pf_zone(); - pf_osfp_cleanup(); - return (error); - } + pf_normalize_init(); + + V_pf_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT; + V_pf_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT; - V_pf_pool_limits[PF_LIMIT_STATES].pp = V_pf_state_pl; - V_pf_pool_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT; - V_pf_pool_limits[PF_LIMIT_SRC_NODES].pp = V_pf_src_tree_pl; - V_pf_pool_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT; - V_pf_pool_limits[PF_LIMIT_FRAGS].pp = V_pf_frent_pl; - V_pf_pool_limits[PF_LIMIT_FRAGS].limit = PFFRAG_FRENT_HIWAT; - V_pf_pool_limits[PF_LIMIT_TABLES].pp = V_pfr_ktable_pl; - V_pf_pool_limits[PF_LIMIT_TABLES].limit = PFR_KTABLE_HIWAT; - V_pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].pp = V_pfr_kentry_pl; - V_pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].limit = PFR_KENTRY_HIWAT; - uma_zone_set_max(V_pf_pool_limits[PF_LIMIT_STATES].pp, - V_pf_pool_limits[PF_LIMIT_STATES].limit); - - RB_INIT(&V_tree_src_tracking); RB_INIT(&V_pf_anchors); pf_init_ruleset(&pf_main_ruleset); - TAILQ_INIT(&V_pf_altqs[0]); - TAILQ_INIT(&V_pf_altqs[1]); - TAILQ_INIT(&V_pf_pabuf); - V_pf_altqs_active = &V_pf_altqs[0]; - V_pf_altqs_inactive = &V_pf_altqs[1]; - TAILQ_INIT(&V_state_list); - /* default rule should never be garbage collected */ V_pf_default_rule.entries.tqe_prev = &V_pf_default_rule.entries.tqe_next; V_pf_default_rule.action = PF_PASS; @@ -412,8 +242,6 @@ pfattach(void) my_timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; my_timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; - pf_normalize_init(); - bzero(&V_pf_status, sizeof(V_pf_status)); V_pf_status.debug = PF_DEBUG_URGENT; @@ -422,119 +250,19 @@ pfattach(void) /* XXX do our best to avoid a conflict */ V_pf_status.hostid = arc4random(); - if (kproc_create(pf_purge_thread, curvnet, NULL, 0, 0, "pfpurge")) - return (ENXIO); - - m_addr_chg_pf_p = pf_pkt_addr_changed; - - return (error); -} -#else /* !__FreeBSD__ */ - -void -pfattach(int num) -{ - u_int32_t *timeout = pf_default_rule.timeout; - - pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl", - &pool_allocator_nointr); - pool_init(&pf_src_tree_pl, sizeof(struct pf_src_node), 0, 0, 0, - "pfsrctrpl", NULL); - pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl", - NULL); - pool_init(&pf_state_key_pl, sizeof(struct pf_state_key), 0, 0, 0, - "pfstatekeypl", NULL); - pool_init(&pf_state_item_pl, sizeof(struct pf_state_item), 0, 0, 0, - "pfstateitempl", NULL); - pool_init(&pf_altq_pl, sizeof(struct pf_altq), 0, 0, 0, "pfaltqpl", - &pool_allocator_nointr); - pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0, - "pfpooladdrpl", &pool_allocator_nointr); - pfr_initialize(); - pfi_initialize(); - pf_osfp_initialize(); - - pool_sethardlimit(pf_pool_limits[PF_LIMIT_STATES].pp, - pf_pool_limits[PF_LIMIT_STATES].limit, NULL, 0); - - if (physmem <= atop(100*1024*1024)) - pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].limit = - PFR_KENTRY_HIWAT_SMALL; - - RB_INIT(&tree_src_tracking); - RB_INIT(&pf_anchors); - pf_init_ruleset(&pf_main_ruleset); - TAILQ_INIT(&pf_altqs[0]); - TAILQ_INIT(&pf_altqs[1]); - TAILQ_INIT(&pf_pabuf); - pf_altqs_active = &pf_altqs[0]; - pf_altqs_inactive = &pf_altqs[1]; - TAILQ_INIT(&state_list); - - /* default rule should never be garbage collected */ - pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; - pf_default_rule.action = PF_PASS; - pf_default_rule.nr = -1; - pf_default_rule.rtableid = -1; - - /* initialize default timeouts */ - timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; - timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; - timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; - timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; - timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; - timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; - timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; - timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; - timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; - timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; - timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; - timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; - timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; - timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; - timeout[PFTM_FRAG] = PFTM_FRAG_VAL; - timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL; - timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL; - timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL; - timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; - timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; - - pf_normalize_init(); - bzero(&pf_status, sizeof(pf_status)); - pf_status.debug = PF_DEBUG_URGENT; - - /* XXX do our best to avoid a conflict */ - pf_status.hostid = arc4random(); - - /* require process context to purge states, so perform in a thread */ - kthread_create_deferred(pf_thread_create, NULL); -} - -void -pf_thread_create(void *v) -{ - if (kthread_create(pf_purge_thread, NULL, NULL, "pfpurge")) - panic("pfpurge thread"); -} - -int -pfopen(dev_t dev, int flags, int fmt, struct proc *p) -{ - if (minor(dev) >= 1) - return (ENXIO); - return (0); -} + if ((error = kproc_create(pf_purge_thread, curvnet, NULL, 0, 0, + "pf purge")) != 0) + /* XXXGL: leaked all above. */ + return (error); + if ((error = swi_add(NULL, "pf send", pf_intr, curvnet, SWI_NET, + INTR_MPSAFE, &V_pf_swi_cookie)) != 0) + /* XXXGL: leaked all above. */ + return (error); -int -pfclose(dev_t dev, int flags, int fmt, struct proc *p) -{ - if (minor(dev) >= 1) - return (ENXIO); return (0); } -#endif -struct pf_pool * +static struct pf_pool * pf_get_pool(char *anchor, u_int32_t ticket, u_int8_t rule_action, u_int32_t rule_number, u_int8_t r_last, u_int8_t active, u_int8_t check_ticket) @@ -578,7 +306,7 @@ pf_get_pool(char *anchor, u_int32_t ticket, u_int8_t rule_action, return (&rule->rpool); } -void +static void pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb) { struct pf_pooladdr *mv_pool_pa; @@ -589,80 +317,89 @@ pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb) } } -void +static void pf_empty_pool(struct pf_palist *poola) { - struct pf_pooladdr *empty_pool_pa; - - while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) { - pfi_dynaddr_remove(&empty_pool_pa->addr); - pf_tbladdr_remove(&empty_pool_pa->addr); - pfi_kif_unref(empty_pool_pa->kif, PFI_KIF_REF_RULE); - TAILQ_REMOVE(poola, empty_pool_pa, entries); -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, empty_pool_pa); -#else - pool_put(&pf_pooladdr_pl, empty_pool_pa); -#endif + struct pf_pooladdr *pa; + + while ((pa = TAILQ_FIRST(poola)) != NULL) { + switch (pa->addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(pa->addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(pa->addr.p.tbl); + break; + } + if (pa->kif) + pfi_kif_unref(pa->kif); + TAILQ_REMOVE(poola, pa, entries); + free(pa, M_PFRULE); } } +static void +pf_unlink_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) +{ + + PF_RULES_WASSERT(); + + TAILQ_REMOVE(rulequeue, rule, entries); + + PF_UNLNKDRULES_LOCK(); + rule->rule_flag |= PFRULE_REFS; + TAILQ_INSERT_TAIL(&V_pf_unlinked_rules, rule, entries); + PF_UNLNKDRULES_UNLOCK(); +} + void -pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) +pf_free_rule(struct pf_rule *rule) { - if (rulequeue != NULL) { - if (rule->states_cur <= 0) { - /* - * XXX - we need to remove the table *before* detaching - * the rule to make sure the table code does not delete - * the anchor under our feet. - */ - pf_tbladdr_remove(&rule->src.addr); - pf_tbladdr_remove(&rule->dst.addr); - if (rule->overload_tbl) - pfr_detach_table(rule->overload_tbl); - } - TAILQ_REMOVE(rulequeue, rule, entries); - rule->entries.tqe_prev = NULL; - rule->nr = -1; - } - if (rule->states_cur > 0 || rule->src_nodes > 0 || - rule->entries.tqe_prev != NULL) - return; - pf_tag_unref(rule->tag); - pf_tag_unref(rule->match_tag); + PF_RULES_WASSERT(); + + if (rule->tag) + tag_unref(&V_pf_tags, rule->tag); + if (rule->match_tag) + tag_unref(&V_pf_tags, rule->match_tag); #ifdef ALTQ if (rule->pqid != rule->qid) pf_qid_unref(rule->pqid); pf_qid_unref(rule->qid); #endif - pf_rtlabel_remove(&rule->src.addr); - pf_rtlabel_remove(&rule->dst.addr); - pfi_dynaddr_remove(&rule->src.addr); - pfi_dynaddr_remove(&rule->dst.addr); - if (rulequeue == NULL) { - pf_tbladdr_remove(&rule->src.addr); - pf_tbladdr_remove(&rule->dst.addr); - if (rule->overload_tbl) - pfr_detach_table(rule->overload_tbl); + switch (rule->src.addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(rule->src.addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(rule->src.addr.p.tbl); + break; + } + switch (rule->dst.addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(rule->dst.addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(rule->dst.addr.p.tbl); + break; } - pfi_kif_unref(rule->kif, PFI_KIF_REF_RULE); + if (rule->overload_tbl) + pfr_detach_table(rule->overload_tbl); + if (rule->kif) + pfi_kif_unref(rule->kif); pf_anchor_remove(rule); pf_empty_pool(&rule->rpool.list); -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, rule); -#else - pool_put(&pf_rule_pl, rule); -#endif + free(rule, M_PFRULE); } -u_int16_t +static u_int16_t tagname2tag(struct pf_tags *head, char *tagname) { struct pf_tagname *tag, *p = NULL; u_int16_t new_tagid = 1; + PF_RULES_WASSERT(); + TAILQ_FOREACH(tag, head, entries) if (strcmp(tagname, tag->name) == 0) { tag->ref++; @@ -685,7 +422,7 @@ tagname2tag(struct pf_tags *head, char *tagname) return (0); /* allocate and fill new struct pf_tagname */ - tag = malloc(sizeof(*tag), M_TEMP, M_NOWAIT|M_ZERO); + tag = malloc(sizeof(*tag), M_PFTAG, M_NOWAIT|M_ZERO); if (tag == NULL) return (0); strlcpy(tag->name, tagname, sizeof(tag->name)); @@ -700,207 +437,78 @@ tagname2tag(struct pf_tags *head, char *tagname) return (tag->tag); } -void -tag2tagname(struct pf_tags *head, u_int16_t tagid, char *p) -{ - struct pf_tagname *tag; - - TAILQ_FOREACH(tag, head, entries) - if (tag->tag == tagid) { - strlcpy(p, tag->name, PF_TAG_NAME_SIZE); - return; - } -} - -void +static void tag_unref(struct pf_tags *head, u_int16_t tag) { struct pf_tagname *p, *next; - if (tag == 0) - return; + PF_RULES_WASSERT(); for (p = TAILQ_FIRST(head); p != NULL; p = next) { next = TAILQ_NEXT(p, entries); if (tag == p->tag) { if (--p->ref == 0) { TAILQ_REMOVE(head, p, entries); - free(p, M_TEMP); + free(p, M_PFTAG); } break; } } } -u_int16_t +static u_int16_t pf_tagname2tag(char *tagname) { -#ifdef __FreeBSD__ return (tagname2tag(&V_pf_tags, tagname)); -#else - return (tagname2tag(&pf_tags, tagname)); -#endif -} - -void -pf_tag2tagname(u_int16_t tagid, char *p) -{ -#ifdef __FreeBSD__ - tag2tagname(&V_pf_tags, tagid, p); -#else - tag2tagname(&pf_tags, tagid, p); -#endif -} - -void -pf_tag_ref(u_int16_t tag) -{ - struct pf_tagname *t; - -#ifdef __FreeBSD__ - TAILQ_FOREACH(t, &V_pf_tags, entries) -#else - TAILQ_FOREACH(t, &pf_tags, entries) -#endif - if (t->tag == tag) - break; - if (t != NULL) - t->ref++; -} - -void -pf_tag_unref(u_int16_t tag) -{ -#ifdef __FreeBSD__ - tag_unref(&V_pf_tags, tag); -#else - tag_unref(&pf_tags, tag); -#endif -} - -int -pf_rtlabel_add(struct pf_addr_wrap *a) -{ -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ - return (0); -#else - if (a->type == PF_ADDR_RTLABEL && - (a->v.rtlabel = rtlabel_name2id(a->v.rtlabelname)) == 0) - return (-1); - return (0); -#endif -} - -void -pf_rtlabel_remove(struct pf_addr_wrap *a) -{ -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ -#else - if (a->type == PF_ADDR_RTLABEL) - rtlabel_unref(a->v.rtlabel); -#endif -} - -void -pf_rtlabel_copyout(struct pf_addr_wrap *a) -{ -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ - if (a->type == PF_ADDR_RTLABEL && a->v.rtlabel) - strlcpy(a->v.rtlabelname, "?", sizeof(a->v.rtlabelname)); -#else - const char *name; - - if (a->type == PF_ADDR_RTLABEL && a->v.rtlabel) { - if ((name = rtlabel_id2name(a->v.rtlabel)) == NULL) - strlcpy(a->v.rtlabelname, "?", - sizeof(a->v.rtlabelname)); - else - strlcpy(a->v.rtlabelname, name, - sizeof(a->v.rtlabelname)); - } -#endif } #ifdef ALTQ -u_int32_t +static u_int32_t pf_qname2qid(char *qname) { -#ifdef __FreeBSD__ return ((u_int32_t)tagname2tag(&V_pf_qids, qname)); -#else - return ((u_int32_t)tagname2tag(&pf_qids, qname)); -#endif -} - -void -pf_qid2qname(u_int32_t qid, char *p) -{ -#ifdef __FreeBSD__ - tag2tagname(&V_pf_qids, (u_int16_t)qid, p); -#else - tag2tagname(&pf_qids, (u_int16_t)qid, p); -#endif } -void +static void pf_qid_unref(u_int32_t qid) { -#ifdef __FreeBSD__ tag_unref(&V_pf_qids, (u_int16_t)qid); -#else - tag_unref(&pf_qids, (u_int16_t)qid); -#endif } -int +static int pf_begin_altq(u_int32_t *ticket) { struct pf_altq *altq; int error = 0; + PF_RULES_WASSERT(); + /* Purge the old altq list */ -#ifdef __FreeBSD__ while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); if (altq->qname[0] == 0 && (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { -#else - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { -#endif /* detach and destroy the discipline */ error = altq_remove(altq); } else pf_qid_unref(altq->qid); -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, altq); -#else - pool_put(&pf_altq_pl, altq); -#endif + free(altq, M_PFALTQ); } if (error) return (error); -#ifdef __FreeBSD__ *ticket = ++V_ticket_altqs_inactive; V_altqs_inactive_open = 1; -#else - *ticket = ++ticket_altqs_inactive; - altqs_inactive_open = 1; -#endif return (0); } -int +static int pf_rollback_altq(u_int32_t ticket) { struct pf_altq *altq; int error = 0; -#ifdef __FreeBSD__ + PF_RULES_WASSERT(); + if (!V_altqs_inactive_open || ticket != V_ticket_altqs_inactive) return (0); /* Purge the old altq list */ @@ -908,101 +516,54 @@ pf_rollback_altq(u_int32_t ticket) TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); if (altq->qname[0] == 0 && (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { -#else - if (!altqs_inactive_open || ticket != ticket_altqs_inactive) - return (0); - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { -#endif /* detach and destroy the discipline */ error = altq_remove(altq); } else pf_qid_unref(altq->qid); -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, altq); -#else - pool_put(&pf_altq_pl, altq); -#endif + free(altq, M_PFALTQ); } -#ifdef __FreeBSD__ V_altqs_inactive_open = 0; -#else - altqs_inactive_open = 0; -#endif return (error); } -int +static int pf_commit_altq(u_int32_t ticket) { struct pf_altqqueue *old_altqs; struct pf_altq *altq; - int s, err, error = 0; + int err, error = 0; + + PF_RULES_WASSERT(); -#ifdef __FreeBSD__ if (!V_altqs_inactive_open || ticket != V_ticket_altqs_inactive) -#else - if (!altqs_inactive_open || ticket != ticket_altqs_inactive) -#endif return (EBUSY); /* swap altqs, keep the old. */ - s = splsoftnet(); -#ifdef __FreeBSD__ old_altqs = V_pf_altqs_active; V_pf_altqs_active = V_pf_altqs_inactive; V_pf_altqs_inactive = old_altqs; V_ticket_altqs_active = V_ticket_altqs_inactive; -#else - old_altqs = pf_altqs_active; - pf_altqs_active = pf_altqs_inactive; - pf_altqs_inactive = old_altqs; - ticket_altqs_active = ticket_altqs_inactive; -#endif /* Attach new disciplines */ -#ifdef __FreeBSD__ TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { if (altq->qname[0] == 0 && (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { -#else - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { -#endif /* attach the discipline */ error = altq_pfattach(altq); -#ifdef __FreeBSD__ if (error == 0 && V_pf_altq_running) -#else - if (error == 0 && pf_altq_running) -#endif error = pf_enable_altq(altq); - if (error != 0) { - splx(s); + if (error != 0) return (error); - } } } /* Purge the old altq list */ -#ifdef __FreeBSD__ while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); if (altq->qname[0] == 0 && (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { -#else - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { -#endif /* detach and destroy the discipline */ -#ifdef __FreeBSD__ if (V_pf_altq_running) -#else - if (pf_altq_running) -#endif error = pf_disable_altq(altq); err = altq_pfdetach(altq); if (err != 0 && error == 0) @@ -1012,28 +573,19 @@ pf_commit_altq(u_int32_t ticket) error = err; } else pf_qid_unref(altq->qid); -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, altq); -#else - pool_put(&pf_altq_pl, altq); -#endif + free(altq, M_PFALTQ); } - splx(s); -#ifdef __FreeBSD__ V_altqs_inactive_open = 0; -#else - altqs_inactive_open = 0; -#endif return (error); } -int +static int pf_enable_altq(struct pf_altq *altq) { struct ifnet *ifp; struct tb_profile tb; - int s, error = 0; + int error = 0; if ((ifp = ifunit(altq->ifname)) == NULL) return (EINVAL); @@ -1045,26 +597,18 @@ pf_enable_altq(struct pf_altq *altq) if (error == 0 && ifp != NULL && ALTQ_IS_ENABLED(&ifp->if_snd)) { tb.rate = altq->ifbandwidth; tb.depth = altq->tbrsize; - s = splnet(); -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif error = tbr_set(&ifp->if_snd, &tb); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - splx(s); } return (error); } -int +static int pf_disable_altq(struct pf_altq *altq) { struct ifnet *ifp; struct tb_profile tb; - int s, error; + int error; if ((ifp = ifunit(altq->ifname)) == NULL) return (EINVAL); @@ -1081,21 +625,12 @@ pf_disable_altq(struct pf_altq *altq) if (error == 0) { /* clear tokenbucket regulator */ tb.rate = 0; - s = splnet(); -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif error = tbr_set(&ifp->if_snd, &tb); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - splx(s); } return (error); } -#ifdef __FreeBSD__ void pf_altq_ifnet_event(struct ifnet *ifp, int remove) { @@ -1105,26 +640,16 @@ pf_altq_ifnet_event(struct ifnet *ifp, int remove) int error = 0; /* Interrupt userland queue modifications */ -#ifdef __FreeBSD__ if (V_altqs_inactive_open) pf_rollback_altq(V_ticket_altqs_inactive); -#else - if (altqs_inactive_open) - pf_rollback_altq(ticket_altqs_inactive); -#endif /* Start new altq ruleset */ if (pf_begin_altq(&ticket)) return; /* Copy the current active set */ -#ifdef __FreeBSD__ TAILQ_FOREACH(a1, V_pf_altqs_active, entries) { - a2 = pool_get(&V_pf_altq_pl, PR_NOWAIT); -#else - TAILQ_FOREACH(a1, pf_altqs_active, entries) { - a2 = pool_get(&pf_altq_pl, PR_NOWAIT); -#endif + a2 = malloc(sizeof(*a2), M_PFALTQ, M_NOWAIT); if (a2 == NULL) { error = ENOMEM; break; @@ -1134,19 +659,11 @@ pf_altq_ifnet_event(struct ifnet *ifp, int remove) if (a2->qname[0] != 0) { if ((a2->qid = pf_qname2qid(a2->qname)) == 0) { error = EBUSY; -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, a2); -#else - pool_put(&pf_altq_pl, a2); -#endif + free(a2, M_PFALTQ); break; } a2->altq_disc = NULL; -#ifdef __FreeBSD__ TAILQ_FOREACH(a3, V_pf_altqs_inactive, entries) { -#else - TAILQ_FOREACH(a3, pf_altqs_inactive, entries) { -#endif if (strncmp(a3->ifname, a2->ifname, IFNAMSIZ) == 0 && a3->qname[0] == 0) { a2->altq_disc = a3->altq_disc; @@ -1160,55 +677,42 @@ pf_altq_ifnet_event(struct ifnet *ifp, int remove) (remove && ifp1 == ifp)) { a2->local_flags |= PFALTQ_FLAG_IF_REMOVED; } else { - PF_UNLOCK(); error = altq_add(a2); - PF_LOCK(); -#ifdef __FreeBSD__ if (ticket != V_ticket_altqs_inactive) -#else - if (ticket != ticket_altqs_inactive) -#endif error = EBUSY; if (error) { -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, a2); -#else - pool_put(&pf_altq_pl, a2); -#endif + free(a2, M_PFALTQ); break; } } -#ifdef __FreeBSD__ TAILQ_INSERT_TAIL(V_pf_altqs_inactive, a2, entries); -#else - TAILQ_INSERT_TAIL(pf_altqs_inactive, a2, entries); -#endif } if (error != 0) pf_rollback_altq(ticket); else pf_commit_altq(ticket); - } -#endif +} #endif /* ALTQ */ -int +static int pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor) { struct pf_ruleset *rs; struct pf_rule *rule; + PF_RULES_WASSERT(); + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) return (EINVAL); rs = pf_find_or_create_ruleset(anchor); if (rs == NULL) return (EINVAL); while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) { - pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule); + pf_unlink_rule(rs->rules[rs_num].inactive.ptr, rule); rs->rules[rs_num].inactive.rcount--; } *ticket = ++rs->rules[rs_num].inactive.ticket; @@ -1216,12 +720,14 @@ pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor) return (0); } -int +static int pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor) { struct pf_ruleset *rs; struct pf_rule *rule; + PF_RULES_WASSERT(); + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) return (EINVAL); rs = pf_find_ruleset(anchor); @@ -1229,7 +735,7 @@ pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor) rs->rules[rs_num].inactive.ticket != ticket) return (0); while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) { - pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule); + pf_unlink_rule(rs->rules[rs_num].inactive.ptr, rule); rs->rules[rs_num].inactive.rcount--; } rs->rules[rs_num].inactive.open = 0; @@ -1252,7 +758,7 @@ pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor) MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int16_t));\ } while (0) -void +static void pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) { PF_MD5_UPD(pfr, addr.type); @@ -1269,9 +775,6 @@ pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) PF_MD5_UPD(pfr, addr.v.a.addr.addr32); PF_MD5_UPD(pfr, addr.v.a.mask.addr32); break; - case PF_ADDR_RTLABEL: - PF_MD5_UPD(pfr, addr.v.rtlabelname); - break; } PF_MD5_UPD(pfr, port[0]); @@ -1280,7 +783,7 @@ pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) PF_MD5_UPD(pfr, port_op); } -void +static void pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule) { u_int16_t x; @@ -1319,15 +822,17 @@ pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule) PF_MD5_UPD(rule, tos); } -int +static int pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) { struct pf_ruleset *rs; struct pf_rule *rule, **old_array; struct pf_rulequeue *old_rules; - int s, error; + int error; u_int32_t old_rcount; + PF_RULES_WASSERT(); + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) return (EINVAL); rs = pf_find_ruleset(anchor); @@ -1343,7 +848,6 @@ pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) } /* Swap rules, keep the old. */ - s = splsoftnet(); old_rules = rs->rules[rs_num].active.ptr; old_rcount = rs->rules[rs_num].active.rcount; old_array = rs->rules[rs_num].active.ptr_array; @@ -1365,18 +869,18 @@ pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) /* Purge the old rule list. */ while ((rule = TAILQ_FIRST(old_rules)) != NULL) - pf_rm_rule(old_rules, rule); + pf_unlink_rule(old_rules, rule); if (rs->rules[rs_num].inactive.ptr_array) free(rs->rules[rs_num].inactive.ptr_array, M_TEMP); rs->rules[rs_num].inactive.ptr_array = NULL; rs->rules[rs_num].inactive.rcount = 0; rs->rules[rs_num].inactive.open = 0; pf_remove_if_empty_ruleset(rs); - splx(s); + return (0); } -int +static int pf_setup_pfsync_matching(struct pf_ruleset *rs) { MD5_CTX ctx; @@ -1412,55 +916,53 @@ pf_setup_pfsync_matching(struct pf_ruleset *rs) } MD5Final(digest, &ctx); -#ifdef __FreeBSD__ memcpy(V_pf_status.pf_chksum, digest, sizeof(V_pf_status.pf_chksum)); -#else - memcpy(pf_status.pf_chksum, digest, sizeof(pf_status.pf_chksum)); -#endif return (0); } -int +static int pf_addr_setup(struct pf_ruleset *ruleset, struct pf_addr_wrap *addr, sa_family_t af) { - if (pfi_dynaddr_setup(addr, af) || - pf_tbladdr_setup(ruleset, addr)) - return (EINVAL); + int error = 0; - return (0); + switch (addr->type) { + case PF_ADDR_TABLE: + addr->p.tbl = pfr_attach_table(ruleset, addr->v.tblname); + if (addr->p.tbl == NULL) + error = ENOMEM; + break; + case PF_ADDR_DYNIFTL: + error = pfi_dynaddr_setup(addr, af); + break; + } + + return (error); } -void +static void pf_addr_copyout(struct pf_addr_wrap *addr) { - pfi_dynaddr_copyout(addr); - pf_tbladdr_copyout(addr); - pf_rtlabel_copyout(addr); + + switch (addr->type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_copyout(addr); + break; + case PF_ADDR_TABLE: + pf_tbladdr_copyout(addr); + break; + } } -int -#ifdef __FreeBSD__ +static int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) -#else -pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) -#endif { - struct pf_pooladdr *pa = NULL; - struct pf_pool *pool = NULL; -#ifndef __FreeBSD__ - int s; -#endif int error = 0; CURVNET_SET(TD_TO_VNET(td)); /* XXX keep in sync with switch() below */ -#ifdef __FreeBSD__ if (securelevel_gt(td->td_ucred, 2)) -#else - if (securelevel > 1) -#endif switch (cmd) { case DIOCGETRULES: case DIOCGETRULE: @@ -1496,9 +998,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCGETSRCNODES: case DIOCCLRSRCNODES: case DIOCIGETIFACES: -#ifdef __FreeBSD__ case DIOCGIFSPEED: -#endif case DIOCSETIFFLAG: case DIOCCLRIFFLAG: break; @@ -1538,9 +1038,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCOSFPGET: case DIOCGETSRCNODES: case DIOCIGETIFACES: -#ifdef __FreeBSD__ case DIOCGIFSPEED: -#endif break; case DIOCRCLRTABLES: case DIOCRADDTABLES: @@ -1566,85 +1064,51 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) return (EACCES); } - if (flags & FWRITE) -#ifdef __FreeBSD__ - sx_xlock(&V_pf_consistency_lock); - else - sx_slock(&V_pf_consistency_lock); -#else - rw_enter_write(&pf_consistency_lock); - else - rw_enter_read(&pf_consistency_lock); -#endif - -#ifdef __FreeBSD__ - PF_LOCK(); -#else - s = splsoftnet(); -#endif switch (cmd) { - case DIOCSTART: -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); if (V_pf_status.running) -#else - if (pf_status.running) -#endif error = EEXIST; else { -#ifdef __FreeBSD__ - PF_UNLOCK(); + int cpu; + + PF_RULES_WUNLOCK(); error = hook_pf(); - PF_LOCK(); if (error) { DPFPRINTF(PF_DEBUG_MISC, - ("pf: pfil registeration fail\n")); + ("pf: pfil registration failed\n")); break; } + PF_RULES_WLOCK(); V_pf_status.running = 1; V_pf_status.since = time_second; - if (V_pf_status.stateid == 0) { - V_pf_status.stateid = time_second; - V_pf_status.stateid = V_pf_status.stateid << 32; - } -#else - pf_status.running = 1; - pf_status.since = time_second; + CPU_FOREACH(cpu) + V_pf_stateid[cpu] = time_second; - if (pf_status.stateid == 0) { - pf_status.stateid = time_second; - pf_status.stateid = pf_status.stateid << 32; - } -#endif DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); } + PF_RULES_WUNLOCK(); break; case DIOCSTOP: -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); if (!V_pf_status.running) error = ENOENT; else { V_pf_status.running = 0; - PF_UNLOCK(); + PF_RULES_WUNLOCK(); error = dehook_pf(); - PF_LOCK(); if (error) { V_pf_status.running = 1; DPFPRINTF(PF_DEBUG_MISC, - ("pf: pfil unregisteration failed\n")); + ("pf: pfil unregistration failed\n")); } + PF_RULES_WLOCK(); V_pf_status.since = time_second; -#else - if (!pf_status.running) - error = ENOENT; - else { - pf_status.running = 0; - pf_status.since = time_second; -#endif DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); } + PF_RULES_WUNLOCK(); break; case DIOCADDRULE: { @@ -1652,89 +1116,57 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_ruleset *ruleset; struct pf_rule *rule, *tail; struct pf_pooladdr *pa; + struct pfi_kif *kif = NULL; int rs_num; - pr->anchor[sizeof(pr->anchor) - 1] = 0; - ruleset = pf_find_ruleset(pr->anchor); - if (ruleset == NULL) { + if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { error = EINVAL; break; } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - error = EINVAL; +#ifndef INET + if (pr->rule.af == AF_INET) { + error = EAFNOSUPPORT; break; } - if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { - error = EINVAL; +#endif /* INET */ +#ifndef INET6 + if (pr->rule.af == AF_INET6) { + error = EAFNOSUPPORT; break; } +#endif /* INET6 */ + + rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK); + bcopy(&pr->rule, rule, sizeof(struct pf_rule)); + if (rule->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + rule->cuid = td->td_ucred->cr_ruid; + rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; + TAILQ_INIT(&rule->rpool.list); + +#define ERROUT(x) { error = (x); goto DIOCADDRULE_error; } + + PF_RULES_WLOCK(); + pr->anchor[sizeof(pr->anchor) - 1] = 0; + ruleset = pf_find_ruleset(pr->anchor); + if (ruleset == NULL) + ERROUT(EINVAL); + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) + ERROUT(EINVAL); if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { -#ifdef __FreeBSD__ DPFPRINTF(PF_DEBUG_MISC, ("ticket: %d != [%d]%d\n", pr->ticket, rs_num, ruleset->rules[rs_num].inactive.ticket)); -#endif - error = EBUSY; - break; + ERROUT(EBUSY); } -#ifdef __FreeBSD__ if (pr->pool_ticket != V_ticket_pabuf) { DPFPRINTF(PF_DEBUG_MISC, ("pool_ticket: %d != %d\n", pr->pool_ticket, V_ticket_pabuf)); -#else - if (pr->pool_ticket != ticket_pabuf) { -#endif - error = EBUSY; - break; - } -#ifdef __FreeBSD__ - rule = pool_get(&V_pf_rule_pl, PR_NOWAIT); -#else - rule = pool_get(&pf_rule_pl, PR_WAITOK|PR_LIMITFAIL); -#endif - if (rule == NULL) { - error = ENOMEM; - break; - } - bcopy(&pr->rule, rule, sizeof(struct pf_rule)); -#ifdef __FreeBSD__ - rule->cuid = td->td_ucred->cr_ruid; - rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; -#else - rule->cuid = p->p_cred->p_ruid; - rule->cpid = p->p_pid; -#endif - rule->anchor = NULL; - rule->kif = NULL; - TAILQ_INIT(&rule->rpool.list); - /* initialize refcounting */ - rule->states_cur = 0; - rule->src_nodes = 0; - rule->entries.tqe_prev = NULL; -#ifndef INET - if (rule->af == AF_INET) { -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, rule); -#else - pool_put(&pf_rule_pl, rule); -#endif - error = EAFNOSUPPORT; - break; - } -#endif /* INET */ -#ifndef INET6 - if (rule->af == AF_INET6) { -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, rule); -#else - pool_put(&pf_rule_pl, rule); -#endif - error = EAFNOSUPPORT; - break; + ERROUT(EBUSY); } -#endif /* INET6 */ + tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, pf_rulequeue); if (tail) @@ -1742,24 +1174,12 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) else rule->nr = 0; if (rule->ifname[0]) { - rule->kif = pfi_kif_get(rule->ifname); - if (rule->kif == NULL) { -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, rule); -#else - pool_put(&pf_rule_pl, rule); -#endif - error = EINVAL; - break; - } - pfi_kif_ref(rule->kif, PFI_KIF_REF_RULE); - } + rule->kif = pfi_kif_attach(kif, rule->ifname); + pfi_kif_ref(rule->kif); + } else + rule->kif = NULL; -#ifdef __FreeBSD__ /* ROUTING */ if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) -#else - if (rule->rtableid > 0 && !rtable_exists(rule->rtableid)) -#endif error = EBUSY; #ifdef ALTQ @@ -1784,43 +1204,34 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EBUSY; if (rule->rt && !rule->direction) error = EINVAL; -#if NPFLOG > 0 if (!rule->log) rule->logif = 0; if (rule->logif >= PFLOGIFS_MAX) error = EINVAL; -#endif - if (pf_rtlabel_add(&rule->src.addr) || - pf_rtlabel_add(&rule->dst.addr)) - error = EBUSY; if (pf_addr_setup(ruleset, &rule->src.addr, rule->af)) - error = EINVAL; + error = ENOMEM; if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af)) - error = EINVAL; + error = ENOMEM; if (pf_anchor_setup(rule, ruleset, pr->anchor_call)) error = EINVAL; -#ifdef __FreeBSD__ TAILQ_FOREACH(pa, &V_pf_pabuf, entries) -#else - TAILQ_FOREACH(pa, &pf_pabuf, entries) -#endif - if (pf_tbladdr_setup(ruleset, &pa->addr)) - error = EINVAL; + if (pa->addr.type == PF_ADDR_TABLE) { + pa->addr.p.tbl = pfr_attach_table(ruleset, + pa->addr.v.tblname); + if (pa->addr.p.tbl == NULL) + error = ENOMEM; + } if (rule->overload_tblname[0]) { if ((rule->overload_tbl = pfr_attach_table(ruleset, - rule->overload_tblname, 0)) == NULL) + rule->overload_tblname)) == NULL) error = EINVAL; else rule->overload_tbl->pfrkt_flags |= PFR_TFLAG_ACTIVE; } -#ifdef __FreeBSD__ pf_mv_pool(&V_pf_pabuf, &rule->rpool.list); -#else - pf_mv_pool(&pf_pabuf, &rule->rpool.list); -#endif if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || (rule->action == PF_BINAT)) && rule->anchor == NULL) || (rule->rt > PF_FASTROUTE)) && @@ -1828,24 +1239,26 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; if (error) { - pf_rm_rule(NULL, rule); + pf_free_rule(rule); + PF_RULES_WUNLOCK(); break; } -#ifdef __FreeBSD__ - if (!V_debug_pfugidhack && (rule->uid.op || rule->gid.op || - rule->log & PF_LOG_SOCKET_LOOKUP)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: debug.pfugidhack enabled\n")); - V_debug_pfugidhack = 1; - } -#endif rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); rule->evaluations = rule->packets[0] = rule->packets[1] = rule->bytes[0] = rule->bytes[1] = 0; TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, rule, entries); ruleset->rules[rs_num].inactive.rcount++; + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCADDRULE_error: + PF_RULES_WUNLOCK(); + free(rule, M_PFRULE); + if (kif) + free(kif, PFI_MTYPE); break; } @@ -1855,14 +1268,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_rule *tail; int rs_num; + PF_RULES_WLOCK(); pr->anchor[sizeof(pr->anchor) - 1] = 0; ruleset = pf_find_ruleset(pr->anchor); if (ruleset == NULL) { + PF_RULES_WUNLOCK(); error = EINVAL; break; } rs_num = pf_get_ruleset_number(pr->rule.action); if (rs_num >= PF_RULESET_MAX) { + PF_RULES_WUNLOCK(); error = EINVAL; break; } @@ -1873,6 +1289,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) else pr->nr = 0; pr->ticket = ruleset->rules[rs_num].active.ticket; + PF_RULES_WUNLOCK(); break; } @@ -1882,18 +1299,22 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_rule *rule; int rs_num, i; + PF_RULES_WLOCK(); pr->anchor[sizeof(pr->anchor) - 1] = 0; ruleset = pf_find_ruleset(pr->anchor); if (ruleset == NULL) { + PF_RULES_WUNLOCK(); error = EINVAL; break; } rs_num = pf_get_ruleset_number(pr->rule.action); if (rs_num >= PF_RULESET_MAX) { + PF_RULES_WUNLOCK(); error = EINVAL; break; } if (pr->ticket != ruleset->rules[rs_num].active.ticket) { + PF_RULES_WUNLOCK(); error = EBUSY; break; } @@ -1901,11 +1322,13 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) while ((rule != NULL) && (rule->nr != pr->nr)) rule = TAILQ_NEXT(rule, entries); if (rule == NULL) { + PF_RULES_WUNLOCK(); error = EBUSY; break; } bcopy(rule, &pr->rule, sizeof(struct pf_rule)); if (pf_anchor_copyout(ruleset, rule, pr)) { + PF_RULES_WUNLOCK(); error = EBUSY; break; } @@ -1924,6 +1347,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) rule->bytes[0] = rule->bytes[1] = 0; rule->states_tot = 0; } + PF_RULES_WUNLOCK(); break; } @@ -1931,116 +1355,80 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pfioc_rule *pcr = (struct pfioc_rule *)addr; struct pf_ruleset *ruleset; struct pf_rule *oldrule = NULL, *newrule = NULL; + struct pfi_kif *kif = NULL; + struct pf_pooladdr *pa; u_int32_t nr = 0; int rs_num; - if (!(pcr->action == PF_CHANGE_REMOVE || - pcr->action == PF_CHANGE_GET_TICKET) && -#ifdef __FreeBSD__ - pcr->pool_ticket != V_ticket_pabuf) { -#else - pcr->pool_ticket != ticket_pabuf) { -#endif - error = EBUSY; - break; - } - if (pcr->action < PF_CHANGE_ADD_HEAD || pcr->action > PF_CHANGE_GET_TICKET) { error = EINVAL; break; } - ruleset = pf_find_ruleset(pcr->anchor); - if (ruleset == NULL) { + if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { error = EINVAL; break; } - rs_num = pf_get_ruleset_number(pcr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - error = EINVAL; - break; - } - - if (pcr->action == PF_CHANGE_GET_TICKET) { - pcr->ticket = ++ruleset->rules[rs_num].active.ticket; - break; - } else { - if (pcr->ticket != - ruleset->rules[rs_num].active.ticket) { - error = EINVAL; - break; - } - if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { - error = EINVAL; - break; - } - } if (pcr->action != PF_CHANGE_REMOVE) { -#ifdef __FreeBSD__ - newrule = pool_get(&V_pf_rule_pl, PR_NOWAIT); -#else - newrule = pool_get(&pf_rule_pl, PR_WAITOK|PR_LIMITFAIL); -#endif - if (newrule == NULL) { - error = ENOMEM; - break; - } - bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); -#ifdef __FreeBSD__ - newrule->cuid = td->td_ucred->cr_ruid; - newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0; -#else - newrule->cuid = p->p_cred->p_ruid; - newrule->cpid = p->p_pid; -#endif - TAILQ_INIT(&newrule->rpool.list); - /* initialize refcounting */ - newrule->states_cur = 0; - newrule->entries.tqe_prev = NULL; #ifndef INET - if (newrule->af == AF_INET) { -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, newrule); -#else - pool_put(&pf_rule_pl, newrule); -#endif + if (pcr->rule.af == AF_INET) { error = EAFNOSUPPORT; break; } #endif /* INET */ #ifndef INET6 - if (newrule->af == AF_INET6) { -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, newrule); -#else - pool_put(&pf_rule_pl, newrule); -#endif + if (pcr->rule.af == AF_INET6) { error = EAFNOSUPPORT; break; } #endif /* INET6 */ + newrule = malloc(sizeof(*newrule), M_PFRULE, M_WAITOK); + bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); + newrule->cuid = td->td_ucred->cr_ruid; + newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0; + TAILQ_INIT(&newrule->rpool.list); + /* Initialize refcounting. */ + newrule->states_cur = 0; + newrule->entries.tqe_prev = NULL; + + if (newrule->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + } + +#define ERROUT(x) { error = (x); goto DIOCCHANGERULE_error; } + + PF_RULES_WLOCK(); + if (!(pcr->action == PF_CHANGE_REMOVE || + pcr->action == PF_CHANGE_GET_TICKET) && + pcr->pool_ticket != V_ticket_pabuf) + ERROUT(EBUSY); + + ruleset = pf_find_ruleset(pcr->anchor); + if (ruleset == NULL) + ERROUT(EINVAL); + + rs_num = pf_get_ruleset_number(pcr->rule.action); + if (rs_num >= PF_RULESET_MAX) + ERROUT(EINVAL); + + if (pcr->action == PF_CHANGE_GET_TICKET) { + pcr->ticket = ++ruleset->rules[rs_num].active.ticket; + ERROUT(0); + } else if (pcr->ticket != + ruleset->rules[rs_num].active.ticket) + ERROUT(EINVAL); + + if (pcr->action != PF_CHANGE_REMOVE) { if (newrule->ifname[0]) { - newrule->kif = pfi_kif_get(newrule->ifname); - if (newrule->kif == NULL) { -#ifdef __FreeBSD__ - pool_put(&V_pf_rule_pl, newrule); -#else - pool_put(&pf_rule_pl, newrule); -#endif - error = EINVAL; - break; - } - pfi_kif_ref(newrule->kif, PFI_KIF_REF_RULE); + newrule->kif = pfi_kif_attach(kif, + newrule->ifname); + pfi_kif_ref(newrule->kif); } else newrule->kif = NULL; if (newrule->rtableid > 0 && -#ifdef __FreeBSD__ /* ROUTING */ newrule->rtableid >= rt_numfibs) -#else - !rtable_exists(newrule->rtableid)) -#endif error = EBUSY; #ifdef ALTQ @@ -2067,32 +1455,28 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EBUSY; if (newrule->rt && !newrule->direction) error = EINVAL; -#if NPFLOG > 0 if (!newrule->log) newrule->logif = 0; if (newrule->logif >= PFLOGIFS_MAX) error = EINVAL; -#endif - if (pf_rtlabel_add(&newrule->src.addr) || - pf_rtlabel_add(&newrule->dst.addr)) - error = EBUSY; if (pf_addr_setup(ruleset, &newrule->src.addr, newrule->af)) - error = EINVAL; + error = ENOMEM; if (pf_addr_setup(ruleset, &newrule->dst.addr, newrule->af)) - error = EINVAL; + error = ENOMEM; if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call)) error = EINVAL; -#ifdef __FreeBSD__ TAILQ_FOREACH(pa, &V_pf_pabuf, entries) -#else - TAILQ_FOREACH(pa, &pf_pabuf, entries) -#endif - if (pf_tbladdr_setup(ruleset, &pa->addr)) - error = EINVAL; + if (pa->addr.type == PF_ADDR_TABLE) { + pa->addr.p.tbl = + pfr_attach_table(ruleset, + pa->addr.v.tblname); + if (pa->addr.p.tbl == NULL) + error = ENOMEM; + } if (newrule->overload_tblname[0]) { if ((newrule->overload_tbl = pfr_attach_table( - ruleset, newrule->overload_tblname, 0)) == + ruleset, newrule->overload_tblname)) == NULL) error = EINVAL; else @@ -2100,11 +1484,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) PFR_TFLAG_ACTIVE; } -#ifdef __FreeBSD__ pf_mv_pool(&V_pf_pabuf, &newrule->rpool.list); -#else - pf_mv_pool(&pf_pabuf, &newrule->rpool.list); -#endif if (((((newrule->action == PF_NAT) || (newrule->action == PF_RDR) || (newrule->action == PF_BINAT) || @@ -2114,30 +1494,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; if (error) { - pf_rm_rule(NULL, newrule); + pf_free_rule(newrule); + PF_RULES_WUNLOCK(); break; } -#ifdef __FreeBSD__ - if (!V_debug_pfugidhack && (newrule->uid.op || - newrule->gid.op || - newrule->log & PF_LOG_SOCKET_LOOKUP)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: debug.pfugidhack enabled\n")); - V_debug_pfugidhack = 1; - } -#endif - newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list); newrule->evaluations = 0; newrule->packets[0] = newrule->packets[1] = 0; newrule->bytes[0] = newrule->bytes[1] = 0; } -#ifdef __FreeBSD__ pf_empty_pool(&V_pf_pabuf); -#else - pf_empty_pool(&pf_pabuf); -#endif if (pcr->action == PF_CHANGE_ADD_HEAD) oldrule = TAILQ_FIRST( @@ -2152,14 +1519,16 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) oldrule = TAILQ_NEXT(oldrule, entries); if (oldrule == NULL) { if (newrule != NULL) - pf_rm_rule(NULL, newrule); + pf_free_rule(newrule); + PF_RULES_WUNLOCK(); error = EINVAL; break; } } if (pcr->action == PF_CHANGE_REMOVE) { - pf_rm_rule(ruleset->rules[rs_num].active.ptr, oldrule); + pf_unlink_rule(ruleset->rules[rs_num].active.ptr, + oldrule); ruleset->rules[rs_num].active.rcount--; } else { if (oldrule == NULL) @@ -2186,114 +1555,120 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); pf_remove_if_empty_ruleset(ruleset); + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCCHANGERULE_error: + PF_RULES_WUNLOCK(); + if (newrule != NULL) + free(newrule, M_PFRULE); + if (kif != NULL) + free(kif, PFI_MTYPE); break; } case DIOCCLRSTATES: { - struct pf_state *s, *nexts; + struct pf_state *s; struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; - u_int killed = 0; - -#ifdef __FreeBSD__ - for (s = RB_MIN(pf_state_tree_id, &V_tree_id); s; s = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &V_tree_id, s); -#else - for (s = RB_MIN(pf_state_tree_id, &tree_id); s; s = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &tree_id, s); -#endif - - if (!psk->psk_ifname[0] || !strcmp(psk->psk_ifname, - s->kif->pfik_name)) { -#if NPFSYNC > 0 - /* don't send out individual delete messages */ - SET(s->state_flags, PFSTATE_NOSYNC); -#endif - pf_unlink_state(s); - killed++; - } + u_int i, killed = 0; + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + +relock_DIOCCLRSTATES: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) + if (!psk->psk_ifname[0] || + !strcmp(psk->psk_ifname, + s->kif->pfik_name)) { + /* + * Don't send out individual + * delete messages. + */ + s->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(s, PF_ENTER_LOCKED); + killed++; + goto relock_DIOCCLRSTATES; + } + PF_HASHROW_UNLOCK(ih); } psk->psk_killed = killed; -#if NPFSYNC > 0 -#ifdef __FreeBSD__ if (pfsync_clear_states_ptr != NULL) pfsync_clear_states_ptr(V_pf_status.hostid, psk->psk_ifname); -#else - pfsync_clear_states(pf_status.hostid, psk->psk_ifname); -#endif -#endif break; } case DIOCKILLSTATES: { - struct pf_state *s, *nexts; + struct pf_state *s; struct pf_state_key *sk; struct pf_addr *srcaddr, *dstaddr; u_int16_t srcport, dstport; struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; - u_int killed = 0; + u_int i, killed = 0; if (psk->psk_pfcmp.id) { if (psk->psk_pfcmp.creatorid == 0) -#ifdef __FreeBSD__ psk->psk_pfcmp.creatorid = V_pf_status.hostid; -#else - psk->psk_pfcmp.creatorid = pf_status.hostid; -#endif - if ((s = pf_find_state_byid(&psk->psk_pfcmp))) { - pf_unlink_state(s); + if ((s = pf_find_state_byid(psk->psk_pfcmp.id, + psk->psk_pfcmp.creatorid))) { + pf_unlink_state(s, PF_ENTER_LOCKED); psk->psk_killed = 1; } break; } -#ifdef __FreeBSD__ - for (s = RB_MIN(pf_state_tree_id, &V_tree_id); s; - s = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &V_tree_id, s); -#else - for (s = RB_MIN(pf_state_tree_id, &tree_id); s; - s = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &tree_id, s); -#endif - sk = s->key[PF_SK_WIRE]; - - if (s->direction == PF_OUT) { - srcaddr = &sk->addr[1]; - dstaddr = &sk->addr[0]; - srcport = sk->port[0]; - dstport = sk->port[0]; - } else { - srcaddr = &sk->addr[0]; - dstaddr = &sk->addr[1]; - srcport = sk->port[0]; - dstport = sk->port[0]; - } - if ((!psk->psk_af || sk->af == psk->psk_af) - && (!psk->psk_proto || psk->psk_proto == - sk->proto) && - PF_MATCHA(psk->psk_src.neg, - &psk->psk_src.addr.v.a.addr, - &psk->psk_src.addr.v.a.mask, - srcaddr, sk->af) && - PF_MATCHA(psk->psk_dst.neg, - &psk->psk_dst.addr.v.a.addr, - &psk->psk_dst.addr.v.a.mask, - dstaddr, sk->af) && - (psk->psk_src.port_op == 0 || - pf_match_port(psk->psk_src.port_op, - psk->psk_src.port[0], psk->psk_src.port[1], - srcport)) && - (psk->psk_dst.port_op == 0 || - pf_match_port(psk->psk_dst.port_op, - psk->psk_dst.port[0], psk->psk_dst.port[1], - dstport)) && - (!psk->psk_label[0] || (s->rule.ptr->label[0] && - !strcmp(psk->psk_label, s->rule.ptr->label))) && - (!psk->psk_ifname[0] || !strcmp(psk->psk_ifname, - s->kif->pfik_name))) { - pf_unlink_state(s); - killed++; + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + +relock_DIOCKILLSTATES: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + sk = s->key[PF_SK_WIRE]; + if (s->direction == PF_OUT) { + srcaddr = &sk->addr[1]; + dstaddr = &sk->addr[0]; + srcport = sk->port[0]; + dstport = sk->port[0]; + } else { + srcaddr = &sk->addr[0]; + dstaddr = &sk->addr[1]; + srcport = sk->port[0]; + dstport = sk->port[0]; + } + + if ((!psk->psk_af || sk->af == psk->psk_af) + && (!psk->psk_proto || psk->psk_proto == + sk->proto) && + PF_MATCHA(psk->psk_src.neg, + &psk->psk_src.addr.v.a.addr, + &psk->psk_src.addr.v.a.mask, + srcaddr, sk->af) && + PF_MATCHA(psk->psk_dst.neg, + &psk->psk_dst.addr.v.a.addr, + &psk->psk_dst.addr.v.a.mask, + dstaddr, sk->af) && + (psk->psk_src.port_op == 0 || + pf_match_port(psk->psk_src.port_op, + psk->psk_src.port[0], psk->psk_src.port[1], + srcport)) && + (psk->psk_dst.port_op == 0 || + pf_match_port(psk->psk_dst.port_op, + psk->psk_dst.port[0], psk->psk_dst.port[1], + dstport)) && + (!psk->psk_label[0] || + (s->rule.ptr->label[0] && + !strcmp(psk->psk_label, + s->rule.ptr->label))) && + (!psk->psk_ifname[0] || + !strcmp(psk->psk_ifname, + s->kif->pfik_name))) { + pf_unlink_state(s, PF_ENTER_LOCKED); + killed++; + goto relock_DIOCKILLSTATES; + } } + PF_HASHROW_UNLOCK(ih); } psk->psk_killed = killed; break; @@ -2308,98 +1683,83 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; break; } -#ifdef __FreeBSD__ - if (pfsync_state_import_ptr != NULL) + if (pfsync_state_import_ptr != NULL) { + PF_RULES_RLOCK(); error = pfsync_state_import_ptr(sp, PFSYNC_SI_IOCTL); -#else - error = pfsync_state_import(sp, PFSYNC_SI_IOCTL); -#endif + PF_RULES_RUNLOCK(); + } + error = EOPNOTSUPP; break; } case DIOCGETSTATE: { struct pfioc_state *ps = (struct pfioc_state *)addr; struct pf_state *s; - struct pf_state_cmp id_key; - - bcopy(ps->state.id, &id_key.id, sizeof(id_key.id)); - id_key.creatorid = ps->state.creatorid; - s = pf_find_state_byid(&id_key); + s = pf_find_state_byid(ps->state.id, ps->state.creatorid); if (s == NULL) { error = ENOENT; break; } pfsync_state_export(&ps->state, s); + PF_STATE_UNLOCK(s); break; } case DIOCGETSTATES: { struct pfioc_states *ps = (struct pfioc_states *)addr; - struct pf_state *state; - struct pfsync_state *p, *pstore; - u_int32_t nr = 0; + struct pf_state *s; + struct pfsync_state *pstore, *p; + int i, nr; if (ps->ps_len == 0) { -#ifdef __FreeBSD__ - nr = V_pf_status.states; -#else - nr = pf_status.states; -#endif + nr = uma_zone_get_cur(V_pf_state_z); ps->ps_len = sizeof(struct pfsync_state) * nr; break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - pstore = malloc(sizeof(*pstore), M_TEMP, M_WAITOK); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif + p = pstore = malloc(ps->ps_len, M_TEMP, M_WAITOK); + nr = 0; - p = ps->ps_states; + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; -#ifdef __FreeBSD__ - state = TAILQ_FIRST(&V_state_list); -#else - state = TAILQ_FIRST(&state_list); -#endif - while (state) { - if (state->timeout != PFTM_UNLINKED) { - if ((nr+1) * sizeof(*p) > (unsigned)ps->ps_len) - break; - pfsync_state_export(pstore, state); -#ifdef __FreeBSD__ - PF_COPYOUT(pstore, p, sizeof(*p), error); -#else - error = copyout(pstore, p, sizeof(*p)); -#endif - if (error) { - free(pstore, M_TEMP); - goto fail; + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + + if (s->timeout == PFTM_UNLINKED) + continue; + + if ((nr+1) * sizeof(*p) > ps->ps_len) { + PF_HASHROW_UNLOCK(ih); + goto DIOCGETSTATES_full; } + pfsync_state_export(p, s); p++; nr++; } - state = TAILQ_NEXT(state, entry_list); + PF_HASHROW_UNLOCK(ih); + } +DIOCGETSTATES_full: + error = copyout(pstore, ps->ps_states, + sizeof(struct pfsync_state) * nr); + if (error) { + free(pstore, M_TEMP); + break; } - ps->ps_len = sizeof(struct pfsync_state) * nr; - free(pstore, M_TEMP); + break; } case DIOCGETSTATUS: { struct pf_status *s = (struct pf_status *)addr; -#ifdef __FreeBSD__ + PF_RULES_RLOCK(); bcopy(&V_pf_status, s, sizeof(struct pf_status)); -#else - bcopy(&pf_status, s, sizeof(struct pf_status)); -#endif pfi_update_status(s->ifname, s); + PF_RULES_RUNLOCK(); break; } @@ -2407,37 +1767,24 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pfioc_if *pi = (struct pfioc_if *)addr; if (pi->ifname[0] == 0) { -#ifdef __FreeBSD__ bzero(V_pf_status.ifname, IFNAMSIZ); -#else - bzero(pf_status.ifname, IFNAMSIZ); -#endif break; } -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); strlcpy(V_pf_status.ifname, pi->ifname, IFNAMSIZ); -#else - strlcpy(pf_status.ifname, pi->ifname, IFNAMSIZ); -#endif + PF_RULES_WUNLOCK(); break; } case DIOCCLRSTATUS: { -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); bzero(V_pf_status.counters, sizeof(V_pf_status.counters)); bzero(V_pf_status.fcounters, sizeof(V_pf_status.fcounters)); bzero(V_pf_status.scounters, sizeof(V_pf_status.scounters)); V_pf_status.since = time_second; if (*V_pf_status.ifname) pfi_update_status(V_pf_status.ifname, NULL); -#else - bzero(pf_status.counters, sizeof(pf_status.counters)); - bzero(pf_status.fcounters, sizeof(pf_status.fcounters)); - bzero(pf_status.scounters, sizeof(pf_status.scounters)); - pf_status.since = time_second; - if (*pf_status.ifname) - pfi_update_status(pf_status.ifname, NULL); -#endif + PF_RULES_WUNLOCK(); break; } @@ -2473,6 +1820,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (m > 1) error = E2BIG; /* more than one state */ else if (state != NULL) { + /* XXXGL: not locked read */ sk = state->key[sidx]; PF_ACPY(&pnl->rsaddr, &sk->addr[sidx], sk->af); pnl->rsport = sk->port[sidx]; @@ -2491,23 +1839,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pt->timeout < 0 || pt->timeout >= PFTM_MAX || pt->seconds < 0) { error = EINVAL; - goto fail; + break; } -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); old = V_pf_default_rule.timeout[pt->timeout]; -#else - old = pf_default_rule.timeout[pt->timeout]; -#endif if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0) pt->seconds = 1; -#ifdef __FreeBSD__ V_pf_default_rule.timeout[pt->timeout] = pt->seconds; -#else - pf_default_rule.timeout[pt->timeout] = pt->seconds; -#endif if (pt->timeout == PFTM_INTERVAL && pt->seconds < old) wakeup(pf_purge_thread); pt->seconds = old; + PF_RULES_WUNLOCK(); break; } @@ -2516,13 +1858,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) { error = EINVAL; - goto fail; + break; } -#ifdef __FreeBSD__ + PF_RULES_RLOCK(); pt->seconds = V_pf_default_rule.timeout[pt->timeout]; -#else - pt->seconds = pf_default_rule.timeout[pt->timeout]; -#endif + PF_RULES_RUNLOCK(); break; } @@ -2531,13 +1871,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { error = EINVAL; - goto fail; + break; } -#ifdef __FreeBSD__ - pl->limit = V_pf_pool_limits[pl->index].limit; -#else - pl->limit = pf_pool_limits[pl->index].limit; -#endif + PF_RULES_RLOCK(); + pl->limit = V_pf_limits[pl->index].limit; + PF_RULES_RUNLOCK(); break; } @@ -2545,41 +1883,27 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pfioc_limit *pl = (struct pfioc_limit *)addr; int old_limit; + PF_RULES_WLOCK(); if (pl->index < 0 || pl->index >= PF_LIMIT_MAX || -#ifdef __FreeBSD__ - V_pf_pool_limits[pl->index].pp == NULL) { -#else - pf_pool_limits[pl->index].pp == NULL) { -#endif - error = EINVAL; - goto fail; - } -#ifdef __FreeBSD__ - uma_zone_set_max(V_pf_pool_limits[pl->index].pp, pl->limit); - old_limit = V_pf_pool_limits[pl->index].limit; - V_pf_pool_limits[pl->index].limit = pl->limit; - pl->limit = old_limit; -#else - if (pool_sethardlimit(pf_pool_limits[pl->index].pp, - pl->limit, NULL, 0) != 0) { - error = EBUSY; - goto fail; + V_pf_limits[pl->index].zone == NULL) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; } - old_limit = pf_pool_limits[pl->index].limit; - pf_pool_limits[pl->index].limit = pl->limit; + uma_zone_set_max(V_pf_limits[pl->index].zone, pl->limit); + old_limit = V_pf_limits[pl->index].limit; + V_pf_limits[pl->index].limit = pl->limit; pl->limit = old_limit; -#endif + PF_RULES_WUNLOCK(); break; } case DIOCSETDEBUG: { u_int32_t *level = (u_int32_t *)addr; -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); V_pf_status.debug = *level; -#else - pf_status.debug = *level; -#endif + PF_RULES_WUNLOCK(); break; } @@ -2588,16 +1912,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_ruleset *ruleset = &pf_main_ruleset; struct pf_rule *rule; + PF_RULES_WLOCK(); TAILQ_FOREACH(rule, ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) { rule->evaluations = 0; rule->packets[0] = rule->packets[1] = 0; rule->bytes[0] = rule->bytes[1] = 0; } + PF_RULES_WUNLOCK(); break; } -#ifdef __FreeBSD__ case DIOCGIFSPEED: { struct pf_ifspeed *psp = (struct pf_ifspeed *)addr; struct pf_ifspeed ps; @@ -2615,32 +1940,24 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; break; } -#endif /* __FreeBSD__ */ #ifdef ALTQ case DIOCSTARTALTQ: { struct pf_altq *altq; + PF_RULES_WLOCK(); /* enable all altq interfaces on active list */ -#ifdef __FreeBSD__ TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { if (altq->qname[0] == 0 && (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { -#else - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { -#endif error = pf_enable_altq(altq); if (error != 0) break; } } if (error == 0) -#ifdef __FreeBSD__ V_pf_altq_running = 1; -#else - pf_altq_running = 1; -#endif + PF_RULES_WUNLOCK(); DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n")); break; } @@ -2648,26 +1965,19 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCSTOPALTQ: { struct pf_altq *altq; + PF_RULES_WLOCK(); /* disable all altq interfaces on active list */ -#ifdef __FreeBSD__ TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { if (altq->qname[0] == 0 && (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { -#else - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { -#endif error = pf_disable_altq(altq); if (error != 0) break; } } if (error == 0) -#ifdef __FreeBSD__ V_pf_altq_running = 0; -#else - pf_altq_running = 0; -#endif + PF_RULES_WUNLOCK(); DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); break; } @@ -2675,28 +1985,19 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCADDALTQ: { struct pfioc_altq *pa = (struct pfioc_altq *)addr; struct pf_altq *altq, *a; + struct ifnet *ifp; + + altq = malloc(sizeof(*altq), M_PFALTQ, M_WAITOK); + bcopy(&pa->altq, altq, sizeof(struct pf_altq)); + altq->local_flags = 0; -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); if (pa->ticket != V_ticket_altqs_inactive) { -#else - if (pa->ticket != ticket_altqs_inactive) { -#endif + PF_RULES_WUNLOCK(); + free(altq, M_PFALTQ); error = EBUSY; break; } -#ifdef __FreeBSD__ - altq = pool_get(&V_pf_altq_pl, PR_NOWAIT); -#else - altq = pool_get(&pf_altq_pl, PR_WAITOK|PR_LIMITFAIL); -#endif - if (altq == NULL) { - error = ENOMEM; - break; - } - bcopy(&pa->altq, altq, sizeof(struct pf_altq)); -#ifdef __FreeBSD__ - altq->local_flags = 0; -#endif /* * if this is for a queue, find the discipline and @@ -2704,20 +2005,13 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) */ if (altq->qname[0] != 0) { if ((altq->qid = pf_qname2qid(altq->qname)) == 0) { + PF_RULES_WUNLOCK(); error = EBUSY; -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, altq); -#else - pool_put(&pf_altq_pl, altq); -#endif + free(altq, M_PFALTQ); break; } altq->altq_disc = NULL; -#ifdef __FreeBSD__ TAILQ_FOREACH(a, V_pf_altqs_inactive, entries) { -#else - TAILQ_FOREACH(a, pf_altqs_inactive, entries) { -#endif if (strncmp(a->ifname, altq->ifname, IFNAMSIZ) == 0 && a->qname[0] == 0) { altq->altq_disc = a->altq_disc; @@ -2726,34 +2020,20 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } } -#ifdef __FreeBSD__ - struct ifnet *ifp; - - if ((ifp = ifunit(altq->ifname)) == NULL) { + if ((ifp = ifunit(altq->ifname)) == NULL) altq->local_flags |= PFALTQ_FLAG_IF_REMOVED; - } else { - PF_UNLOCK(); -#endif - error = altq_add(altq); -#ifdef __FreeBSD__ - PF_LOCK(); - } -#endif + else + error = altq_add(altq); + if (error) { -#ifdef __FreeBSD__ - pool_put(&V_pf_altq_pl, altq); -#else - pool_put(&pf_altq_pl, altq); -#endif + PF_RULES_WUNLOCK(); + free(altq, M_PFALTQ); break; } -#ifdef __FreeBSD__ TAILQ_INSERT_TAIL(V_pf_altqs_inactive, altq, entries); -#else - TAILQ_INSERT_TAIL(pf_altqs_inactive, altq, entries); -#endif bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + PF_RULES_WUNLOCK(); break; } @@ -2761,16 +2041,12 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pfioc_altq *pa = (struct pfioc_altq *)addr; struct pf_altq *altq; + PF_RULES_RLOCK(); pa->nr = 0; -#ifdef __FreeBSD__ TAILQ_FOREACH(altq, V_pf_altqs_active, entries) pa->nr++; pa->ticket = V_ticket_altqs_active; -#else - TAILQ_FOREACH(altq, pf_altqs_active, entries) - pa->nr++; - pa->ticket = ticket_altqs_active; -#endif + PF_RULES_RUNLOCK(); break; } @@ -2779,29 +2055,25 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_altq *altq; u_int32_t nr; -#ifdef __FreeBSD__ + PF_RULES_RLOCK(); if (pa->ticket != V_ticket_altqs_active) { -#else - if (pa->ticket != ticket_altqs_active) { -#endif + PF_RULES_RUNLOCK(); error = EBUSY; break; } nr = 0; -#ifdef __FreeBSD__ altq = TAILQ_FIRST(V_pf_altqs_active); -#else - altq = TAILQ_FIRST(pf_altqs_active); -#endif while ((altq != NULL) && (nr < pa->nr)) { altq = TAILQ_NEXT(altq, entries); nr++; } if (altq == NULL) { + PF_RULES_RUNLOCK(); error = EBUSY; break; } bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + PF_RULES_RUNLOCK(); break; } @@ -2816,41 +2088,32 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) u_int32_t nr; int nbytes; -#ifdef __FreeBSD__ + PF_RULES_RLOCK(); if (pq->ticket != V_ticket_altqs_active) { -#else - if (pq->ticket != ticket_altqs_active) { -#endif + PF_RULES_RUNLOCK(); error = EBUSY; break; } nbytes = pq->nbytes; nr = 0; -#ifdef __FreeBSD__ altq = TAILQ_FIRST(V_pf_altqs_active); -#else - altq = TAILQ_FIRST(pf_altqs_active); -#endif while ((altq != NULL) && (nr < pq->nr)) { altq = TAILQ_NEXT(altq, entries); nr++; } if (altq == NULL) { + PF_RULES_RUNLOCK(); error = EBUSY; break; } -#ifdef __FreeBSD__ if ((altq->local_flags & PFALTQ_FLAG_IF_REMOVED) != 0) { + PF_RULES_RUNLOCK(); error = ENXIO; break; } - PF_UNLOCK(); -#endif + PF_RULES_RUNLOCK(); error = altq_getqstats(altq, pq->buf, &nbytes); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif if (error == 0) { pq->scheduler = altq->scheduler; pq->nbytes = nbytes; @@ -2862,27 +2125,18 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCBEGINADDRS: { struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); pf_empty_pool(&V_pf_pabuf); pp->ticket = ++V_ticket_pabuf; -#else - pf_empty_pool(&pf_pabuf); - pp->ticket = ++ticket_pabuf; -#endif + PF_RULES_WUNLOCK(); break; } case DIOCADDADDR: { struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + struct pf_pooladdr *pa; + struct pfi_kif *kif = NULL; -#ifdef __FreeBSD__ - if (pp->ticket != V_ticket_pabuf) { -#else - if (pp->ticket != ticket_pabuf) { -#endif - error = EBUSY; - break; - } #ifndef INET if (pp->af == AF_INET) { error = EAFNOSUPPORT; @@ -2901,70 +2155,68 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; break; } -#ifdef __FreeBSD__ - pa = pool_get(&V_pf_pooladdr_pl, PR_NOWAIT); -#else - pa = pool_get(&pf_pooladdr_pl, PR_WAITOK|PR_LIMITFAIL); -#endif - if (pa == NULL) { - error = ENOMEM; + pa = malloc(sizeof(*pa), M_PFRULE, M_WAITOK); + bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); + if (pa->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + PF_RULES_WLOCK(); + if (pp->ticket != V_ticket_pabuf) { + PF_RULES_WUNLOCK(); + if (pa->ifname[0]) + free(kif, PFI_MTYPE); + free(pa, M_PFRULE); + error = EBUSY; break; } - bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); if (pa->ifname[0]) { - pa->kif = pfi_kif_get(pa->ifname); - if (pa->kif == NULL) { -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, pa); -#else - pool_put(&pf_pooladdr_pl, pa); -#endif - error = EINVAL; - break; - } - pfi_kif_ref(pa->kif, PFI_KIF_REF_RULE); - } - if (pfi_dynaddr_setup(&pa->addr, pp->af)) { - pfi_dynaddr_remove(&pa->addr); - pfi_kif_unref(pa->kif, PFI_KIF_REF_RULE); -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, pa); -#else - pool_put(&pf_pooladdr_pl, pa); -#endif - error = EINVAL; + pa->kif = pfi_kif_attach(kif, pa->ifname); + pfi_kif_ref(pa->kif); + } else + pa->kif = NULL; + if (pa->addr.type == PF_ADDR_DYNIFTL && ((error = + pfi_dynaddr_setup(&pa->addr, pp->af)) != 0)) { + if (pa->ifname[0]) + pfi_kif_unref(pa->kif); + PF_RULES_WUNLOCK(); + free(pa, M_PFRULE); break; } -#ifdef __FreeBSD__ TAILQ_INSERT_TAIL(&V_pf_pabuf, pa, entries); -#else - TAILQ_INSERT_TAIL(&pf_pabuf, pa, entries); -#endif + PF_RULES_WUNLOCK(); break; } case DIOCGETADDRS: { struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; + struct pf_pooladdr *pa; + PF_RULES_RLOCK(); pp->nr = 0; pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, pp->r_num, 0, 1, 0); if (pool == NULL) { + PF_RULES_RUNLOCK(); error = EBUSY; break; } TAILQ_FOREACH(pa, &pool->list, entries) pp->nr++; + PF_RULES_RUNLOCK(); break; } case DIOCGETADDR: { struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; + struct pf_pooladdr *pa; u_int32_t nr = 0; + PF_RULES_RLOCK(); pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, pp->r_num, 0, 1, 1); if (pool == NULL) { + PF_RULES_RUNLOCK(); error = EBUSY; break; } @@ -2974,18 +2226,22 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) nr++; } if (pa == NULL) { + PF_RULES_RUNLOCK(); error = EBUSY; break; } bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); pf_addr_copyout(&pp->addr.addr); + PF_RULES_RUNLOCK(); break; } case DIOCCHANGEADDR: { struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; struct pf_pooladdr *oldpa = NULL, *newpa = NULL; struct pf_ruleset *ruleset; + struct pfi_kif *kif = NULL; if (pca->action < PF_CHANGE_ADD_HEAD || pca->action > PF_CHANGE_REMOVE) { @@ -2999,76 +2255,60 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } - ruleset = pf_find_ruleset(pca->anchor); - if (ruleset == NULL) { - error = EBUSY; - break; - } - pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action, - pca->r_num, pca->r_last, 1, 1); - if (pool == NULL) { - error = EBUSY; - break; - } if (pca->action != PF_CHANGE_REMOVE) { -#ifdef __FreeBSD__ - newpa = pool_get(&V_pf_pooladdr_pl, - PR_NOWAIT); -#else - newpa = pool_get(&pf_pooladdr_pl, - PR_WAITOK|PR_LIMITFAIL); -#endif - if (newpa == NULL) { - error = ENOMEM; - break; - } - bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); #ifndef INET if (pca->af == AF_INET) { -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, newpa); -#else - pool_put(&pf_pooladdr_pl, newpa); -#endif error = EAFNOSUPPORT; break; } #endif /* INET */ #ifndef INET6 if (pca->af == AF_INET6) { -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, newpa); -#else - pool_put(&pf_pooladdr_pl, newpa); -#endif error = EAFNOSUPPORT; break; } #endif /* INET6 */ + newpa = malloc(sizeof(*newpa), M_PFRULE, M_WAITOK); + bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); + if (newpa->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + } + +#define ERROUT(x) { error = (x); goto DIOCCHANGEADDR_error; } + PF_RULES_WLOCK(); + ruleset = pf_find_ruleset(pca->anchor); + if (ruleset == NULL) + ERROUT(EBUSY); + + pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action, + pca->r_num, pca->r_last, 1, 1); + if (pool == NULL) + ERROUT(EBUSY); + + if (pca->action != PF_CHANGE_REMOVE) { if (newpa->ifname[0]) { - newpa->kif = pfi_kif_get(newpa->ifname); - if (newpa->kif == NULL) { -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, newpa); -#else - pool_put(&pf_pooladdr_pl, newpa); -#endif - error = EINVAL; - break; - } - pfi_kif_ref(newpa->kif, PFI_KIF_REF_RULE); + newpa->kif = pfi_kif_attach(kif, newpa->ifname); + pfi_kif_ref(newpa->kif); } else newpa->kif = NULL; - if (pfi_dynaddr_setup(&newpa->addr, pca->af) || - pf_tbladdr_setup(ruleset, &newpa->addr)) { - pfi_dynaddr_remove(&newpa->addr); - pfi_kif_unref(newpa->kif, PFI_KIF_REF_RULE); -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, newpa); -#else - pool_put(&pf_pooladdr_pl, newpa); -#endif - error = EINVAL; + + switch (newpa->addr.type) { + case PF_ADDR_DYNIFTL: + error = pfi_dynaddr_setup(&newpa->addr, + pca->af); + break; + case PF_ADDR_TABLE: + newpa->addr.p.tbl = pfr_attach_table(ruleset, + newpa->addr.v.tblname); + if (newpa->addr.p.tbl == NULL) + error = ENOMEM; + break; + } + if (error) { + if (newpa->kif) + pfi_kif_unref(newpa->kif); + PF_RULES_WUNLOCK(); + free(newpa, M_PFRULE); break; } } @@ -3086,6 +2326,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) i++; } if (oldpa == NULL) { + PF_RULES_WUNLOCK(); error = EINVAL; break; } @@ -3093,14 +2334,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pca->action == PF_CHANGE_REMOVE) { TAILQ_REMOVE(&pool->list, oldpa, entries); - pfi_dynaddr_remove(&oldpa->addr); - pf_tbladdr_remove(&oldpa->addr); - pfi_kif_unref(oldpa->kif, PFI_KIF_REF_RULE); -#ifdef __FreeBSD__ - pool_put(&V_pf_pooladdr_pl, oldpa); -#else - pool_put(&pf_pooladdr_pl, oldpa); -#endif + switch (oldpa->addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(oldpa->addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(oldpa->addr.p.tbl); + break; + } + if (oldpa->kif) + pfi_kif_unref(oldpa->kif); + free(oldpa, M_PFRULE); } else { if (oldpa == NULL) TAILQ_INSERT_TAIL(&pool->list, newpa, entries); @@ -3115,6 +2359,16 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) pool->cur = TAILQ_FIRST(&pool->list); PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, pca->af); + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCCHANGEADDR_error: + PF_RULES_WUNLOCK(); + if (newpa != NULL) + free(newpa, M_PFRULE); + if (kif != NULL) + free(kif, PFI_MTYPE); break; } @@ -3123,19 +2377,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_ruleset *ruleset; struct pf_anchor *anchor; + PF_RULES_RLOCK(); pr->path[sizeof(pr->path) - 1] = 0; if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { - error = EINVAL; + PF_RULES_RUNLOCK(); + error = ENOENT; break; } pr->nr = 0; if (ruleset->anchor == NULL) { /* XXX kludge for pf_main_ruleset */ -#ifdef __FreeBSD__ RB_FOREACH(anchor, pf_anchor_global, &V_pf_anchors) -#else - RB_FOREACH(anchor, pf_anchor_global, &pf_anchors) -#endif if (anchor->parent == NULL) pr->nr++; } else { @@ -3143,6 +2395,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) &ruleset->anchor->children) pr->nr++; } + PF_RULES_RUNLOCK(); break; } @@ -3152,19 +2405,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_anchor *anchor; u_int32_t nr = 0; + PF_RULES_RLOCK(); pr->path[sizeof(pr->path) - 1] = 0; if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { - error = EINVAL; + PF_RULES_RUNLOCK(); + error = ENOENT; break; } pr->name[0] = 0; if (ruleset->anchor == NULL) { /* XXX kludge for pf_main_ruleset */ -#ifdef __FreeBSD__ RB_FOREACH(anchor, pf_anchor_global, &V_pf_anchors) -#else - RB_FOREACH(anchor, pf_anchor_global, &pf_anchors) -#endif if (anchor->parent == NULL && nr++ == pr->nr) { strlcpy(pr->name, anchor->name, sizeof(pr->name)); @@ -3181,6 +2432,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } if (!pr->name[0]) error = EBUSY; + PF_RULES_RUNLOCK(); break; } @@ -3191,81 +2443,149 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENODEV; break; } + PF_RULES_WLOCK(); error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); break; } case DIOCRADDTABLES: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } - error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size, + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_add_tables(pfrts, io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); break; } case DIOCRDELTABLES: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } - error = pfr_del_tables(io->pfrio_buffer, io->pfrio_size, + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_del_tables(pfrts, io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); break; } case DIOCRGETTABLES: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } - error = pfr_get_tables(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + error = pfr_get_tables(&io->pfrio_table, pfrts, &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfrts, io->pfrio_buffer, totlen); + free(pfrts, M_TEMP); break; } case DIOCRGETTSTATS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_tstats *pfrtstats; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_tstats)) { error = ENODEV; break; } - error = pfr_get_tstats(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_tstats); + pfrtstats = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_WLOCK(); + error = pfr_get_tstats(&io->pfrio_table, pfrtstats, &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0) + error = copyout(pfrtstats, io->pfrio_buffer, totlen); + free(pfrtstats, M_TEMP); break; } case DIOCRCLRTSTATS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } - error = pfr_clr_tstats(io->pfrio_buffer, io->pfrio_size, + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_clr_tstats(pfrts, io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); break; } case DIOCRSETTFLAGS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } - error = pfr_set_tflags(io->pfrio_buffer, io->pfrio_size, + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_set_tflags(pfrts, io->pfrio_size, io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); break; } @@ -3276,332 +2596,398 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENODEV; break; } + PF_RULES_WLOCK(); error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); break; } case DIOCRADDADDRS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_add_addrs(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_add_addrs(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); break; } case DIOCRDELADDRS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_del_addrs(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_del_addrs(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); break; } case DIOCRSETADDRS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_set_addrs(&io->pfrio_table, io->pfrio_buffer, + totlen = (io->pfrio_size + io->pfrio_size2) * + sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_set_addrs(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags | PFR_FLAG_USERIOCTL, 0); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); break; } case DIOCRGETADDRS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + error = pfr_get_addrs(&io->pfrio_table, pfras, &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); break; } case DIOCRGETASTATS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_astats *pfrastats; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_astats)) { error = ENODEV; break; } - error = pfr_get_astats(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_astats); + pfrastats = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + error = pfr_get_astats(&io->pfrio_table, pfrastats, &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfrastats, io->pfrio_buffer, totlen); + free(pfrastats, M_TEMP); break; } case DIOCRCLRASTATS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_clr_astats(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_clr_astats(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); break; } case DIOCRTSTADDRS: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_RLOCK(); + error = pfr_tst_addrs(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); break; } case DIOCRINADEFINE: { struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; if (io->pfrio_esize != sizeof(struct pfr_addr)) { error = ENODEV; break; } - error = pfr_ina_define(&io->pfrio_table, io->pfrio_buffer, + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_ina_define(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr, io->pfrio_ticket, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfras, M_TEMP); break; } case DIOCOSFPADD: { struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; + PF_RULES_WLOCK(); error = pf_osfp_add(io); + PF_RULES_WUNLOCK(); break; } case DIOCOSFPGET: { struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; + PF_RULES_RLOCK(); error = pf_osfp_get(io); + PF_RULES_RUNLOCK(); break; } case DIOCXBEGIN: { struct pfioc_trans *io = (struct pfioc_trans *)addr; - struct pfioc_trans_e *ioe; - struct pfr_table *table; + struct pfioc_trans_e *ioes, *ioe; + size_t totlen; int i; if (io->esize != sizeof(*ioe)) { error = ENODEV; - goto fail; + break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - ioe = malloc(sizeof(*ioe), M_TEMP, M_WAITOK); - table = malloc(sizeof(*table), M_TEMP, M_WAITOK); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - for (i = 0; i < io->size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error); + totlen = sizeof(struct pfioc_trans_e) * io->size; + ioes = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->array, ioes, totlen); if (error) { -#else - if (copyin(io->array+i, ioe, sizeof(*ioe))) { -#endif - free(table, M_TEMP); - free(ioe, M_TEMP); - error = EFAULT; - goto fail; - } + free(ioes, M_TEMP); + break; + } + PF_RULES_WLOCK(); + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { switch (ioe->rs_num) { #ifdef ALTQ case PF_RULESET_ALTQ: if (ioe->anchor[0]) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EINVAL; goto fail; } if ((error = pf_begin_altq(&ioe->ticket))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; } break; #endif /* ALTQ */ case PF_RULESET_TABLE: - bzero(table, sizeof(*table)); - strlcpy(table->pfrt_anchor, ioe->anchor, - sizeof(table->pfrt_anchor)); - if ((error = pfr_ina_begin(table, + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_begin(&table, &ioe->ticket, NULL, 0))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; } break; + } default: if ((error = pf_begin_rules(&ioe->ticket, ioe->rs_num, ioe->anchor))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; } break; } -#ifdef __FreeBSD__ - PF_COPYOUT(ioe, io->array+i, sizeof(io->array[i]), - error); - if (error) { -#else - if (copyout(ioe, io->array+i, sizeof(io->array[i]))) { -#endif - free(table, M_TEMP); - free(ioe, M_TEMP); - error = EFAULT; - goto fail; - } } - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + error = copyout(ioes, io->array, totlen); + free(ioes, M_TEMP); break; } case DIOCXROLLBACK: { struct pfioc_trans *io = (struct pfioc_trans *)addr; - struct pfioc_trans_e *ioe; - struct pfr_table *table; + struct pfioc_trans_e *ioe, *ioes; + size_t totlen; int i; if (io->esize != sizeof(*ioe)) { error = ENODEV; - goto fail; + break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - ioe = malloc(sizeof(*ioe), M_TEMP, M_WAITOK); - table = malloc(sizeof(*table), M_TEMP, M_WAITOK); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - for (i = 0; i < io->size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error); - if (error) { -#else - if (copyin(io->array+i, ioe, sizeof(*ioe))) { -#endif - free(table, M_TEMP); - free(ioe, M_TEMP); - error = EFAULT; - goto fail; - } + totlen = sizeof(struct pfioc_trans_e) * io->size; + ioes = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->array, ioes, totlen); + if (error) { + free(ioes, M_TEMP); + break; + } + PF_RULES_WLOCK(); + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { switch (ioe->rs_num) { #ifdef ALTQ case PF_RULESET_ALTQ: if (ioe->anchor[0]) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EINVAL; goto fail; } if ((error = pf_rollback_altq(ioe->ticket))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; /* really bad */ } break; #endif /* ALTQ */ case PF_RULESET_TABLE: - bzero(table, sizeof(*table)); - strlcpy(table->pfrt_anchor, ioe->anchor, - sizeof(table->pfrt_anchor)); - if ((error = pfr_ina_rollback(table, + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_rollback(&table, ioe->ticket, NULL, 0))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; /* really bad */ } break; + } default: if ((error = pf_rollback_rules(ioe->ticket, ioe->rs_num, ioe->anchor))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; /* really bad */ } break; } } - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); break; } case DIOCXCOMMIT: { struct pfioc_trans *io = (struct pfioc_trans *)addr; - struct pfioc_trans_e *ioe; - struct pfr_table *table; + struct pfioc_trans_e *ioe, *ioes; struct pf_ruleset *rs; + size_t totlen; int i; if (io->esize != sizeof(*ioe)) { error = ENODEV; - goto fail; + break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - ioe = malloc(sizeof(*ioe), M_TEMP, M_WAITOK); - table = malloc(sizeof(*table), M_TEMP, M_WAITOK); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - /* first makes sure everything will succeed */ - for (i = 0; i < io->size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error); - if (error) { -#else - if (copyin(io->array+i, ioe, sizeof(*ioe))) { -#endif - free(table, M_TEMP); - free(ioe, M_TEMP); - error = EFAULT; - goto fail; - } + totlen = sizeof(struct pfioc_trans_e) * io->size; + ioes = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->array, ioes, totlen); + if (error) { + free(ioes, M_TEMP); + break; + } + PF_RULES_WLOCK(); + /* First makes sure everything will succeed. */ + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { switch (ioe->rs_num) { #ifdef ALTQ case PF_RULESET_ALTQ: if (ioe->anchor[0]) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EINVAL; goto fail; } -#ifdef __FreeBSD__ if (!V_altqs_inactive_open || ioe->ticket != V_ticket_altqs_inactive) { -#else - if (!altqs_inactive_open || ioe->ticket != - ticket_altqs_inactive) { -#endif - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EBUSY; goto fail; } @@ -3611,8 +2997,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) rs = pf_find_ruleset(ioe->anchor); if (rs == NULL || !rs->topen || ioe->ticket != rs->tticket) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EBUSY; goto fail; } @@ -3620,8 +3006,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) default: if (ioe->rs_num < 0 || ioe->rs_num >= PF_RULESET_MAX) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EINVAL; goto fail; } @@ -3630,175 +3016,141 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) !rs->rules[ioe->rs_num].inactive.open || rs->rules[ioe->rs_num].inactive.ticket != ioe->ticket) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); error = EBUSY; goto fail; } break; } } - /* now do the commit - no errors should happen here */ - for (i = 0; i < io->size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error); - if (error) { -#else - if (copyin(io->array+i, ioe, sizeof(*ioe))) { -#endif - free(table, M_TEMP); - free(ioe, M_TEMP); - error = EFAULT; - goto fail; - } + /* Now do the commit - no errors should happen here. */ + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { switch (ioe->rs_num) { #ifdef ALTQ case PF_RULESET_ALTQ: if ((error = pf_commit_altq(ioe->ticket))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; /* really bad */ } break; #endif /* ALTQ */ case PF_RULESET_TABLE: - bzero(table, sizeof(*table)); - strlcpy(table->pfrt_anchor, ioe->anchor, - sizeof(table->pfrt_anchor)); - if ((error = pfr_ina_commit(table, ioe->ticket, - NULL, NULL, 0))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_commit(&table, + ioe->ticket, NULL, NULL, 0))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; /* really bad */ } break; + } default: if ((error = pf_commit_rules(ioe->ticket, ioe->rs_num, ioe->anchor))) { - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); goto fail; /* really bad */ } break; } } - free(table, M_TEMP); - free(ioe, M_TEMP); + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); break; } case DIOCGETSRCNODES: { struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr; + struct pf_srchash *sh; struct pf_src_node *n, *p, *pstore; - u_int32_t nr = 0; - int space = psn->psn_len; - - if (space == 0) { -#ifdef __FreeBSD__ - RB_FOREACH(n, pf_src_tree, &V_tree_src_tracking) -#else - RB_FOREACH(n, pf_src_tree, &tree_src_tracking) -#endif - nr++; + uint32_t i, nr = 0; + + if (psn->psn_len == 0) { + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) + nr++; + PF_HASHROW_UNLOCK(sh); + } psn->psn_len = sizeof(struct pf_src_node) * nr; break; } -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - pstore = malloc(sizeof(*pstore), M_TEMP, M_WAITOK); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - p = psn->psn_src_nodes; -#ifdef __FreeBSD__ - RB_FOREACH(n, pf_src_tree, &V_tree_src_tracking) { -#else - RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { -#endif - int secs = time_second, diff; + p = pstore = malloc(psn->psn_len, M_TEMP, M_WAITOK); + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) { + int secs = time_uptime, diff; if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len) break; - bcopy(n, pstore, sizeof(*pstore)); + bcopy(n, p, sizeof(struct pf_src_node)); if (n->rule.ptr != NULL) - pstore->rule.nr = n->rule.ptr->nr; - pstore->creation = secs - pstore->creation; - if (pstore->expire > secs) - pstore->expire -= secs; + p->rule.nr = n->rule.ptr->nr; + p->creation = secs - p->creation; + if (p->expire > secs) + p->expire -= secs; else - pstore->expire = 0; + p->expire = 0; - /* adjust the connection rate estimate */ + /* Adjust the connection rate estimate. */ diff = secs - n->conn_rate.last; if (diff >= n->conn_rate.seconds) - pstore->conn_rate.count = 0; + p->conn_rate.count = 0; else - pstore->conn_rate.count -= + p->conn_rate.count -= n->conn_rate.count * diff / n->conn_rate.seconds; - -#ifdef __FreeBSD__ - PF_COPYOUT(pstore, p, sizeof(*p), error); -#else - error = copyout(pstore, p, sizeof(*p)); -#endif - if (error) { - free(pstore, M_TEMP); - goto fail; - } p++; nr++; + } + PF_HASHROW_UNLOCK(sh); + } + error = copyout(pstore, psn->psn_src_nodes, + sizeof(struct pf_src_node) * nr); + if (error) { + free(pstore, M_TEMP); + break; } psn->psn_len = sizeof(struct pf_src_node) * nr; - free(pstore, M_TEMP); break; } case DIOCCLRSRCNODES: { - struct pf_src_node *n; - struct pf_state *state; -#ifdef __FreeBSD__ - RB_FOREACH(state, pf_state_tree_id, &V_tree_id) { -#else - RB_FOREACH(state, pf_state_tree_id, &tree_id) { -#endif - state->src_node = NULL; - state->nat_src_node = NULL; - } -#ifdef __FreeBSD__ - RB_FOREACH(n, pf_src_tree, &V_tree_src_tracking) { -#else - RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { -#endif - n->expire = 1; - n->states = 0; - } - pf_purge_expired_src_nodes(1); -#ifdef __FreeBSD__ + pf_clear_srcnodes(NULL); + pf_purge_expired_src_nodes(); V_pf_status.src_nodes = 0; -#else - pf_status.src_nodes = 0; -#endif break; } case DIOCKILLSRCNODES: { - struct pf_src_node *sn; - struct pf_state *s; struct pfioc_src_node_kill *psnk = (struct pfioc_src_node_kill *)addr; - u_int killed = 0; - -#ifdef __FreeBSD__ - RB_FOREACH(sn, pf_src_tree, &V_tree_src_tracking) { -#else - RB_FOREACH(sn, pf_src_tree, &tree_src_tracking) { -#endif + struct pf_srchash *sh; + struct pf_src_node *sn; + u_int i, killed = 0; + + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + /* + * XXXGL: we don't ever acquire sources hash lock + * but if we ever do, the below call to pf_clear_srcnodes() + * would lead to a LOR. + */ + PF_HASHROW_LOCK(sh); + LIST_FOREACH(sn, &sh->nodes, entry) if (PF_MATCHA(psnk->psnk_src.neg, &psnk->psnk_src.addr.v.a.addr, &psnk->psnk_src.addr.v.a.mask, @@ -3808,27 +3160,16 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) &psnk->psnk_dst.addr.v.a.mask, &sn->raddr, sn->af)) { /* Handle state to src_node linkage */ - if (sn->states != 0) { - RB_FOREACH(s, pf_state_tree_id, -#ifdef __FreeBSD__ - &V_tree_id) { -#else - &tree_id) { -#endif - if (s->src_node == sn) - s->src_node = NULL; - if (s->nat_src_node == sn) - s->nat_src_node = NULL; - } - sn->states = 0; - } + if (sn->states != 0) + pf_clear_srcnodes(sn); sn->expire = 1; killed++; } + PF_HASHROW_UNLOCK(sh); } if (killed > 0) - pf_purge_expired_src_nodes(1); + pf_purge_expired_src_nodes(); psnk->psnk_killed = killed; break; @@ -3837,47 +3178,56 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCSETHOSTID: { u_int32_t *hostid = (u_int32_t *)addr; -#ifdef __FreeBSD__ + PF_RULES_WLOCK(); if (*hostid == 0) V_pf_status.hostid = arc4random(); else V_pf_status.hostid = *hostid; -#else - if (*hostid == 0) - pf_status.hostid = arc4random(); - else - pf_status.hostid = *hostid; -#endif + PF_RULES_WUNLOCK(); break; } case DIOCOSFPFLUSH: + PF_RULES_WLOCK(); pf_osfp_flush(); + PF_RULES_WUNLOCK(); break; case DIOCIGETIFACES: { struct pfioc_iface *io = (struct pfioc_iface *)addr; + struct pfi_kif *ifstore; + size_t bufsiz; if (io->pfiio_esize != sizeof(struct pfi_kif)) { error = ENODEV; break; } - error = pfi_get_ifaces(io->pfiio_name, io->pfiio_buffer, - &io->pfiio_size); + + bufsiz = io->pfiio_size * sizeof(struct pfi_kif); + ifstore = malloc(bufsiz, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + pfi_get_ifaces(io->pfiio_name, ifstore, &io->pfiio_size); + PF_RULES_RUNLOCK(); + error = copyout(ifstore, io->pfiio_buffer, bufsiz); + free(ifstore, M_TEMP); break; } case DIOCSETIFFLAG: { struct pfioc_iface *io = (struct pfioc_iface *)addr; + PF_RULES_WLOCK(); error = pfi_set_flags(io->pfiio_name, io->pfiio_flags); + PF_RULES_WUNLOCK(); break; } case DIOCCLRIFFLAG: { struct pfioc_iface *io = (struct pfioc_iface *)addr; + PF_RULES_WLOCK(); error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags); + PF_RULES_WUNLOCK(); break; } @@ -3886,27 +3236,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } fail: -#ifdef __FreeBSD__ - PF_UNLOCK(); - - if (flags & FWRITE) - sx_xunlock(&V_pf_consistency_lock); - else - sx_sunlock(&V_pf_consistency_lock); -#else - splx(s); - if (flags & FWRITE) - rw_exit_write(&pf_consistency_lock); - else - rw_exit_read(&pf_consistency_lock); -#endif - CURVNET_RESTORE(); return (error); } -#ifdef __FreeBSD__ void pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) { @@ -3927,12 +3261,12 @@ pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) /* copy from state */ strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname)); bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); - sp->creation = htonl(time_second - st->creation); + sp->creation = htonl(time_uptime - st->creation); sp->expire = pf_state_expires(st); - if (sp->expire <= time_second) + if (sp->expire <= time_uptime) sp->expire = htonl(0); else - sp->expire = htonl(sp->expire - time_second); + sp->expire = htonl(sp->expire - time_uptime); sp->direction = st->direction; sp->log = st->log; @@ -3943,7 +3277,7 @@ pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) if (st->nat_src_node) sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE; - bcopy(&st->id, &sp->id, sizeof(sp->id)); + sp->id = st->id; sp->creatorid = st->creatorid; pf_state_peer_hton(&st->src, &sp->src); pf_state_peer_hton(&st->dst, &sp->dst); @@ -3968,33 +3302,43 @@ pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) } +static void +pf_tbladdr_copyout(struct pf_addr_wrap *aw) +{ + struct pfr_ktable *kt; + + KASSERT(aw->type == PF_ADDR_TABLE, ("%s: type %u", __func__, aw->type)); + + kt = aw->p.tbl; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + aw->p.tbl = NULL; + aw->p.tblcnt = (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) ? + kt->pfrkt_cnt : -1; +} + /* * XXX - Check for version missmatch!!! */ static void pf_clear_states(void) { - struct pf_state *state; - -#ifdef __FreeBSD__ - RB_FOREACH(state, pf_state_tree_id, &V_tree_id) { -#else - RB_FOREACH(state, pf_state_tree_id, &tree_id) { -#endif - state->timeout = PFTM_PURGE; -#if NPFSYNC - /* don't send out individual delete messages */ - state->sync_state = PFSTATE_NOSYNC; -#endif - pf_unlink_state(state); + struct pf_state *s; + u_int i; + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; +relock: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + s->timeout = PFTM_PURGE; + /* Don't send out individual delete messages. */ + s->sync_state = PFSTATE_NOSYNC; + pf_unlink_state(s, PF_ENTER_LOCKED); + goto relock; + } + PF_HASHROW_UNLOCK(ih); } - -#if 0 /* NPFSYNC */ -/* - * XXX This is called on module unload, we do not want to sync that over? */ - */ - pfsync_clear_states(V_pf_status.hostid, psk->psk_ifname); -#endif } static int @@ -4012,24 +3356,38 @@ pf_clear_tables(void) } static void -pf_clear_srcnodes(void) +pf_clear_srcnodes(struct pf_src_node *n) { - struct pf_src_node *n; - struct pf_state *state; + struct pf_state *s; + int i; -#ifdef __FreeBSD__ - RB_FOREACH(state, pf_state_tree_id, &V_tree_id) { -#else - RB_FOREACH(state, pf_state_tree_id, &tree_id) { -#endif - state->src_node = NULL; - state->nat_src_node = NULL; + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (n == NULL || n == s->src_node) + s->src_node = NULL; + if (n == NULL || n == s->nat_src_node) + s->nat_src_node = NULL; + } + PF_HASHROW_UNLOCK(ih); } -#ifdef __FreeBSD__ - RB_FOREACH(n, pf_src_tree, &V_tree_src_tracking) { -#else - RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { -#endif + + if (n == NULL) { + struct pf_srchash *sh; + + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) { + n->expire = 1; + n->states = 0; + } + PF_HASHROW_UNLOCK(sh); + } + } else { + /* XXX: hash slot should already be locked here. */ n->expire = 1; n->states = 0; } @@ -4047,7 +3405,7 @@ shutdown_pf(void) int error = 0; u_int32_t t[5]; char nn = '\0'; - + V_pf_status.running = 0; do { if ((error = pf_begin_rules(&t[0], PF_RULESET_SCRUB, &nn)) @@ -4058,22 +3416,22 @@ shutdown_pf(void) if ((error = pf_begin_rules(&t[1], PF_RULESET_FILTER, &nn)) != 0) { DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: FILTER\n")); - break; /* XXX: rollback? */ + break; /* XXX: rollback? */ } if ((error = pf_begin_rules(&t[2], PF_RULESET_NAT, &nn)) != 0) { DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: NAT\n")); - break; /* XXX: rollback? */ + break; /* XXX: rollback? */ } if ((error = pf_begin_rules(&t[3], PF_RULESET_BINAT, &nn)) != 0) { DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: BINAT\n")); - break; /* XXX: rollback? */ + break; /* XXX: rollback? */ } if ((error = pf_begin_rules(&t[4], PF_RULESET_RDR, &nn)) != 0) { DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: RDR\n")); - break; /* XXX: rollback? */ + break; /* XXX: rollback? */ } /* XXX: these should always succeed here */ @@ -4086,17 +3444,17 @@ shutdown_pf(void) if ((error = pf_clear_tables()) != 0) break; - #ifdef ALTQ +#ifdef ALTQ if ((error = pf_begin_altq(&t[0])) != 0) { DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: ALTQ\n")); break; } pf_commit_altq(t[0]); - #endif +#endif pf_clear_states(); - pf_clear_srcnodes(); + pf_clear_srcnodes(NULL); /* status does not use malloced mem so no need to cleanup */ /* fingerprints and interfaces have thier own cleanup code */ @@ -4116,7 +3474,7 @@ pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, * in network stack. OpenBSD's network stack have converted * ip_len/ip_off to host byte order frist as FreeBSD. * Now this is not true anymore , so we should convert back to network - * byte order. + * byte order. */ struct ip *h = NULL; int chk; @@ -4128,7 +3486,7 @@ pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, HTONS(h->ip_off); } CURVNET_SET(ifp->if_vnet); - chk = pf_test(PF_IN, ifp, m, NULL, inp); + chk = pf_test(PF_IN, ifp, m, inp); CURVNET_RESTORE(); if (chk && *m) { m_freem(*m); @@ -4153,7 +3511,7 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, * in network stack. OpenBSD's network stack have converted * ip_len/ip_off to host byte order frist as FreeBSD. * Now this is not true anymore , so we should convert back to network - * byte order. + * byte order. */ struct ip *h = NULL; int chk; @@ -4170,7 +3528,7 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, HTONS(h->ip_off); } CURVNET_SET(ifp->if_vnet); - chk = pf_test(PF_OUT, ifp, m, NULL, inp); + chk = pf_test(PF_OUT, ifp, m, inp); CURVNET_RESTORE(); if (chk && *m) { m_freem(*m); @@ -4203,8 +3561,7 @@ pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, * filtering we have change this to lo0 as it is the case in IPv4. */ CURVNET_SET(ifp->if_vnet); - chk = pf_test6(PF_IN, (*m)->m_flags & M_LOOP ? V_loif : ifp, m, - NULL, inp); + chk = pf_test6(PF_IN, (*m)->m_flags & M_LOOP ? V_loif : ifp, m, inp); CURVNET_RESTORE(); if (chk && *m) { m_freem(*m); @@ -4231,7 +3588,7 @@ pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } CURVNET_SET(ifp->if_vnet); - chk = pf_test6(PF_OUT, ifp, m, NULL, inp); + chk = pf_test6(PF_OUT, ifp, m, inp); CURVNET_RESTORE(); if (chk && *m) { m_freem(*m); @@ -4251,10 +3608,8 @@ hook_pf(void) struct pfil_head *pfh_inet6; #endif - PF_UNLOCK_ASSERT(); - if (V_pf_pfil_hooked) - return (0); + return (0); #ifdef INET pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); @@ -4292,8 +3647,6 @@ dehook_pf(void) struct pfil_head *pfh_inet6; #endif - PF_UNLOCK_ASSERT(); - if (V_pf_pfil_hooked == 0) return (0); @@ -4323,6 +3676,8 @@ dehook_pf(void) static int pf_load(void) { + int error; + VNET_ITERATOR_DECL(vnet_iter); VNET_LIST_RLOCK(); @@ -4330,19 +3685,17 @@ pf_load(void) CURVNET_SET(vnet_iter); V_pf_pfil_hooked = 0; V_pf_end_threads = 0; - V_debug_pfugidhack = 0; TAILQ_INIT(&V_pf_tags); TAILQ_INIT(&V_pf_qids); CURVNET_RESTORE(); } VNET_LIST_RUNLOCK(); - init_pf_mutex(); + rw_init(&pf_rules_lock, "pf rulesets"); + pf_dev = make_dev(&pf_cdevsw, 0, 0, 0, 0600, PF_NAME); - init_zone_var(); - sx_init(&V_pf_consistency_lock, "pf_statetbl_lock"); - if (pfattach() < 0) - return (ENOMEM); + if ((error = pfattach()) != 0) + return (error); return (0); } @@ -4352,10 +3705,10 @@ pf_unload(void) { int error = 0; - PF_LOCK(); + PF_RULES_WLOCK(); V_pf_status.running = 0; - PF_UNLOCK(); - m_addr_chg_pf_p = NULL; + PF_RULES_WUNLOCK(); + swi_remove(V_pf_swi_cookie); error = dehook_pf(); if (error) { /* @@ -4366,22 +3719,23 @@ pf_unload(void) printf("%s : pfil unregisteration fail\n", __FUNCTION__); return error; } - PF_LOCK(); + PF_RULES_WLOCK(); shutdown_pf(); V_pf_end_threads = 1; while (V_pf_end_threads < 2) { wakeup_one(pf_purge_thread); - msleep(pf_purge_thread, &pf_task_mtx, 0, "pftmo", hz); + rw_sleep(pf_purge_thread, &pf_rules_lock, 0, "pftmo", 0); } + pf_normalize_cleanup(); pfi_cleanup(); + pfr_cleanup(); pf_osfp_flush(); - pf_osfp_cleanup(); - cleanup_pf_zone(); - PF_UNLOCK(); + pf_cleanup(); + PF_RULES_WUNLOCK(); destroy_dev(pf_dev); - destroy_pf_mutex(); - sx_destroy(&V_pf_consistency_lock); - return error; + rw_destroy(&pf_rules_lock); + + return (error); } static int @@ -4406,9 +3760,10 @@ pf_modevent(module_t mod, int type, void *data) error = EINVAL; break; } - return error; + + return (error); } - + static moduledata_t pf_mod = { "pf", pf_modevent, @@ -4417,4 +3772,3 @@ static moduledata_t pf_mod = { DECLARE_MODULE(pf, pf_mod, SI_SUB_PSEUDO, SI_ORDER_FIRST); MODULE_VERSION(pf, PF_MODVER); -#endif /* __FreeBSD__ */ diff --git a/sys/contrib/pf/net/pf_lb.c b/sys/contrib/pf/net/pf_lb.c index 4adc6f00e8db0..5b47852a5d1b4 100644 --- a/sys/contrib/pf/net/pf_lb.c +++ b/sys/contrib/pf/net/pf_lb.c @@ -35,136 +35,31 @@ * */ -#ifdef __FreeBSD__ -#include "opt_inet.h" -#include "opt_inet6.h" - #include __FBSDID("$FreeBSD$"); -#endif -#ifdef __FreeBSD__ -#include "opt_bpf.h" #include "opt_pf.h" - -#ifdef DEV_BPF -#define NBPFILTER DEV_BPF -#else -#define NBPFILTER 0 -#endif - -#ifdef DEV_PFLOG -#define NPFLOG DEV_PFLOG -#else -#define NPFLOG 0 -#endif - -#ifdef DEV_PFSYNC -#define NPFSYNC DEV_PFSYNC -#else -#define NPFSYNC 0 -#endif - -#ifdef DEV_PFLOW -#define NPFLOW DEV_PFLOW -#else -#define NPFLOW 0 -#endif - -#else -#include "bpfilter.h" -#include "pflog.h" -#include "pfsync.h" -#include "pflow.h" -#endif +#include "opt_inet.h" +#include "opt_inet6.h" #include -#include -#include -#include #include -#include -#include -#include -#ifdef __FreeBSD__ #include -#endif -#ifndef __FreeBSD__ -#include -#endif -#include -#ifdef __FreeBSD__ -#include -#include -#include -#else -#include -#endif - -#ifdef __FreeBSD__ -#include -#else -#include -#endif #include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __FreeBSD__ -#include -#endif #include #include -#include - -#if NPFSYNC > 0 -#include -#endif /* NPFSYNC > 0 */ - -#ifdef INET6 -#include -#include -#include -#include -#endif /* INET6 */ - +#include -#ifdef __FreeBSD__ #define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x -#else -#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x -#endif - -/* - * Global variables - */ -void pf_hash(struct pf_addr *, struct pf_addr *, +static void pf_hash(struct pf_addr *, struct pf_addr *, struct pf_poolhashkey *, sa_family_t); -struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, +static struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, int, int, struct pfi_kif *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, int); -int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, +static int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, struct pf_addr *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, struct pf_src_node **); @@ -185,7 +80,7 @@ int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, /* * hash function based on bridge_hash in if_bridge.c */ -void +static void pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, struct pf_poolhashkey *key, sa_family_t af) { @@ -226,7 +121,7 @@ pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, } } -struct pf_rule * +static struct pf_rule * pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, u_int16_t dport, int rs_num) @@ -279,11 +174,8 @@ pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, !pf_match_port(dst->port_op, dst->port[0], dst->port[1], dport)) r = r->skip[PF_SKIP_DST_PORT].ptr; -#ifdef __FreeBSD__ - else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag)) -#else - else if (r->match_tag && !pf_match_tag(m, r, &tag)) -#endif + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m, @@ -304,19 +196,19 @@ pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, pf_step_out_of_anchor(&asd, &ruleset, rs_num, &r, NULL, NULL); } -#ifdef __FreeBSD__ - if (pf_tag_packet(m, tag, rtableid, pd->pf_mtag)) -#else - if (pf_tag_packet(m, tag, rtableid)) -#endif + + if (tag > 0 && pf_tag_packet(m, pd, tag)) return (NULL); + if (rtableid >= 0) + M_SETFIB(m, rtableid); + if (rm != NULL && (rm->action == PF_NONAT || rm->action == PF_NORDR || rm->action == PF_NOBINAT)) return (NULL); return (rm); } -int +static int pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, @@ -370,20 +262,12 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, high = tmp; } /* low < high */ -#ifdef __FreeBSD__ cut = htonl(arc4random()) % (1 + high - low) + low; -#else - cut = arc4random_uniform(1 + high - low) + low; -#endif /* low <= cut <= high */ for (tmp = cut; tmp <= high; ++(tmp)) { key.port[0] = htons(tmp); if (pf_find_state_all(&key, PF_IN, NULL) == -#ifdef __FreeBSD__ NULL) { -#else - NULL && !in_baddynamic(tmp, proto)) { -#endif *nport = htons(tmp); return (0); } @@ -391,11 +275,7 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, for (tmp = cut - 1; tmp >= low; --(tmp)) { key.port[0] = htons(tmp); if (pf_find_state_all(&key, PF_IN, NULL) == -#ifdef __FreeBSD__ NULL) { -#else - NULL && !in_baddynamic(tmp, proto)) { -#endif *nport = htons(tmp); return (0); } @@ -422,38 +302,17 @@ int pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) { - unsigned char hash[16]; struct pf_pool *rpool = &r->rpool; - struct pf_addr *raddr = &rpool->cur->addr.v.a.addr; - struct pf_addr *rmask = &rpool->cur->addr.v.a.mask; - struct pf_pooladdr *acur = rpool->cur; - struct pf_src_node k; + struct pf_addr *raddr = NULL, *rmask = NULL; if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { - k.af = af; - PF_ACPY(&k.addr, saddr, af); - if (r->rule_flag & PFRULE_RULESRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) - k.rule.ptr = r; - else - k.rule.ptr = NULL; -#ifdef __FreeBSD__ - V_pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; - *sn = RB_FIND(pf_src_tree, &V_tree_src_tracking, &k); -#else - pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; - *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); -#endif + *sn = pf_find_src_node(saddr, r, af, 0); if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { PF_ACPY(naddr, &(*sn)->raddr, af); -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif printf("pf_map_addr: src tracking maps "); - pf_print_host(&k.addr, 0, af); + pf_print_host(saddr, 0, af); printf(" to "); pf_print_host(naddr, 0, af); printf("\n"); @@ -542,31 +401,58 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, } break; case PF_POOL_SRCHASH: + { + unsigned char hash[16]; + pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); break; + } case PF_POOL_ROUNDROBIN: + { + struct pf_pooladdr *acur = rpool->cur; + + /* + * XXXGL: in the round-robin case we need to store + * the round-robin machine state in the rule, thus + * forwarding thread needs to modify rule. + * + * This is done w/o locking, because performance is assumed + * more important than round-robin precision. + * + * In the simpliest case we just update the "rpool->cur" + * pointer. However, if pool contains tables or dynamic + * addresses, then "tblidx" is also used to store machine + * state. Since "tblidx" is int, concurrent access to it can't + * lead to inconsistence, only to lost of precision. + * + * Things get worse, if table contains not hosts, but + * prefixes. In this case counter also stores machine state, + * and for IPv6 address, counter can't be updated atomically. + * Probably, using round-robin on a table containing IPv6 + * prefixes (or even IPv4) would cause a panic. + */ + if (rpool->cur->addr.type == PF_ADDR_TABLE) { if (!pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) + &rpool->tblidx, &rpool->counter, af)) goto get_addr; } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) + &rpool->tblidx, &rpool->counter, af)) goto get_addr; } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) goto get_addr; try_next: - if ((rpool->cur = TAILQ_NEXT(rpool->cur, entries)) == NULL) + if (TAILQ_NEXT(rpool->cur, entries) == NULL) rpool->cur = TAILQ_FIRST(&rpool->list); + else + rpool->cur = TAILQ_NEXT(rpool->cur, entries); if (rpool->cur->addr.type == PF_ADDR_TABLE) { rpool->tblidx = -1; if (pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) { + &rpool->tblidx, &rpool->counter, af)) { /* table contains no address of type 'af' */ if (rpool->cur != acur) goto try_next; @@ -575,8 +461,7 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { rpool->tblidx = -1; if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) { + &rpool->tblidx, &rpool->counter, af)) { /* table contains no address of type 'af' */ if (rpool->cur != acur) goto try_next; @@ -594,15 +479,12 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, PF_ACPY(init_addr, naddr, af); PF_AINC(&rpool->counter, af); break; + } } if (*sn != NULL) PF_ACPY(&(*sn)->raddr, naddr, af); -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC && -#else - if (pf_status.debug >= PF_DEBUG_MISC && -#endif (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { printf("pf_map_addr: selected address "); pf_print_host(naddr, 0, af); @@ -615,13 +497,17 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, struct pf_rule * pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, struct pfi_kif *kif, struct pf_src_node **sn, - struct pf_state_key **skw, struct pf_state_key **sks, struct pf_state_key **skp, struct pf_state_key **nkp, struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t sport, u_int16_t dport) { struct pf_rule *r = NULL; + struct pf_addr *naddr; + uint16_t *nport; + PF_RULES_RASSERT(); + KASSERT(*skp == NULL, ("*skp not NULL")); + KASSERT(*nkp == NULL, ("*nkp not NULL")); if (direction == PF_OUT) { r = pf_match_translation(pd, m, off, direction, kif, saddr, @@ -637,157 +523,141 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, saddr, sport, daddr, dport, PF_RULESET_BINAT); } - if (r != NULL) { - struct pf_addr *naddr; - u_int16_t *nport; - - if (pf_state_key_setup(pd, r, skw, sks, skp, nkp, - saddr, daddr, sport, dport)) - return r; - - /* XXX We only modify one side for now. */ - naddr = &(*nkp)->addr[1]; - nport = &(*nkp)->port[1]; - - switch (r->action) { - case PF_NONAT: - case PF_NOBINAT: - case PF_NORDR: - return (NULL); - case PF_NAT: - if (pf_get_sport(pd->af, pd->proto, r, saddr, - daddr, dport, naddr, nport, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], sn)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: NAT proxy port allocation " - "(%u-%u) failed\n", - r->rpool.proxy_port[0], - r->rpool.proxy_port[1])); - return (NULL); - } - break; - case PF_BINAT: - switch (direction) { - case PF_OUT: - if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ - switch (pd->af) { + if (r == NULL) + return (NULL); + + switch (r->action) { + case PF_NONAT: + case PF_NOBINAT: + case PF_NORDR: + return (NULL); + } + + *skp = pf_state_key_setup(pd, saddr, daddr, sport, dport); + if (*skp == NULL) + return (NULL); + *nkp = pf_state_key_clone(*skp); + if (*nkp == NULL) { + uma_zfree(V_pf_state_key_z, skp); + *skp = NULL; + return (NULL); + } + + /* XXX We only modify one side for now. */ + naddr = &(*nkp)->addr[1]; + nport = &(*nkp)->port[1]; + + switch (r->action) { + case PF_NAT: + if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, + naddr, nport, r->rpool.proxy_port[0], + r->rpool.proxy_port[1], sn)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: NAT proxy port allocation (%u-%u) failed\n", + r->rpool.proxy_port[0], r->rpool.proxy_port[1])); + goto notrans; + } + break; + case PF_BINAT: + switch (direction) { + case PF_OUT: + if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ + switch (pd->af) { #ifdef INET - case AF_INET: - if (r->rpool.cur->addr.p.dyn-> - pfid_acnt4 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->rpool.cur->addr.p.dyn-> - pfid_addr4, - &r->rpool.cur->addr.p.dyn-> - pfid_mask4, - saddr, AF_INET); - break; + case AF_INET: + if (r->rpool.cur->addr.p.dyn-> + pfid_acnt4 < 1) + goto notrans; + PF_POOLMASK(naddr, + &r->rpool.cur->addr.p.dyn-> + pfid_addr4, + &r->rpool.cur->addr.p.dyn-> + pfid_mask4, saddr, AF_INET); + break; #endif /* INET */ #ifdef INET6 - case AF_INET6: - if (r->rpool.cur->addr.p.dyn-> - pfid_acnt6 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->rpool.cur->addr.p.dyn-> - pfid_addr6, - &r->rpool.cur->addr.p.dyn-> - pfid_mask6, - saddr, AF_INET6); - break; -#endif /* INET6 */ - } - } else + case AF_INET6: + if (r->rpool.cur->addr.p.dyn-> + pfid_acnt6 < 1) + goto notrans; PF_POOLMASK(naddr, - &r->rpool.cur->addr.v.a.addr, - &r->rpool.cur->addr.v.a.mask, - saddr, pd->af); - break; - case PF_IN: - if (r->src.addr.type == PF_ADDR_DYNIFTL) { - switch (pd->af) { + &r->rpool.cur->addr.p.dyn-> + pfid_addr6, + &r->rpool.cur->addr.p.dyn-> + pfid_mask6, saddr, AF_INET6); + break; +#endif /* INET6 */ + } + } else + PF_POOLMASK(naddr, + &r->rpool.cur->addr.v.a.addr, + &r->rpool.cur->addr.v.a.mask, saddr, + pd->af); + break; + case PF_IN: + if (r->src.addr.type == PF_ADDR_DYNIFTL) { + switch (pd->af) { #ifdef INET - case AF_INET: - if (r->src.addr.p.dyn-> - pfid_acnt4 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->src.addr.p.dyn-> - pfid_addr4, - &r->src.addr.p.dyn-> - pfid_mask4, - daddr, AF_INET); - break; + case AF_INET: + if (r->src.addr.p.dyn-> pfid_acnt4 < 1) + goto notrans; + PF_POOLMASK(naddr, + &r->src.addr.p.dyn->pfid_addr4, + &r->src.addr.p.dyn->pfid_mask4, + daddr, AF_INET); + break; #endif /* INET */ #ifdef INET6 - case AF_INET6: - if (r->src.addr.p.dyn-> - pfid_acnt6 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->src.addr.p.dyn-> - pfid_addr6, - &r->src.addr.p.dyn-> - pfid_mask6, - daddr, AF_INET6); - break; -#endif /* INET6 */ - } - } else + case AF_INET6: + if (r->src.addr.p.dyn->pfid_acnt6 < 1) + goto notrans; PF_POOLMASK(naddr, - &r->src.addr.v.a.addr, - &r->src.addr.v.a.mask, daddr, - pd->af); - break; - } - break; - case PF_RDR: { - if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) - return (NULL); - if ((r->rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_BITMASK) - PF_POOLMASK(naddr, naddr, - &r->rpool.cur->addr.v.a.mask, daddr, - pd->af); - - if (r->rpool.proxy_port[1]) { - u_int32_t tmp_nport; - - tmp_nport = ((ntohs(dport) - - ntohs(r->dst.port[0])) % - (r->rpool.proxy_port[1] - - r->rpool.proxy_port[0] + 1)) + - r->rpool.proxy_port[0]; - - /* wrap around if necessary */ - if (tmp_nport > 65535) - tmp_nport -= 65535; - *nport = htons((u_int16_t)tmp_nport); - } else if (r->rpool.proxy_port[0]) - *nport = htons(r->rpool.proxy_port[0]); + &r->src.addr.p.dyn->pfid_addr6, + &r->src.addr.p.dyn->pfid_mask6, + daddr, AF_INET6); + break; +#endif /* INET6 */ + } + } else + PF_POOLMASK(naddr, &r->src.addr.v.a.addr, + &r->src.addr.v.a.mask, daddr, pd->af); break; } - default: - return (NULL); - } - /* - * Translation was a NOP. - * Pretend there was no match. - */ - if (!bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) { -#ifdef __FreeBSD__ - pool_put(&V_pf_state_key_pl, *nkp); - pool_put(&V_pf_state_key_pl, *skp); -#else - pool_put(&pf_state_key_pl, *nkp); - pool_put(&pf_state_key_pl, *skp); -#endif - *skw = *sks = *nkp = *skp = NULL; - return (NULL); - } + break; + case PF_RDR: { + if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) + goto notrans; + if ((r->rpool.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) + PF_POOLMASK(naddr, naddr, &r->rpool.cur->addr.v.a.mask, + daddr, pd->af); + + if (r->rpool.proxy_port[1]) { + uint32_t tmp_nport; + + tmp_nport = ((ntohs(dport) - ntohs(r->dst.port[0])) % + (r->rpool.proxy_port[1] - r->rpool.proxy_port[0] + + 1)) + r->rpool.proxy_port[0]; + + /* Wrap around if necessary. */ + if (tmp_nport > 65535) + tmp_nport -= 65535; + *nport = htons((uint16_t)tmp_nport); + } else if (r->rpool.proxy_port[0]) + *nport = htons(r->rpool.proxy_port[0]); + break; + } + default: + panic("%s: unknown action %u", __func__, r->action); } - return (r); -} + /* Return success only if translation really happened. */ + if (bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) + return (r); +notrans: + uma_zfree(V_pf_state_key_z, *nkp); + uma_zfree(V_pf_state_key_z, *skp); + *skp = *nkp = NULL; + + return (NULL); +} diff --git a/sys/contrib/pf/net/pf_mtag.h b/sys/contrib/pf/net/pf_mtag.h index 141a867998dca..baff00a699b9b 100644 --- a/sys/contrib/pf/net/pf_mtag.h +++ b/sys/contrib/pf/net/pf_mtag.h @@ -42,17 +42,12 @@ struct pf_mtag { void *hdr; /* saved hdr pos in mbuf, for ECN */ - void *statekey; /* pf stackside statekey */ u_int32_t qid; /* queue id */ - u_int rtableid; /* alternate routing table id */ u_int16_t tag; /* tag id */ u_int8_t flags; u_int8_t routed; }; -static __inline struct pf_mtag *pf_find_mtag(struct mbuf *); -static __inline struct pf_mtag *pf_get_mtag(struct mbuf *); - static __inline struct pf_mtag * pf_find_mtag(struct mbuf *m) { @@ -63,22 +58,5 @@ pf_find_mtag(struct mbuf *m) return ((struct pf_mtag *)(mtag + 1)); } - -static __inline struct pf_mtag * -pf_get_mtag(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) == NULL) { - mtag = m_tag_get(PACKET_TAG_PF, sizeof(struct pf_mtag), - M_NOWAIT); - if (mtag == NULL) - return (NULL); - bzero(mtag + 1, sizeof(struct pf_mtag)); - m_tag_prepend(m, mtag); - } - - return ((struct pf_mtag *)(mtag + 1)); -} #endif /* _KERNEL */ #endif /* _NET_PF_MTAG_H_ */ diff --git a/sys/contrib/pf/net/pf_norm.c b/sys/contrib/pf/net/pf_norm.c index 2b20c85b24642..9063fe8917c03 100644 --- a/sys/contrib/pf/net/pf_norm.c +++ b/sys/contrib/pf/net/pf_norm.c @@ -25,78 +25,56 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef __FreeBSD__ -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_pf.h" - #include __FBSDID("$FreeBSD$"); -#ifdef DEV_PFLOG -#define NPFLOG DEV_PFLOG -#else -#define NPFLOG 0 -#endif -#else -#include "pflog.h" -#endif +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_pf.h" #include -#include +#include #include -#include -#include +#include +#include +#include #include -#include -#include -#ifndef __FreeBSD__ -#include -#include -#endif #include -#include -#include -#include +#include +#include +#include #include #include -#include -#include #include #include #include +#include #include -#include -#include #ifdef INET6 #include #endif /* INET6 */ -#include - -#ifndef __FreeBSD__ struct pf_frent { LIST_ENTRY(pf_frent) fr_next; - struct ip *fr_ip; - struct mbuf *fr_m; -}; - -struct pf_frcache { - LIST_ENTRY(pf_frcache) fr_next; - uint16_t fr_off; - uint16_t fr_end; + union { + struct { + struct ip *_fr_ip; + struct mbuf *_fr_m; + } _frag; + struct { + uint16_t _fr_off; + uint16_t _fr_end; + } _cache; + } _u; }; -#endif - -#define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ -#define PFFRAG_NOBUFFER 0x0002 /* Non-buffering fragment cache */ -#define PFFRAG_DROP 0x0004 /* Drop all fragments */ -#define BUFFER_FRAGMENTS(fr) (!((fr)->fr_flags & PFFRAG_NOBUFFER)) +#define fr_ip _u._frag._fr_ip +#define fr_m _u._frag._fr_m +#define fr_off _u._cache._fr_off +#define fr_end _u._cache._fr_end -#ifndef __FreeBSD__ struct pf_fragment { RB_ENTRY(pf_fragment) fr_entry; TAILQ_ENTRY(pf_fragment) frag_next; @@ -104,145 +82,104 @@ struct pf_fragment { struct in_addr fr_dst; u_int8_t fr_p; /* protocol of this fragment */ u_int8_t fr_flags; /* status flags */ +#define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ +#define PFFRAG_NOBUFFER 0x0002 /* Non-buffering fragment cache */ +#define PFFRAG_DROP 0x0004 /* Drop all fragments */ +#define BUFFER_FRAGMENTS(fr) (!((fr)->fr_flags & PFFRAG_NOBUFFER)) u_int16_t fr_id; /* fragment id for reassemble */ u_int16_t fr_max; /* fragment data max */ u_int32_t fr_timeout; -#define fr_queue fr_u.fru_queue -#define fr_cache fr_u.fru_cache - union { - LIST_HEAD(pf_fragq, pf_frent) fru_queue; /* buffering */ - LIST_HEAD(pf_cacheq, pf_frcache) fru_cache; /* non-buf */ - } fr_u; + LIST_HEAD(, pf_frent) fr_queue; }; -#endif -#ifdef __FreeBSD__ +static struct mtx pf_frag_mtx; +#define PF_FRAG_LOCK() mtx_lock(&pf_frag_mtx) +#define PF_FRAG_UNLOCK() mtx_unlock(&pf_frag_mtx) +#define PF_FRAG_ASSERT() mtx_assert(&pf_frag_mtx, MA_OWNED) + +VNET_DEFINE(uma_zone_t, pf_state_scrub_z); /* XXX: shared with pfsync */ + +static VNET_DEFINE(uma_zone_t, pf_frent_z); +#define V_pf_frent_z VNET(pf_frent_z) +static VNET_DEFINE(uma_zone_t, pf_frag_z); +#define V_pf_frag_z VNET(pf_frag_z) + TAILQ_HEAD(pf_fragqueue, pf_fragment); TAILQ_HEAD(pf_cachequeue, pf_fragment); -VNET_DEFINE(struct pf_fragqueue, pf_fragqueue); +static VNET_DEFINE(struct pf_fragqueue, pf_fragqueue); #define V_pf_fragqueue VNET(pf_fragqueue) -VNET_DEFINE(struct pf_cachequeue, pf_cachequeue); +static VNET_DEFINE(struct pf_cachequeue, pf_cachequeue); #define V_pf_cachequeue VNET(pf_cachequeue) -#else -TAILQ_HEAD(pf_fragqueue, pf_fragment) pf_fragqueue; -TAILQ_HEAD(pf_cachequeue, pf_fragment) pf_cachequeue; -#endif - -#ifndef __FreeBSD__ -static __inline int pf_frag_compare(struct pf_fragment *, - struct pf_fragment *); -#else -static int pf_frag_compare(struct pf_fragment *, - struct pf_fragment *); -#endif - -#ifdef __FreeBSD__ RB_HEAD(pf_frag_tree, pf_fragment); -VNET_DEFINE(struct pf_frag_tree, pf_frag_tree); +static VNET_DEFINE(struct pf_frag_tree, pf_frag_tree); #define V_pf_frag_tree VNET(pf_frag_tree) -VNET_DEFINE(struct pf_frag_tree, pf_cache_tree); +static VNET_DEFINE(struct pf_frag_tree, pf_cache_tree); #define V_pf_cache_tree VNET(pf_cache_tree) -#else -RB_HEAD(pf_frag_tree, pf_fragment) pf_frag_tree, pf_cache_tree; -#endif -RB_PROTOTYPE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); -RB_GENERATE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); +static int pf_frag_compare(struct pf_fragment *, + struct pf_fragment *); +static RB_PROTOTYPE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); +static RB_GENERATE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); /* Private prototypes */ -void pf_ip2key(struct pf_fragment *, struct ip *); -void pf_remove_fragment(struct pf_fragment *); -void pf_flush_fragments(void); -void pf_free_fragment(struct pf_fragment *); -struct pf_fragment *pf_find_fragment(struct ip *, struct pf_frag_tree *); -struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment **, - struct pf_frent *, int); -struct mbuf *pf_fragcache(struct mbuf **, struct ip*, - struct pf_fragment **, int, int, int *); -int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *, +static void pf_free_fragment(struct pf_fragment *); +static void pf_remove_fragment(struct pf_fragment *); +static int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *, struct tcphdr *, int, sa_family_t); -void pf_scrub_ip(struct mbuf **, u_int32_t, u_int8_t, +#ifdef INET +static void pf_ip2key(struct pf_fragment *, struct ip *); +static void pf_scrub_ip(struct mbuf **, u_int32_t, u_int8_t, u_int8_t); +static void pf_flush_fragments(void); +static struct pf_fragment *pf_find_fragment(struct ip *, struct pf_frag_tree *); +static struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment **, + struct pf_frent *, int); +static struct mbuf *pf_fragcache(struct mbuf **, struct ip*, + struct pf_fragment **, int, int, int *); +#endif /* INET */ #ifdef INET6 -void pf_scrub_ip6(struct mbuf **, u_int8_t); +static void pf_scrub_ip6(struct mbuf **, u_int8_t); #endif -#ifdef __FreeBSD__ #define DPFPRINTF(x) do { \ if (V_pf_status.debug >= PF_DEBUG_MISC) { \ printf("%s: ", __func__); \ printf x ; \ } \ } while(0) -#else -#define DPFPRINTF(x) do { \ - if (pf_status.debug >= PF_DEBUG_MISC) { \ - printf("%s: ", __func__); \ - printf x ; \ - } \ -} while(0) -#endif - -/* Globals */ -#ifdef __FreeBSD__ -VNET_DEFINE(uma_zone_t, pf_frent_pl); -VNET_DEFINE(uma_zone_t, pf_frag_pl); -VNET_DEFINE(uma_zone_t, pf_cache_pl); -VNET_DEFINE(uma_zone_t, pf_cent_pl); -VNET_DEFINE(uma_zone_t, pf_state_scrub_pl); - -VNET_DEFINE(int, pf_nfrents); -#define V_pf_nfrents VNET(pf_nfrents) -VNET_DEFINE(int, pf_ncache); -#define V_pf_ncache VNET(pf_ncache) -#else -struct pool pf_frent_pl, pf_frag_pl, pf_cache_pl, pf_cent_pl; -struct pool pf_state_scrub_pl; -int pf_nfrents, pf_ncache; -#endif void pf_normalize_init(void) { -#ifdef __FreeBSD__ - /* - * XXX - * No high water mark support(It's hint not hard limit). - * uma_zone_set_max(pf_frag_pl, PFFRAG_FRAG_HIWAT); - */ - uma_zone_set_max(V_pf_frent_pl, PFFRAG_FRENT_HIWAT); - uma_zone_set_max(V_pf_cache_pl, PFFRAG_FRCACHE_HIWAT); - uma_zone_set_max(V_pf_cent_pl, PFFRAG_FRCENT_HIWAT); -#else - pool_init(&pf_frent_pl, sizeof(struct pf_frent), 0, 0, 0, "pffrent", - NULL); - pool_init(&pf_frag_pl, sizeof(struct pf_fragment), 0, 0, 0, "pffrag", - NULL); - pool_init(&pf_cache_pl, sizeof(struct pf_fragment), 0, 0, 0, - "pffrcache", NULL); - pool_init(&pf_cent_pl, sizeof(struct pf_frcache), 0, 0, 0, "pffrcent", - NULL); - pool_init(&pf_state_scrub_pl, sizeof(struct pf_state_scrub), 0, 0, 0, - "pfstscr", NULL); - - pool_sethiwat(&pf_frag_pl, PFFRAG_FRAG_HIWAT); - pool_sethardlimit(&pf_frent_pl, PFFRAG_FRENT_HIWAT, NULL, 0); - pool_sethardlimit(&pf_cache_pl, PFFRAG_FRCACHE_HIWAT, NULL, 0); - pool_sethardlimit(&pf_cent_pl, PFFRAG_FRCENT_HIWAT, NULL, 0); -#endif -#ifdef __FreeBSD__ + V_pf_frag_z = uma_zcreate("pf frags", sizeof(struct pf_fragment), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + V_pf_frent_z = uma_zcreate("pf frag entries", sizeof(struct pf_frent), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + V_pf_state_scrub_z = uma_zcreate("pf state scrubs", + sizeof(struct pf_state_scrub), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + + V_pf_limits[PF_LIMIT_FRAGS].zone = V_pf_frent_z; + V_pf_limits[PF_LIMIT_FRAGS].limit = PFFRAG_FRENT_HIWAT; + uma_zone_set_max(V_pf_frent_z, PFFRAG_FRENT_HIWAT); + + mtx_init(&pf_frag_mtx, "pf fragments", NULL, MTX_DEF); + TAILQ_INIT(&V_pf_fragqueue); TAILQ_INIT(&V_pf_cachequeue); -#else - TAILQ_INIT(&pf_fragqueue); - TAILQ_INIT(&pf_cachequeue); -#endif } -#ifdef __FreeBSD__ +void +pf_normalize_cleanup(void) +{ + + uma_zdestroy(V_pf_state_scrub_z); + uma_zdestroy(V_pf_frent_z); + uma_zdestroy(V_pf_frag_z); + + mtx_destroy(&pf_frag_mtx); +} + static int -#else -static __inline int -#endif pf_frag_compare(struct pf_fragment *a, struct pf_fragment *b) { int diff; @@ -266,22 +203,13 @@ void pf_purge_expired_fragments(void) { struct pf_fragment *frag; -#ifdef __FreeBSD__ - u_int32_t expire = time_second - + u_int32_t expire = time_uptime - V_pf_default_rule.timeout[PFTM_FRAG]; -#else - u_int32_t expire = time_second - - pf_default_rule.timeout[PFTM_FRAG]; -#endif -#ifdef __FreeBSD__ + PF_FRAG_LOCK(); while ((frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue)) != NULL) { KASSERT((BUFFER_FRAGMENTS(frag)), ("BUFFER_FRAGMENTS(frag) == 0: %s", __FUNCTION__)); -#else - while ((frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue)) != NULL) { - KASSERT(BUFFER_FRAGMENTS(frag)); -#endif if (frag->fr_timeout > expire) break; @@ -289,92 +217,56 @@ pf_purge_expired_fragments(void) pf_free_fragment(frag); } -#ifdef __FreeBSD__ while ((frag = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue)) != NULL) { KASSERT((!BUFFER_FRAGMENTS(frag)), ("BUFFER_FRAGMENTS(frag) != 0: %s", __FUNCTION__)); -#else - while ((frag = TAILQ_LAST(&pf_cachequeue, pf_cachequeue)) != NULL) { - KASSERT(!BUFFER_FRAGMENTS(frag)); -#endif if (frag->fr_timeout > expire) break; DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); pf_free_fragment(frag); -#ifdef __FreeBSD__ KASSERT((TAILQ_EMPTY(&V_pf_cachequeue) || TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue) != frag), ("!(TAILQ_EMPTY() || TAILQ_LAST() == farg): %s", __FUNCTION__)); -#else - KASSERT(TAILQ_EMPTY(&pf_cachequeue) || - TAILQ_LAST(&pf_cachequeue, pf_cachequeue) != frag); -#endif } + PF_FRAG_UNLOCK(); } +#ifdef INET /* * Try to flush old fragments to make space for new ones */ - -void +static void pf_flush_fragments(void) { - struct pf_fragment *frag; + struct pf_fragment *frag, *cache; int goal; -#ifdef __FreeBSD__ - goal = V_pf_nfrents * 9 / 10; - DPFPRINTF(("trying to free > %d frents\n", - V_pf_nfrents - goal)); - while (goal < V_pf_nfrents) { -#else - goal = pf_nfrents * 9 / 10; - DPFPRINTF(("trying to free > %d frents\n", - pf_nfrents - goal)); - while (goal < pf_nfrents) { -#endif -#ifdef __FreeBSD__ - frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue); -#else - frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue); -#endif - if (frag == NULL) - break; - pf_free_fragment(frag); - } + PF_FRAG_ASSERT(); - -#ifdef __FreeBSD__ - goal = V_pf_ncache * 9 / 10; - DPFPRINTF(("trying to free > %d cache entries\n", - V_pf_ncache - goal)); - while (goal < V_pf_ncache) { -#else - goal = pf_ncache * 9 / 10; - DPFPRINTF(("trying to free > %d cache entries\n", - pf_ncache - goal)); - while (goal < pf_ncache) { -#endif -#ifdef __FreeBSD__ - frag = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue); -#else - frag = TAILQ_LAST(&pf_cachequeue, pf_cachequeue); -#endif - if (frag == NULL) + goal = uma_zone_get_cur(V_pf_frent_z) * 9 / 10; + DPFPRINTF(("trying to free %d frag entriess\n", goal)); + while (goal < uma_zone_get_cur(V_pf_frent_z)) { + frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue); + if (frag) + pf_free_fragment(frag); + cache = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue); + if (cache) + pf_free_fragment(cache); + if (frag == NULL && cache == NULL) break; - pf_free_fragment(frag); } } +#endif /* INET */ /* Frees the fragments and all associated entries */ - -void +static void pf_free_fragment(struct pf_fragment *frag) { struct pf_frent *frent; - struct pf_frcache *frcache; + + PF_FRAG_ASSERT(); /* Free all fragments */ if (BUFFER_FRAGMENTS(frag)) { @@ -383,43 +275,28 @@ pf_free_fragment(struct pf_fragment *frag) LIST_REMOVE(frent, fr_next); m_freem(frent->fr_m); -#ifdef __FreeBSD__ - pool_put(&V_pf_frent_pl, frent); - V_pf_nfrents--; -#else - pool_put(&pf_frent_pl, frent); - pf_nfrents--; -#endif + uma_zfree(V_pf_frent_z, frent); } } else { - for (frcache = LIST_FIRST(&frag->fr_cache); frcache; - frcache = LIST_FIRST(&frag->fr_cache)) { - LIST_REMOVE(frcache, fr_next); - -#ifdef __FreeBSD__ - KASSERT((LIST_EMPTY(&frag->fr_cache) || - LIST_FIRST(&frag->fr_cache)->fr_off > - frcache->fr_end), - ("! (LIST_EMPTY() || LIST_FIRST()->fr_off >" - " frcache->fr_end): %s", __FUNCTION__)); + for (frent = LIST_FIRST(&frag->fr_queue); frent; + frent = LIST_FIRST(&frag->fr_queue)) { + LIST_REMOVE(frent, fr_next); - pool_put(&V_pf_cent_pl, frcache); - V_pf_ncache--; -#else - KASSERT(LIST_EMPTY(&frag->fr_cache) || - LIST_FIRST(&frag->fr_cache)->fr_off > - frcache->fr_end); + KASSERT((LIST_EMPTY(&frag->fr_queue) || + LIST_FIRST(&frag->fr_queue)->fr_off > + frent->fr_end), + ("! (LIST_EMPTY() || LIST_FIRST()->fr_off >" + " frent->fr_end): %s", __func__)); - pool_put(&pf_cent_pl, frcache); - pf_ncache--; -#endif + uma_zfree(V_pf_frent_z, frent); } } pf_remove_fragment(frag); } -void +#ifdef INET +static void pf_ip2key(struct pf_fragment *key, struct ip *ip) { key->fr_p = ip->ip_p; @@ -428,70 +305,55 @@ pf_ip2key(struct pf_fragment *key, struct ip *ip) key->fr_dst.s_addr = ip->ip_dst.s_addr; } -struct pf_fragment * +static struct pf_fragment * pf_find_fragment(struct ip *ip, struct pf_frag_tree *tree) { struct pf_fragment key; struct pf_fragment *frag; + PF_FRAG_ASSERT(); + pf_ip2key(&key, ip); frag = RB_FIND(pf_frag_tree, tree, &key); if (frag != NULL) { /* XXX Are we sure we want to update the timeout? */ - frag->fr_timeout = time_second; + frag->fr_timeout = time_uptime; if (BUFFER_FRAGMENTS(frag)) { -#ifdef __FreeBSD__ TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next); TAILQ_INSERT_HEAD(&V_pf_fragqueue, frag, frag_next); -#else - TAILQ_REMOVE(&pf_fragqueue, frag, frag_next); - TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next); -#endif } else { -#ifdef __FreeBSD__ TAILQ_REMOVE(&V_pf_cachequeue, frag, frag_next); TAILQ_INSERT_HEAD(&V_pf_cachequeue, frag, frag_next); -#else - TAILQ_REMOVE(&pf_cachequeue, frag, frag_next); - TAILQ_INSERT_HEAD(&pf_cachequeue, frag, frag_next); -#endif } } return (frag); } +#endif /* INET */ /* Removes a fragment from the fragment queue and frees the fragment */ -void +static void pf_remove_fragment(struct pf_fragment *frag) { + + PF_FRAG_ASSERT(); + if (BUFFER_FRAGMENTS(frag)) { -#ifdef __FreeBSD__ RB_REMOVE(pf_frag_tree, &V_pf_frag_tree, frag); TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next); - pool_put(&V_pf_frag_pl, frag); -#else - RB_REMOVE(pf_frag_tree, &pf_frag_tree, frag); - TAILQ_REMOVE(&pf_fragqueue, frag, frag_next); - pool_put(&pf_frag_pl, frag); -#endif + uma_zfree(V_pf_frag_z, frag); } else { -#ifdef __FreeBSD__ RB_REMOVE(pf_frag_tree, &V_pf_cache_tree, frag); TAILQ_REMOVE(&V_pf_cachequeue, frag, frag_next); - pool_put(&V_pf_cache_pl, frag); -#else - RB_REMOVE(pf_frag_tree, &pf_cache_tree, frag); - TAILQ_REMOVE(&pf_cachequeue, frag, frag_next); - pool_put(&pf_cache_pl, frag); -#endif + uma_zfree(V_pf_frag_z, frag); } } +#ifdef INET #define FR_IP_OFF(fr) ((ntohs((fr)->fr_ip->ip_off) & IP_OFFMASK) << 3) -struct mbuf * +static struct mbuf * pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, struct pf_frent *frent, int mff) { @@ -504,12 +366,9 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, u_int16_t ip_len = ntohs(ip->ip_len) - ip->ip_hl * 4; u_int16_t max = ip_len + off; -#ifdef __FreeBSD__ + PF_FRAG_ASSERT(); KASSERT((*frag == NULL || BUFFER_FRAGMENTS(*frag)), ("! (*frag == NULL || BUFFER_FRAGMENTS(*frag)): %s", __FUNCTION__)); -#else - KASSERT(*frag == NULL || BUFFER_FRAGMENTS(*frag)); -#endif /* Strip off ip header */ m->m_data += hlen; @@ -517,18 +376,10 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, /* Create a new reassembly queue for this packet */ if (*frag == NULL) { -#ifdef __FreeBSD__ - *frag = pool_get(&V_pf_frag_pl, PR_NOWAIT); -#else - *frag = pool_get(&pf_frag_pl, PR_NOWAIT); -#endif + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); if (*frag == NULL) { pf_flush_fragments(); -#ifdef __FreeBSD__ - *frag = pool_get(&V_pf_frag_pl, PR_NOWAIT); -#else - *frag = pool_get(&pf_frag_pl, PR_NOWAIT); -#endif + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); if (*frag == NULL) goto drop_fragment; } @@ -539,16 +390,11 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, (*frag)->fr_dst = frent->fr_ip->ip_dst; (*frag)->fr_p = frent->fr_ip->ip_p; (*frag)->fr_id = frent->fr_ip->ip_id; - (*frag)->fr_timeout = time_second; + (*frag)->fr_timeout = time_uptime; LIST_INIT(&(*frag)->fr_queue); -#ifdef __FreeBSD__ RB_INSERT(pf_frag_tree, &V_pf_frag_tree, *frag); TAILQ_INSERT_HEAD(&V_pf_fragqueue, *frag, frag_next); -#else - RB_INSERT(pf_frag_tree, &pf_frag_tree, *frag); - TAILQ_INSERT_HEAD(&pf_fragqueue, *frag, frag_next); -#endif /* We do not have a previous fragment */ frep = NULL; @@ -565,12 +411,8 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, frep = frea; } -#ifdef __FreeBSD__ KASSERT((frep != NULL || frea != NULL), ("!(frep != NULL || frea != NULL): %s", __FUNCTION__));; -#else - KASSERT(frep != NULL || frea != NULL); -#endif if (frep != NULL && FR_IP_OFF(frep) + ntohs(frep->fr_ip->ip_len) - frep->fr_ip->ip_hl * @@ -613,13 +455,7 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, next = LIST_NEXT(frea, fr_next); m_freem(frea->fr_m); LIST_REMOVE(frea, fr_next); -#ifdef __FreeBSD__ - pool_put(&V_pf_frent_pl, frea); - V_pf_nfrents--; -#else - pool_put(&pf_frent_pl, frea); - pf_nfrents--; -#endif + uma_zfree(V_pf_frent_z, frea); } insert: @@ -660,11 +496,7 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, /* We have all the data */ frent = LIST_FIRST(&(*frag)->fr_queue); -#ifdef __FreeBSD__ KASSERT((frent != NULL), ("frent == NULL: %s", __FUNCTION__)); -#else - KASSERT(frent != NULL); -#endif if ((frent->fr_ip->ip_hl << 2) + off > IP_MAXPACKET) { DPFPRINTF(("drop: too big: %d\n", off)); pf_free_fragment(*frag); @@ -679,36 +511,20 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, m2 = m->m_next; m->m_next = NULL; m_cat(m, m2); -#ifdef __FreeBSD__ - pool_put(&V_pf_frent_pl, frent); - V_pf_nfrents--; -#else - pool_put(&pf_frent_pl, frent); - pf_nfrents--; -#endif + uma_zfree(V_pf_frent_z, frent); for (frent = next; frent != NULL; frent = next) { next = LIST_NEXT(frent, fr_next); m2 = frent->fr_m; -#ifdef __FreeBSD__ - pool_put(&V_pf_frent_pl, frent); - V_pf_nfrents--; -#else - pool_put(&pf_frent_pl, frent); - pf_nfrents--; -#endif -#ifdef __FreeBSD__ + uma_zfree(V_pf_frent_z, frent); m->m_pkthdr.csum_flags &= m2->m_pkthdr.csum_flags; m->m_pkthdr.csum_data += m2->m_pkthdr.csum_data; -#endif m_cat(m, m2); } -#ifdef __FreeBSD__ while (m->m_pkthdr.csum_data & 0xffff0000) m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data & 0xffff) + (m->m_pkthdr.csum_data >> 16); -#endif ip->ip_src = (*frag)->fr_src; ip->ip_dst = (*frag)->fr_dst; @@ -735,71 +551,43 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, drop_fragment: /* Oops - fail safe - drop packet */ -#ifdef __FreeBSD__ - pool_put(&V_pf_frent_pl, frent); - V_pf_nfrents--; -#else - pool_put(&pf_frent_pl, frent); - pf_nfrents--; -#endif + uma_zfree(V_pf_frent_z, frent); m_freem(m); return (NULL); } -struct mbuf * +static struct mbuf * pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, int drop, int *nomem) { struct mbuf *m = *m0; - struct pf_frcache *frp, *fra, *cur = NULL; + struct pf_frent *frp, *fra, *cur = NULL; int ip_len = ntohs(h->ip_len) - (h->ip_hl << 2); u_int16_t off = ntohs(h->ip_off) << 3; u_int16_t max = ip_len + off; int hosed = 0; -#ifdef __FreeBSD__ + PF_FRAG_ASSERT(); KASSERT((*frag == NULL || !BUFFER_FRAGMENTS(*frag)), ("!(*frag == NULL || !BUFFER_FRAGMENTS(*frag)): %s", __FUNCTION__)); -#else - KASSERT(*frag == NULL || !BUFFER_FRAGMENTS(*frag)); -#endif /* Create a new range queue for this packet */ if (*frag == NULL) { -#ifdef __FreeBSD__ - *frag = pool_get(&V_pf_cache_pl, PR_NOWAIT); -#else - *frag = pool_get(&pf_cache_pl, PR_NOWAIT); -#endif + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); if (*frag == NULL) { pf_flush_fragments(); -#ifdef __FreeBSD__ - *frag = pool_get(&V_pf_cache_pl, PR_NOWAIT); -#else - *frag = pool_get(&pf_cache_pl, PR_NOWAIT); -#endif + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); if (*frag == NULL) goto no_mem; } /* Get an entry for the queue */ -#ifdef __FreeBSD__ - cur = pool_get(&V_pf_cent_pl, PR_NOWAIT); - if (cur == NULL) { - pool_put(&V_pf_cache_pl, *frag); -#else - cur = pool_get(&pf_cent_pl, PR_NOWAIT); + cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); if (cur == NULL) { - pool_put(&pf_cache_pl, *frag); -#endif + uma_zfree(V_pf_frag_z, *frag); *frag = NULL; goto no_mem; } -#ifdef __FreeBSD__ - V_pf_ncache++; -#else - pf_ncache++; -#endif (*frag)->fr_flags = PFFRAG_NOBUFFER; (*frag)->fr_max = 0; @@ -807,20 +595,15 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, (*frag)->fr_dst = h->ip_dst; (*frag)->fr_p = h->ip_p; (*frag)->fr_id = h->ip_id; - (*frag)->fr_timeout = time_second; + (*frag)->fr_timeout = time_uptime; cur->fr_off = off; cur->fr_end = max; - LIST_INIT(&(*frag)->fr_cache); - LIST_INSERT_HEAD(&(*frag)->fr_cache, cur, fr_next); + LIST_INIT(&(*frag)->fr_queue); + LIST_INSERT_HEAD(&(*frag)->fr_queue, cur, fr_next); -#ifdef __FreeBSD__ RB_INSERT(pf_frag_tree, &V_pf_cache_tree, *frag); TAILQ_INSERT_HEAD(&V_pf_cachequeue, *frag, frag_next); -#else - RB_INSERT(pf_frag_tree, &pf_cache_tree, *frag); - TAILQ_INSERT_HEAD(&pf_cachequeue, *frag, frag_next); -#endif DPFPRINTF(("fragcache[%d]: new %d-%d\n", h->ip_id, off, max)); @@ -832,18 +615,14 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, * - off contains the real shifted offset. */ frp = NULL; - LIST_FOREACH(fra, &(*frag)->fr_cache, fr_next) { + LIST_FOREACH(fra, &(*frag)->fr_queue, fr_next) { if (fra->fr_off > off) break; frp = fra; } -#ifdef __FreeBSD__ KASSERT((frp != NULL || fra != NULL), ("!(frp != NULL || fra != NULL): %s", __FUNCTION__)); -#else - KASSERT(frp != NULL || fra != NULL); -#endif if (frp != NULL) { int precut; @@ -885,24 +664,16 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, * than this mbuf magic. For my next trick, * I'll pull a rabbit out of my laptop. */ -#ifdef __FreeBSD__ - *m0 = m_dup(m, M_DONTWAIT); -#else - *m0 = m_copym2(m, 0, h->ip_hl << 2, M_NOWAIT); -#endif + *m0 = m_dup(m, M_NOWAIT); if (*m0 == NULL) goto no_mem; -#ifdef __FreeBSD__ /* From KAME Project : We have missed this! */ m_adj(*m0, (h->ip_hl << 2) - (*m0)->m_pkthdr.len); - KASSERT(((*m0)->m_next == NULL), - ("(*m0)->m_next != NULL: %s", + KASSERT(((*m0)->m_next == NULL), + ("(*m0)->m_next != NULL: %s", __FUNCTION__)); -#else - KASSERT((*m0)->m_next == NULL); -#endif m_adj(m, precut + (h->ip_hl << 2)); m_cat(*m0, m); m = *m0; @@ -917,15 +688,10 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, h = mtod(m, struct ip *); -#ifdef __FreeBSD__ KASSERT(((int)m->m_len == ntohs(h->ip_len) - precut), ("m->m_len != ntohs(h->ip_len) - precut: %s", __FUNCTION__)); -#else - KASSERT((int)m->m_len == - ntohs(h->ip_len) - precut); -#endif h->ip_off = htons(ntohs(h->ip_off) + (precut >> 3)); h->ip_len = htons(ntohs(h->ip_len) - precut); @@ -939,18 +705,9 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, h->ip_id, -precut, frp->fr_off, frp->fr_end, off, max)); -#ifdef __FreeBSD__ - cur = pool_get(&V_pf_cent_pl, PR_NOWAIT); -#else - cur = pool_get(&pf_cent_pl, PR_NOWAIT); -#endif + cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); if (cur == NULL) goto no_mem; -#ifdef __FreeBSD__ - V_pf_ncache++; -#else - pf_ncache++; -#endif cur->fr_off = off; cur->fr_end = max; @@ -989,14 +746,9 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, m->m_pkthdr.len = plen; } h = mtod(m, struct ip *); -#ifdef __FreeBSD__ KASSERT(((int)m->m_len == ntohs(h->ip_len) - aftercut), ("m->m_len != ntohs(h->ip_len) - aftercut: %s", __FUNCTION__)); -#else - KASSERT((int)m->m_len == - ntohs(h->ip_len) - aftercut); -#endif h->ip_len = htons(ntohs(h->ip_len) - aftercut); } else { hosed++; @@ -1007,18 +759,9 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, h->ip_id, -aftercut, off, max, fra->fr_off, fra->fr_end)); -#ifdef __FreeBSD__ - cur = pool_get(&V_pf_cent_pl, PR_NOWAIT); -#else - cur = pool_get(&pf_cent_pl, PR_NOWAIT); -#endif + cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); if (cur == NULL) goto no_mem; -#ifdef __FreeBSD__ - V_pf_ncache++; -#else - pf_ncache++; -#endif cur->fr_off = off; cur->fr_end = max; @@ -1036,36 +779,20 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, max, fra->fr_off, fra->fr_end)); fra->fr_off = cur->fr_off; LIST_REMOVE(cur, fr_next); -#ifdef __FreeBSD__ - pool_put(&V_pf_cent_pl, cur); - V_pf_ncache--; -#else - pool_put(&pf_cent_pl, cur); - pf_ncache--; -#endif + uma_zfree(V_pf_frent_z, cur); cur = NULL; } else if (frp && fra->fr_off <= frp->fr_end) { /* Need to merge in a modified 'frp' */ -#ifdef __FreeBSD__ KASSERT((cur == NULL), ("cur != NULL: %s", __FUNCTION__)); -#else - KASSERT(cur == NULL); -#endif DPFPRINTF(("fragcache[%d]: adjacent(merge " "%d-%d) %d-%d (%d-%d)\n", h->ip_id, frp->fr_off, frp->fr_end, off, max, fra->fr_off, fra->fr_end)); fra->fr_off = frp->fr_off; LIST_REMOVE(frp, fr_next); -#ifdef __FreeBSD__ - pool_put(&V_pf_cent_pl, frp); - V_pf_ncache--; -#else - pool_put(&pf_cent_pl, frp); - pf_ncache--; -#endif + uma_zfree(V_pf_frent_z, frp); frp = NULL; } @@ -1093,8 +820,8 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, /* Check if we are completely reassembled */ if (((*frag)->fr_flags & PFFRAG_SEENLAST) && - LIST_FIRST(&(*frag)->fr_cache)->fr_off == 0 && - LIST_FIRST(&(*frag)->fr_cache)->fr_end == (*frag)->fr_max) { + LIST_FIRST(&(*frag)->fr_queue)->fr_off == 0 && + LIST_FIRST(&(*frag)->fr_queue)->fr_end == (*frag)->fr_max) { /* Remove from fragment queue */ DPFPRINTF(("fragcache[%d]: done 0-%d\n", h->ip_id, (*frag)->fr_max)); @@ -1132,7 +859,6 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, return (NULL); } -#ifdef INET int pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, struct pf_pdesc *pd) @@ -1150,6 +876,8 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, int ip_off; int tag = -1; + PF_RULES_RASSERT(); + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); while (r != NULL) { r->evaluations++; @@ -1169,11 +897,8 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, (struct pf_addr *)&h->ip_dst.s_addr, AF_INET, r->dst.neg, NULL, M_GETFIB(m))) r = r->skip[PF_SKIP_DST_ADDR].ptr; -#ifdef __FreeBSD__ - else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag)) -#else - else if (r->match_tag && !pf_match_tag(m, r, &tag)) -#endif + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) r = TAILQ_NEXT(r, entries); else break; @@ -1231,13 +956,10 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, max = fragoff + ip_len; if ((r->rule_flag & (PFRULE_FRAGCROP|PFRULE_FRAGDROP)) == 0) { - /* Fully buffer all of the fragments */ -#ifdef __FreeBSD__ + /* Fully buffer all of the fragments */ + PF_FRAG_LOCK(); frag = pf_find_fragment(h, &V_pf_frag_tree); -#else - frag = pf_find_fragment(h, &pf_frag_tree); -#endif /* Check if we saw the last fragment already */ if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && @@ -1245,26 +967,19 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, goto bad; /* Get an entry for the fragment queue */ -#ifdef __FreeBSD__ - frent = pool_get(&V_pf_frent_pl, PR_NOWAIT); -#else - frent = pool_get(&pf_frent_pl, PR_NOWAIT); -#endif + frent = uma_zalloc(V_pf_frent_z, M_NOWAIT); if (frent == NULL) { + PF_FRAG_UNLOCK(); REASON_SET(reason, PFRES_MEMORY); return (PF_DROP); } -#ifdef __FreeBSD__ - V_pf_nfrents++; -#else - pf_nfrents++; -#endif frent->fr_ip = h; frent->fr_m = m; /* Might return a completely reassembled mbuf, or NULL */ DPFPRINTF(("reass frag %d @ %d-%d\n", h->ip_id, fragoff, max)); *m0 = m = pf_reassemble(m0, &frag, frent, mff); + PF_FRAG_UNLOCK(); if (m == NULL) return (PF_DROP); @@ -1289,11 +1004,7 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, /* non-buffering fragment cache (drops or masks overlaps) */ int nomem = 0; -#ifdef __FreeBSD__ if (dir == PF_OUT && pd->pf_mtag->flags & PF_TAG_FRAGCACHE) { -#else - if (dir == PF_OUT && m->m_pkthdr.pf.flags & PF_TAG_FRAGCACHE) { -#endif /* * Already passed the fragment cache in the * input direction. If we continued, it would @@ -1302,11 +1013,8 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, goto fragment_pass; } -#ifdef __FreeBSD__ + PF_FRAG_LOCK(); frag = pf_find_fragment(h, &V_pf_cache_tree); -#else - frag = pf_find_fragment(h, &pf_cache_tree); -#endif /* Check if we saw the last fragment already */ if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && @@ -1318,6 +1026,7 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, *m0 = m = pf_fragcache(m0, h, &frag, mff, (r->rule_flag & PFRULE_FRAGDROP) ? 1 : 0, &nomem); + PF_FRAG_UNLOCK(); if (m == NULL) { if (nomem) goto no_mem; @@ -1337,11 +1046,7 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, } #endif if (dir == PF_IN) -#ifdef __FreeBSD__ pd->pf_mtag->flags |= PF_TAG_FRAGCACHE; -#else - m->m_pkthdr.pf.flags |= PF_TAG_FRAGCACHE; -#endif if (frag != NULL && (frag->fr_flags & PFFRAG_DROP)) goto drop; @@ -1369,25 +1074,30 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, no_mem: REASON_SET(reason, PFRES_MEMORY); if (r != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET, dir, *reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, + 1); return (PF_DROP); drop: REASON_SET(reason, PFRES_NORM); if (r != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET, dir, *reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, + 1); return (PF_DROP); bad: DPFPRINTF(("dropping bad fragment\n")); /* Free associated fragments */ - if (frag != NULL) + if (frag != NULL) { pf_free_fragment(frag); + PF_FRAG_UNLOCK(); + } REASON_SET(reason, PFRES_FRAG); if (r != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET, dir, *reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, + 1); return (PF_DROP); } @@ -1413,6 +1123,8 @@ pf_normalize_ip6(struct mbuf **m0, int dir, struct pfi_kif *kif, u_int8_t proto; int terminal; + PF_RULES_RASSERT(); + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); while (r != NULL) { r->evaluations++; @@ -1553,19 +1265,22 @@ pf_normalize_ip6(struct mbuf **m0, int dir, struct pfi_kif *kif, shortpkt: REASON_SET(reason, PFRES_SHORT); if (r != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET6, dir, *reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, + 1); return (PF_DROP); drop: REASON_SET(reason, PFRES_NORM); if (r != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET6, dir, *reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, + 1); return (PF_DROP); badfrag: REASON_SET(reason, PFRES_FRAG); if (r != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET6, dir, *reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, + 1); return (PF_DROP); } #endif /* INET6 */ @@ -1581,6 +1296,8 @@ pf_normalize_tcp(int dir, struct pfi_kif *kif, struct mbuf *m, int ipoff, u_int8_t flags; sa_family_t af = pd->af; + PF_RULES_RASSERT(); + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); while (r != NULL) { r->evaluations++; @@ -1674,18 +1391,15 @@ pf_normalize_tcp(int dir, struct pfi_kif *kif, struct mbuf *m, int ipoff, /* copy back packet headers if we sanitized */ if (rewrite) -#ifdef __FreeBSD__ m_copyback(m, off, sizeof(*th), (caddr_t)th); -#else - m_copyback(m, off, sizeof(*th), th); -#endif return (PF_PASS); tcp_drop: REASON_SET(&reason, PFRES_NORM); if (rm != NULL && r->log) - PFLOG_PACKET(kif, h, m, AF_INET, dir, reason, r, NULL, NULL, pd); + PFLOG_PACKET(kif, m, AF_INET, dir, reason, r, NULL, NULL, pd, + 1); return (PF_DROP); } @@ -1697,19 +1411,12 @@ pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, u_int8_t hdr[60]; u_int8_t *opt; -#ifdef __FreeBSD__ - KASSERT((src->scrub == NULL), + KASSERT((src->scrub == NULL), ("pf_normalize_tcp_init: src->scrub != NULL")); - src->scrub = pool_get(&V_pf_state_scrub_pl, PR_NOWAIT); -#else - KASSERT(src->scrub == NULL); - - src->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT); -#endif + src->scrub = uma_zalloc(V_pf_state_scrub_z, M_ZERO | M_NOWAIT); if (src->scrub == NULL) return (1); - bzero(src->scrub, sizeof(*src->scrub)); switch (pd->af) { #ifdef INET @@ -1782,17 +1489,10 @@ pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, void pf_normalize_tcp_cleanup(struct pf_state *state) { -#ifdef __FreeBSD__ - if (state->src.scrub) - pool_put(&V_pf_state_scrub_pl, state->src.scrub); - if (state->dst.scrub) - pool_put(&V_pf_state_scrub_pl, state->dst.scrub); -#else if (state->src.scrub) - pool_put(&pf_state_scrub_pl, state->src.scrub); + uma_zfree(V_pf_state_scrub_z, state->src.scrub); if (state->dst.scrub) - pool_put(&pf_state_scrub_pl, state->dst.scrub); -#endif + uma_zfree(V_pf_state_scrub_z, state->dst.scrub); /* Someday... flush the TCP segment reassembly descriptors. */ } @@ -1810,12 +1510,8 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, int copyback = 0; int got_ts = 0; -#ifdef __FreeBSD__ - KASSERT((src->scrub || dst->scrub), - ("pf_normalize_tcp_statefull: src->scrub && dst->scrub!")); -#else - KASSERT(src->scrub || dst->scrub); -#endif + KASSERT((src->scrub || dst->scrub), + ("%s: src->scrub && dst->scrub!", __func__)); /* * Enforce the minimum TTL seen for this connection. Negate a common @@ -1870,11 +1566,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, if (got_ts) { /* Huh? Multiple timestamps!? */ -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif DPFPRINTF(("multiple TS??")); pf_print_state(state); printf("\n"); @@ -1942,12 +1634,8 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, getmicrouptime(&uptime); if (src->scrub && (src->scrub->pfss_flags & PFSS_PAWS) && (uptime.tv_sec - src->scrub->pfss_last.tv_sec > TS_MAX_IDLE || - time_second - state->creation > TS_MAX_CONN)) { -#ifdef __FreeBSD__ + time_uptime - state->creation > TS_MAX_CONN)) { if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif DPFPRINTF(("src idled out of PAWS\n")); pf_print_state(state); printf("\n"); @@ -1957,11 +1645,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, } if (dst->scrub && (dst->scrub->pfss_flags & PFSS_PAWS) && uptime.tv_sec - dst->scrub->pfss_last.tv_sec > TS_MAX_IDLE) { -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif DPFPRINTF(("dst idled out of PAWS\n")); pf_print_state(state); printf("\n"); @@ -1978,7 +1662,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, * measurement of RTT (round trip time) and PAWS * (protection against wrapped sequence numbers). PAWS * gives us a set of rules for rejecting packets on - * long fat pipes (packets that were somehow delayed + * long fat pipes (packets that were somehow delayed * in transit longer than the time it took to send the * full TCP sequence space of 4Gb). We can use these * rules and infer a few others that will let us treat @@ -2045,34 +1729,16 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, * this packet. */ if ((ts_fudge = state->rule.ptr->timeout[PFTM_TS_DIFF]) == 0) -#ifdef __FreeBSD__ ts_fudge = V_pf_default_rule.timeout[PFTM_TS_DIFF]; -#else - ts_fudge = pf_default_rule.timeout[PFTM_TS_DIFF]; -#endif - /* Calculate max ticks since the last timestamp */ #define TS_MAXFREQ 1100 /* RFC max TS freq of 1Khz + 10% skew */ #define TS_MICROSECS 1000000 /* microseconds per second */ -#ifdef __FreeBSD__ -#ifndef timersub -#define timersub(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ - (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ - if ((vvp)->tv_usec < 0) { \ - (vvp)->tv_sec--; \ - (vvp)->tv_usec += 1000000; \ - } \ - } while (0) -#endif -#endif - timersub(&uptime, &src->scrub->pfss_last, &delta_ts); + delta_ts = uptime; + timevalsub(&delta_ts, &src->scrub->pfss_last); tsval_from_last = (delta_ts.tv_sec + ts_fudge) * TS_MAXFREQ; tsval_from_last += delta_ts.tv_usec / (TS_MICROSECS/TS_MAXFREQ); - if ((src->state >= TCPS_ESTABLISHED && dst->state >= TCPS_ESTABLISHED) && (SEQ_LT(tsval, dst->scrub->pfss_tsecr) || @@ -2092,7 +1758,6 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, tsval_from_last) ? '1' : ' ', SEQ_GT(tsecr, dst->scrub->pfss_tsval) ? '2' : ' ', SEQ_LT(tsecr, dst->scrub->pfss_tsval0)? '3' : ' ')); -#ifdef __FreeBSD__ DPFPRINTF((" tsval: %u tsecr: %u +ticks: %u " "idle: %jus %lums\n", tsval, tsecr, tsval_from_last, @@ -2103,22 +1768,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, DPFPRINTF((" dst->tsval: %u tsecr: %u tsval0: %u" "\n", dst->scrub->pfss_tsval, dst->scrub->pfss_tsecr, dst->scrub->pfss_tsval0)); -#else - DPFPRINTF((" tsval: %lu tsecr: %lu +ticks: %lu " - "idle: %lus %lums\n", - tsval, tsecr, tsval_from_last, delta_ts.tv_sec, - delta_ts.tv_usec / 1000)); - DPFPRINTF((" src->tsval: %lu tsecr: %lu\n", - src->scrub->pfss_tsval, src->scrub->pfss_tsecr)); - DPFPRINTF((" dst->tsval: %lu tsecr: %lu tsval0: %lu" - "\n", dst->scrub->pfss_tsval, - dst->scrub->pfss_tsecr, dst->scrub->pfss_tsval0)); -#endif -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif pf_print_state(state); pf_print_flags(th->th_flags); printf("\n"); @@ -2166,11 +1816,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, * Hey! Someone tried to sneak a packet in. Or the * stack changed its RFC1323 behavior?!?! */ -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC) { -#else - if (pf_status.debug >= PF_DEBUG_MISC) { -#endif DPFPRINTF(("Did not receive expected RFC1323 " "timestamp\n")); pf_print_state(state); @@ -2197,11 +1843,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, src->scrub->pfss_flags |= PFSS_DATA_TS; else { src->scrub->pfss_flags |= PFSS_DATA_NOTS; -#ifdef __FreeBSD__ if (V_pf_status.debug >= PF_DEBUG_MISC && dst->scrub && -#else - if (pf_status.debug >= PF_DEBUG_MISC && dst->scrub && -#endif (dst->scrub->pfss_flags & PFSS_TIMESTAMP)) { /* Don't warn if other host rejected RFC1323 */ DPFPRINTF(("Broken RFC1323 stack did not " @@ -2247,7 +1889,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, return (0); } -int +static int pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, int off, sa_family_t af) { @@ -2255,11 +1897,7 @@ pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, int thoff; int opt, cnt, optlen = 0; int rewrite = 0; -#ifdef __FreeBSD__ u_char opts[TCP_MAXOLEN]; -#else - u_char opts[MAX_TCPOPTLEN]; -#endif u_char *optp = opts; thoff = th->th_off << 2; @@ -2303,7 +1941,8 @@ pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, return (rewrite); } -void +#ifdef INET +static void pf_scrub_ip(struct mbuf **m0, u_int32_t flags, u_int8_t min_ttl, u_int8_t tos) { struct mbuf *m = *m0; @@ -2344,9 +1983,10 @@ pf_scrub_ip(struct mbuf **m0, u_int32_t flags, u_int8_t min_ttl, u_int8_t tos) h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_id, h->ip_id, 0); } } +#endif /* INET */ #ifdef INET6 -void +static void pf_scrub_ip6(struct mbuf **m0, u_int8_t min_ttl) { struct mbuf *m = *m0; diff --git a/sys/contrib/pf/net/pf_osfp.c b/sys/contrib/pf/net/pf_osfp.c index dcd8af7fb54d8..29d4a4030b51b 100644 --- a/sys/contrib/pf/net/pf_osfp.c +++ b/sys/contrib/pf/net/pf_osfp.c @@ -17,23 +17,14 @@ * */ -#ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); -#endif #include +#include #include -#ifdef _KERNEL -#include -#ifndef __FreeBSD__ -#include -#endif -#endif /* _KERNEL */ -#include #include -#include #include #include @@ -41,77 +32,31 @@ __FBSDID("$FreeBSD$"); #include #include -#ifdef _KERNEL #include -#endif - -#ifdef _KERNEL -#ifdef __FreeBSD__ +static MALLOC_DEFINE(M_PFOSFP, "pf_osfp", "pf(4) operating system fingerprints"); #define DPFPRINTF(format, x...) \ if (V_pf_status.debug >= PF_DEBUG_NOISY) \ printf(format , ##x) -#else -#define DPFPRINTF(format, x...) \ - if (pf_status.debug >= PF_DEBUG_NOISY) \ - printf(format , ##x) -#endif -#ifdef __FreeBSD__ -typedef uma_zone_t pool_t; -#else -typedef struct pool pool_t; -#endif -#else -/* Userland equivalents so we can lend code to tcpdump et al. */ - -#include -#include -#include -#include -#include -#include -#define pool_t int -#define pool_get(pool, flags) malloc(*(pool)) -#define pool_put(pool, item) free(item) -#define pool_init(pool, size, a, ao, f, m, p) (*(pool)) = (size) - -#ifdef __FreeBSD__ -#define NTOHS(x) (x) = ntohs((u_int16_t)(x)) -#endif - -#ifdef PFDEBUG -#include -#define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) -#else -#define DPFPRINTF(format, x...) ((void)0) -#endif /* PFDEBUG */ -#endif /* _KERNEL */ - - -#ifdef __FreeBSD__ SLIST_HEAD(pf_osfp_list, pf_os_fingerprint); -VNET_DEFINE(struct pf_osfp_list, pf_osfp_list); +static VNET_DEFINE(struct pf_osfp_list, pf_osfp_list) = + SLIST_HEAD_INITIALIZER(); #define V_pf_osfp_list VNET(pf_osfp_list) -VNET_DEFINE(pool_t, pf_osfp_entry_pl); -#define pf_osfp_entry_pl VNET(pf_osfp_entry_pl) -VNET_DEFINE(pool_t, pf_osfp_pl); -#define pf_osfp_pl VNET(pf_osfp_pl) -#else -SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list; -pool_t pf_osfp_entry_pl; -pool_t pf_osfp_pl; -#endif -struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *, +static struct pf_osfp_enlist *pf_osfp_fingerprint_hdr(const struct ip *, + const struct ip6_hdr *, + const struct tcphdr *); +static struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *, struct pf_os_fingerprint *, u_int8_t); -struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *, +static struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *, struct pf_os_fingerprint *); -void pf_osfp_insert(struct pf_osfp_list *, +static void pf_osfp_insert(struct pf_osfp_list *, struct pf_os_fingerprint *); +#ifdef PFDEBUG +static struct pf_os_fingerprint *pf_osfp_validate(void); +#endif - -#ifdef _KERNEL /* * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only) * Returns the list of possible OSes. @@ -140,19 +85,14 @@ pf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off, return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr)); } -#endif /* _KERNEL */ -struct pf_osfp_enlist * +static struct pf_osfp_enlist * pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcp) { struct pf_os_fingerprint fp, *fpresult; int cnt, optlen = 0; const u_int8_t *optp; -#ifdef _KERNEL char srcname[128]; -#else - char srcname[NI_MAXHOST]; -#endif if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN) return (NULL); @@ -164,49 +104,21 @@ pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const st memset(&fp, 0, sizeof(fp)); if (ip) { -#ifndef _KERNEL - struct sockaddr_in sin; -#endif - fp.fp_psize = ntohs(ip->ip_len); fp.fp_ttl = ip->ip_ttl; if (ip->ip_off & htons(IP_DF)) fp.fp_flags |= PF_OSFP_DF; -#ifdef _KERNEL strlcpy(srcname, inet_ntoa(ip->ip_src), sizeof(srcname)); -#else - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_len = sizeof(struct sockaddr_in); - sin.sin_addr = ip->ip_src; - (void)getnameinfo((struct sockaddr *)&sin, - sizeof(struct sockaddr_in), srcname, sizeof(srcname), - NULL, 0, NI_NUMERICHOST); -#endif } #ifdef INET6 else if (ip6) { -#ifndef _KERNEL - struct sockaddr_in6 sin6; -#endif - /* jumbo payload? */ fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); fp.fp_ttl = ip6->ip6_hlim; fp.fp_flags |= PF_OSFP_DF; fp.fp_flags |= PF_OSFP_INET6; -#ifdef _KERNEL strlcpy(srcname, ip6_sprintf((struct in6_addr *)&ip6->ip6_src), sizeof(srcname)); -#else - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_addr = ip6->ip6_src; - (void)getnameinfo((struct sockaddr *)&sin6, - sizeof(struct sockaddr_in6), srcname, sizeof(srcname), - NULL, 0, NI_NUMERICHOST); -#endif } #endif else @@ -284,11 +196,7 @@ pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const st (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", fp.fp_wscale); -#ifdef __FreeBSD__ if ((fpresult = pf_osfp_find(&V_pf_osfp_list, &fp, -#else - if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp, -#endif PF_OSFP_MAXTTL_OFFSET))) return (&fpresult->fp_oses); return (NULL); @@ -324,52 +232,6 @@ pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os) return (0); } -/* Initialize the OS fingerprint system */ -#ifdef __FreeBSD__ -int -#else -void -#endif -pf_osfp_initialize(void) -{ -#if defined(__FreeBSD__) && defined(_KERNEL) - int error = ENOMEM; - - do { - pf_osfp_entry_pl = pf_osfp_pl = NULL; - UMA_CREATE(pf_osfp_entry_pl, struct pf_osfp_entry, "pfospfen"); - UMA_CREATE(pf_osfp_pl, struct pf_os_fingerprint, "pfosfp"); - error = 0; - } while(0); - - SLIST_INIT(&V_pf_osfp_list); -#else - pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0, - "pfosfpen", &pool_allocator_nointr); - pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0, - "pfosfp", &pool_allocator_nointr); - SLIST_INIT(&pf_osfp_list); -#endif - -#ifdef __FreeBSD__ -#ifdef _KERNEL - return (error); -#else - return (0); -#endif -#endif -} - -#if defined(__FreeBSD__) && (_KERNEL) -void -pf_osfp_cleanup(void) -{ - - UMA_DESTROY(pf_osfp_entry_pl); - UMA_DESTROY(pf_osfp_pl); -} -#endif - /* Flush the fingerprint list */ void pf_osfp_flush(void) @@ -377,18 +239,13 @@ pf_osfp_flush(void) struct pf_os_fingerprint *fp; struct pf_osfp_entry *entry; -#ifdef __FreeBSD__ while ((fp = SLIST_FIRST(&V_pf_osfp_list))) { SLIST_REMOVE_HEAD(&V_pf_osfp_list, fp_next); -#else - while ((fp = SLIST_FIRST(&pf_osfp_list))) { - SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next); -#endif while ((entry = SLIST_FIRST(&fp->fp_oses))) { SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry); - pool_put(&pf_osfp_entry_pl, entry); + free(entry, M_PFOSFP); } - pool_put(&pf_osfp_pl, fp); + free(fp, M_PFOSFP); } } @@ -400,6 +257,8 @@ pf_osfp_add(struct pf_osfp_ioctl *fpioc) struct pf_os_fingerprint *fp, fpadd; struct pf_osfp_entry *entry; + PF_RULES_WASSERT(); + memset(&fpadd, 0, sizeof(fpadd)); fpadd.fp_tcpopts = fpioc->fp_tcpopts; fpadd.fp_wsize = fpioc->fp_wsize; @@ -436,31 +295,18 @@ pf_osfp_add(struct pf_osfp_ioctl *fpioc) fpioc->fp_os.fp_os); #endif -#ifdef __FreeBSD__ if ((fp = pf_osfp_find_exact(&V_pf_osfp_list, &fpadd))) { -#else - if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) { -#endif SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os)) return (EEXIST); } - if ((entry = pool_get(&pf_osfp_entry_pl, -#ifdef __FreeBSD__ - PR_NOWAIT)) == NULL) -#else - PR_WAITOK|PR_LIMITFAIL)) == NULL) -#endif + if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT)) + == NULL) return (ENOMEM); } else { - if ((fp = pool_get(&pf_osfp_pl, -#ifdef __FreeBSD__ - PR_NOWAIT)) == NULL) -#else - PR_WAITOK|PR_LIMITFAIL)) == NULL) -#endif + if ((fp = malloc(sizeof(*fp), M_PFOSFP, M_ZERO | M_NOWAIT)) + == NULL) return (ENOMEM); - memset(fp, 0, sizeof(*fp)); fp->fp_tcpopts = fpioc->fp_tcpopts; fp->fp_wsize = fpioc->fp_wsize; fp->fp_psize = fpioc->fp_psize; @@ -470,20 +316,12 @@ pf_osfp_add(struct pf_osfp_ioctl *fpioc) fp->fp_wscale = fpioc->fp_wscale; fp->fp_ttl = fpioc->fp_ttl; SLIST_INIT(&fp->fp_oses); - if ((entry = pool_get(&pf_osfp_entry_pl, -#ifdef __FreeBSD__ - PR_NOWAIT)) == NULL) { -#else - PR_WAITOK|PR_LIMITFAIL)) == NULL) { -#endif - pool_put(&pf_osfp_pl, fp); + if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT)) + == NULL) { + free(fp, M_PFOSFP); return (ENOMEM); } -#ifdef __FreeBSD__ pf_osfp_insert(&V_pf_osfp_list, fp); -#else - pf_osfp_insert(&pf_osfp_list, fp); -#endif } memcpy(entry, &fpioc->fp_os, sizeof(*entry)); @@ -503,7 +341,7 @@ pf_osfp_add(struct pf_osfp_ioctl *fpioc) /* Find a fingerprint in the list */ -struct pf_os_fingerprint * +static struct pf_os_fingerprint * pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find, u_int8_t ttldiff) { @@ -578,7 +416,7 @@ pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find, } /* Find an exact fingerprint in the list */ -struct pf_os_fingerprint * +static struct pf_os_fingerprint * pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find) { struct pf_os_fingerprint *f; @@ -599,7 +437,7 @@ pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find) } /* Insert a fingerprint into the list */ -void +static void pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins) { struct pf_os_fingerprint *f, *prev = NULL; @@ -625,11 +463,7 @@ pf_osfp_get(struct pf_osfp_ioctl *fpioc) memset(fpioc, 0, sizeof(*fpioc)); -#ifdef __FreeBSD__ SLIST_FOREACH(fp, &V_pf_osfp_list, fp_next) { -#else - SLIST_FOREACH(fp, &pf_osfp_list, fp_next) { -#endif SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { if (i++ == num) { fpioc->fp_mss = fp->fp_mss; @@ -650,17 +484,14 @@ pf_osfp_get(struct pf_osfp_ioctl *fpioc) } +#ifdef PFDEBUG /* Validate that each signature is reachable */ -struct pf_os_fingerprint * +static struct pf_os_fingerprint * pf_osfp_validate(void) { struct pf_os_fingerprint *f, *f2, find; -#ifdef __FreeBSD__ SLIST_FOREACH(f, &V_pf_osfp_list, fp_next) { -#else - SLIST_FOREACH(f, &pf_osfp_list, fp_next) { -#endif memcpy(&find, f, sizeof(find)); /* We do a few MSS/th_win percolations to make things unique */ @@ -672,11 +503,7 @@ pf_osfp_validate(void) find.fp_wsize *= (find.fp_mss + 40); else if (f->fp_flags & PF_OSFP_WSIZE_MOD) find.fp_wsize *= 2; -#ifdef __FreeBSD__ if (f != (f2 = pf_osfp_find(&V_pf_osfp_list, &find, 0))) { -#else - if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) { -#endif if (f2) printf("Found \"%s %s %s\" instead of " "\"%s %s %s\"\n", @@ -696,3 +523,4 @@ pf_osfp_validate(void) } return (NULL); } +#endif /* PFDEBUG */ diff --git a/sys/contrib/pf/net/pf_ruleset.c b/sys/contrib/pf/net/pf_ruleset.c index ca8667c571e5e..77652a69b3c58 100644 --- a/sys/contrib/pf/net/pf_ruleset.c +++ b/sys/contrib/pf/net/pf_ruleset.c @@ -35,15 +35,14 @@ * */ -#ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); -#endif #include #include #ifdef _KERNEL # include +# include #endif /* _KERNEL */ #include @@ -61,20 +60,10 @@ __FBSDID("$FreeBSD$"); #ifdef _KERNEL -#ifdef __FreeBSD__ #define DPFPRINTF(format, x...) \ if (V_pf_status.debug >= PF_DEBUG_NOISY) \ printf(format , ##x) -#else -#define DPFPRINTF(format, x...) \ - if (pf_status.debug >= PF_DEBUG_NOISY) \ - printf(format , ##x) -#endif -#ifdef __FreeBSD__ #define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO) -#else -#define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO) -#endif #define rs_free(x) free(x, M_TEMP) #else @@ -96,24 +85,22 @@ __FBSDID("$FreeBSD$"); #endif /* PFDEBUG */ #endif /* _KERNEL */ -#if defined(__FreeBSD__) && !defined(_KERNEL) -#undef V_pf_anchors -#define V_pf_anchors pf_anchors - -#undef pf_main_ruleset -#define pf_main_ruleset pf_main_anchor.ruleset -#endif - -#if defined(__FreeBSD__) && defined(_KERNEL) +#ifdef _KERNEL VNET_DEFINE(struct pf_anchor_global, pf_anchors); VNET_DEFINE(struct pf_anchor, pf_main_anchor); -#else +#else /* ! _KERNEL */ struct pf_anchor_global pf_anchors; struct pf_anchor pf_main_anchor; -#endif +#undef V_pf_anchors +#define V_pf_anchors pf_anchors +#undef pf_main_ruleset +#define pf_main_ruleset pf_main_anchor.ruleset +#endif /* _KERNEL */ static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); +static struct pf_anchor *pf_find_anchor(const char *); + RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); @@ -169,7 +156,7 @@ pf_init_ruleset(struct pf_ruleset *ruleset) } } -struct pf_anchor * +static struct pf_anchor * pf_find_anchor(const char *path) { struct pf_anchor *key, *found; @@ -178,11 +165,7 @@ pf_find_anchor(const char *path) if (key == NULL) return (NULL); strlcpy(key->path, path, sizeof(key->path)); -#ifdef __FreeBSD__ found = RB_FIND(pf_anchor_global, &V_pf_anchors, key); -#else - found = RB_FIND(pf_anchor_global, &pf_anchors, key); -#endif rs_free(key); return (found); } @@ -208,11 +191,7 @@ pf_find_or_create_ruleset(const char *path) { char *p, *q, *r; struct pf_ruleset *ruleset; -#ifdef __FreeBSD__ struct pf_anchor *anchor = NULL, *dup, *parent = NULL; -#else - struct pf_anchor *anchor, *dup, *parent = NULL; -#endif if (path[0] == 0) return (&pf_main_ruleset); @@ -263,11 +242,7 @@ pf_find_or_create_ruleset(const char *path) strlcat(anchor->path, "/", sizeof(anchor->path)); } strlcat(anchor->path, anchor->name, sizeof(anchor->path)); -#ifdef __FreeBSD__ if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) != -#else - if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != -#endif NULL) { printf("pf_find_or_create_ruleset: RB_INSERT1 " "'%s' '%s' collides with '%s' '%s'\n", @@ -284,11 +259,7 @@ pf_find_or_create_ruleset(const char *path) "RB_INSERT2 '%s' '%s' collides with " "'%s' '%s'\n", anchor->path, anchor->name, dup->path, dup->name); -#ifdef __FreeBSD__ RB_REMOVE(pf_anchor_global, &V_pf_anchors, -#else - RB_REMOVE(pf_anchor_global, &pf_anchors, -#endif anchor); rs_free(anchor); rs_free(p); @@ -324,11 +295,7 @@ pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || ruleset->rules[i].inactive.open) return; -#ifdef __FreeBSD__ RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor); -#else - RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); -#endif if ((parent = ruleset->anchor->parent) != NULL) RB_REMOVE(pf_anchor_node, &parent->children, ruleset->anchor); diff --git a/sys/contrib/pf/net/pf_table.c b/sys/contrib/pf/net/pf_table.c index ea77e31375cec..fa88045dc5095 100644 --- a/sys/contrib/pf/net/pf_table.c +++ b/sys/contrib/pf/net/pf_table.c @@ -30,31 +30,24 @@ * */ -#ifdef __FreeBSD__ -#include "opt_inet.h" -#include "opt_inet6.h" - #include __FBSDID("$FreeBSD$"); -#endif + +#include "opt_inet.h" +#include "opt_inet6.h" #include -#include -#include -#include #include -#ifdef __FreeBSD__ +#include #include -#else -#include -#endif +#include +#include +#include +#include +#include #include -#include -#include -#ifndef __FreeBSD__ -#include -#endif +#include #include #define ACCEPT_FLAGS(flags, oklist) \ @@ -64,53 +57,6 @@ __FBSDID("$FreeBSD$"); return (EINVAL); \ } while (0) -#ifdef __FreeBSD__ -static inline int -_copyin(const void *uaddr, void *kaddr, size_t len) -{ - int r; - - PF_UNLOCK(); - r = copyin(uaddr, kaddr, len); - PF_LOCK(); - - return (r); -} - -static inline int -_copyout(const void *uaddr, void *kaddr, size_t len) -{ - int r; - - PF_UNLOCK(); - r = copyout(uaddr, kaddr, len); - PF_LOCK(); - - return (r); -} - -#define COPYIN(from, to, size, flags) \ - ((flags & PFR_FLAG_USERIOCTL) ? \ - _copyin((from), (to), (size)) : \ - (bcopy((from), (to), (size)), 0)) - -#define COPYOUT(from, to, size, flags) \ - ((flags & PFR_FLAG_USERIOCTL) ? \ - _copyout((from), (to), (size)) : \ - (bcopy((from), (to), (size)), 0)) - -#else -#define COPYIN(from, to, size, flags) \ - ((flags & PFR_FLAG_USERIOCTL) ? \ - copyin((from), (to), (size)) : \ - (bcopy((from), (to), (size)), 0)) - -#define COPYOUT(from, to, size, flags) \ - ((flags & PFR_FLAG_USERIOCTL) ? \ - copyout((from), (to), (size)) : \ - (bcopy((from), (to), (size)), 0)) -#endif - #define FILLIN_SIN(sin, addr) \ do { \ (sin).sin_len = sizeof(sin); \ @@ -164,7 +110,6 @@ struct pfr_walktree { struct pfi_dynaddr *pfrw1_dyn; } pfrw_1; int pfrw_free; - int pfrw_flags; }; #define pfrw_addr pfrw_1.pfrw1_addr #define pfrw_astats pfrw_1.pfrw1_astats @@ -175,77 +120,69 @@ struct pfr_walktree { #define senderr(e) do { rv = (e); goto _bad; } while (0) -#ifdef __FreeBSD__ -VNET_DEFINE(uma_zone_t, pfr_ktable_pl); -VNET_DEFINE(uma_zone_t, pfr_kentry_pl); -VNET_DEFINE(uma_zone_t, pfr_kcounters_pl); -VNET_DEFINE(struct sockaddr_in, pfr_sin); -#define V_pfr_sin VNET(pfr_sin) -VNET_DEFINE(struct sockaddr_in6, pfr_sin6); -#define V_pfr_sin6 VNET(pfr_sin6) -VNET_DEFINE(union sockaddr_union, pfr_mask); -#define V_pfr_mask VNET(pfr_mask) -VNET_DEFINE(struct pf_addr, pfr_ffaddr); -#define V_pfr_ffaddr VNET(pfr_ffaddr) -#else -struct pool pfr_ktable_pl; -struct pool pfr_kentry_pl; -struct pool pfr_kcounters_pl; -struct sockaddr_in pfr_sin; -struct sockaddr_in6 pfr_sin6; -union sockaddr_union pfr_mask; -struct pf_addr pfr_ffaddr; -#endif - -void pfr_copyout_addr(struct pfr_addr *, +static MALLOC_DEFINE(M_PFTABLE, "pf_table", "pf(4) tables structures"); +static VNET_DEFINE(uma_zone_t, pfr_kentry_z); +#define V_pfr_kentry_z VNET(pfr_kentry_z) +static VNET_DEFINE(uma_zone_t, pfr_kcounters_z); +#define V_pfr_kcounters_z VNET(pfr_kcounters_z) + +static struct pf_addr pfr_ffaddr = { + .addr32 = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff } +}; + +static void pfr_copyout_addr(struct pfr_addr *, struct pfr_kentry *ke); -int pfr_validate_addr(struct pfr_addr *); -void pfr_enqueue_addrs(struct pfr_ktable *, +static int pfr_validate_addr(struct pfr_addr *); +static void pfr_enqueue_addrs(struct pfr_ktable *, struct pfr_kentryworkq *, int *, int); -void pfr_mark_addrs(struct pfr_ktable *); -struct pfr_kentry *pfr_lookup_addr(struct pfr_ktable *, +static void pfr_mark_addrs(struct pfr_ktable *); +static struct pfr_kentry + *pfr_lookup_addr(struct pfr_ktable *, struct pfr_addr *, int); -struct pfr_kentry *pfr_create_kentry(struct pfr_addr *, int); -void pfr_destroy_kentries(struct pfr_kentryworkq *); -void pfr_destroy_kentry(struct pfr_kentry *); -void pfr_insert_kentries(struct pfr_ktable *, +static struct pfr_kentry *pfr_create_kentry(struct pfr_addr *); +static void pfr_destroy_kentries(struct pfr_kentryworkq *); +static void pfr_destroy_kentry(struct pfr_kentry *); +static void pfr_insert_kentries(struct pfr_ktable *, struct pfr_kentryworkq *, long); -void pfr_remove_kentries(struct pfr_ktable *, +static void pfr_remove_kentries(struct pfr_ktable *, struct pfr_kentryworkq *); -void pfr_clstats_kentries(struct pfr_kentryworkq *, long, +static void pfr_clstats_kentries(struct pfr_kentryworkq *, long, int); -void pfr_reset_feedback(struct pfr_addr *, int, int); -void pfr_prepare_network(union sockaddr_union *, int, int); -int pfr_route_kentry(struct pfr_ktable *, +static void pfr_reset_feedback(struct pfr_addr *, int); +static void pfr_prepare_network(union sockaddr_union *, int, int); +static int pfr_route_kentry(struct pfr_ktable *, struct pfr_kentry *); -int pfr_unroute_kentry(struct pfr_ktable *, +static int pfr_unroute_kentry(struct pfr_ktable *, struct pfr_kentry *); -int pfr_walktree(struct radix_node *, void *); -int pfr_validate_table(struct pfr_table *, int, int); -int pfr_fix_anchor(char *); -void pfr_commit_ktable(struct pfr_ktable *, long); -void pfr_insert_ktables(struct pfr_ktableworkq *); -void pfr_insert_ktable(struct pfr_ktable *); -void pfr_setflags_ktables(struct pfr_ktableworkq *); -void pfr_setflags_ktable(struct pfr_ktable *, int); -void pfr_clstats_ktables(struct pfr_ktableworkq *, long, +static int pfr_walktree(struct radix_node *, void *); +static int pfr_validate_table(struct pfr_table *, int, int); +static int pfr_fix_anchor(char *); +static void pfr_commit_ktable(struct pfr_ktable *, long); +static void pfr_insert_ktables(struct pfr_ktableworkq *); +static void pfr_insert_ktable(struct pfr_ktable *); +static void pfr_setflags_ktables(struct pfr_ktableworkq *); +static void pfr_setflags_ktable(struct pfr_ktable *, int); +static void pfr_clstats_ktables(struct pfr_ktableworkq *, long, int); -void pfr_clstats_ktable(struct pfr_ktable *, long, int); -struct pfr_ktable *pfr_create_ktable(struct pfr_table *, long, int, int); -void pfr_destroy_ktables(struct pfr_ktableworkq *, int); -void pfr_destroy_ktable(struct pfr_ktable *, int); -int pfr_ktable_compare(struct pfr_ktable *, +static void pfr_clstats_ktable(struct pfr_ktable *, long, int); +static struct pfr_ktable + *pfr_create_ktable(struct pfr_table *, long, int); +static void pfr_destroy_ktables(struct pfr_ktableworkq *, int); +static void pfr_destroy_ktable(struct pfr_ktable *, int); +static int pfr_ktable_compare(struct pfr_ktable *, struct pfr_ktable *); -struct pfr_ktable *pfr_lookup_table(struct pfr_table *); -void pfr_clean_node_mask(struct pfr_ktable *, +static struct pfr_ktable + *pfr_lookup_table(struct pfr_table *); +static void pfr_clean_node_mask(struct pfr_ktable *, struct pfr_kentryworkq *); -int pfr_table_count(struct pfr_table *, int); -int pfr_skip_table(struct pfr_table *, +static int pfr_table_count(struct pfr_table *, int); +static int pfr_skip_table(struct pfr_table *, struct pfr_ktable *, int); -struct pfr_kentry *pfr_kentry_byidx(struct pfr_ktable *, int, int); +static struct pfr_kentry + *pfr_kentry_byidx(struct pfr_ktable *, int, int); -RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); -RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); +static RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); +static RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); struct pfr_ktablehead pfr_ktables; struct pfr_table pfr_nulltable; @@ -254,28 +191,23 @@ int pfr_ktable_cnt; void pfr_initialize(void) { -#ifndef __FreeBSD__ - pool_init(&pfr_ktable_pl, sizeof(struct pfr_ktable), 0, 0, 0, - "pfrktable", NULL); - pool_init(&pfr_kentry_pl, sizeof(struct pfr_kentry), 0, 0, 0, - "pfrkentry", NULL); - pool_init(&pfr_kcounters_pl, sizeof(struct pfr_kcounters), 0, 0, 0, - "pfrkcounters", NULL); - - pfr_sin.sin_len = sizeof(pfr_sin); - pfr_sin.sin_family = AF_INET; - pfr_sin6.sin6_len = sizeof(pfr_sin6); - pfr_sin6.sin6_family = AF_INET6; - - memset(&pfr_ffaddr, 0xff, sizeof(pfr_ffaddr)); -#else - V_pfr_sin.sin_len = sizeof(V_pfr_sin); - V_pfr_sin.sin_family = AF_INET; - V_pfr_sin6.sin6_len = sizeof(V_pfr_sin6); - V_pfr_sin6.sin6_family = AF_INET6; - - memset(&V_pfr_ffaddr, 0xff, sizeof(V_pfr_ffaddr)); -#endif + + V_pfr_kentry_z = uma_zcreate("pf table entries", + sizeof(struct pfr_kentry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, + 0); + V_pfr_kcounters_z = uma_zcreate("pf table counters", + sizeof(struct pfr_kcounters), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + V_pf_limits[PF_LIMIT_TABLE_ENTRIES].zone = V_pfr_kentry_z; + V_pf_limits[PF_LIMIT_TABLE_ENTRIES].limit = PFR_KENTRY_HIWAT; +} + +void +pfr_cleanup(void) +{ + + uma_zdestroy(V_pfr_kentry_z); + uma_zdestroy(V_pfr_kcounters_z); } int @@ -283,9 +215,10 @@ pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) { struct pfr_ktable *kt; struct pfr_kentryworkq workq; - int s; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY); + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); @@ -296,16 +229,8 @@ pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) pfr_enqueue_addrs(kt, &workq, ndel, 0); if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); pfr_remove_kentries(kt, &workq); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - if (kt->pfrkt_cnt) { - printf("pfr_clr_addrs: corruption detected (%d).\n", - kt->pfrkt_cnt); - kt->pfrkt_cnt = 0; - } + KASSERT(kt->pfrkt_cnt == 0, ("%s: non-null pfrkt_cnt", __func__)); } return (0); } @@ -317,12 +242,13 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_ktable *kt, *tmpkt; struct pfr_kentryworkq workq; struct pfr_kentry *p, *q; - struct pfr_addr ad; - int i, rv, s, xadd = 0; + struct pfr_addr *ad; + int i, rv, xadd = 0; long tzero = time_second; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY | - PFR_FLAG_FEEDBACK); + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); @@ -330,53 +256,42 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, return (ESRCH); if (kt->pfrkt_flags & PFR_TFLAG_CONST) return (EPERM); - tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0, - !(flags & PFR_FLAG_USERIOCTL)); + tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); if (tmpkt == NULL) return (ENOMEM); SLIST_INIT(&workq); - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - senderr(EFAULT); - if (pfr_validate_addr(&ad)) + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) senderr(EINVAL); - p = pfr_lookup_addr(kt, &ad, 1); - q = pfr_lookup_addr(tmpkt, &ad, 1); + p = pfr_lookup_addr(kt, ad, 1); + q = pfr_lookup_addr(tmpkt, ad, 1); if (flags & PFR_FLAG_FEEDBACK) { if (q != NULL) - ad.pfra_fback = PFR_FB_DUPLICATE; + ad->pfra_fback = PFR_FB_DUPLICATE; else if (p == NULL) - ad.pfra_fback = PFR_FB_ADDED; - else if (p->pfrke_not != ad.pfra_not) - ad.pfra_fback = PFR_FB_CONFLICT; + ad->pfra_fback = PFR_FB_ADDED; + else if (p->pfrke_not != ad->pfra_not) + ad->pfra_fback = PFR_FB_CONFLICT; else - ad.pfra_fback = PFR_FB_NONE; + ad->pfra_fback = PFR_FB_NONE; } if (p == NULL && q == NULL) { - p = pfr_create_kentry(&ad, - !(flags & PFR_FLAG_USERIOCTL)); + p = pfr_create_kentry(ad); if (p == NULL) senderr(ENOMEM); if (pfr_route_kentry(tmpkt, p)) { pfr_destroy_kentry(p); - ad.pfra_fback = PFR_FB_NONE; + ad->pfra_fback = PFR_FB_NONE; } else { SLIST_INSERT_HEAD(&workq, p, pfrke_workq); xadd++; } } - if (flags & PFR_FLAG_FEEDBACK) - if (COPYOUT(&ad, addr+i, sizeof(ad), flags)) - senderr(EFAULT); } pfr_clean_node_mask(tmpkt, &workq); - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_insert_kentries(kt, &workq, tzero); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } else + else pfr_destroy_kentries(&workq); if (nadd != NULL) *nadd = xadd; @@ -386,7 +301,7 @@ _bad: pfr_clean_node_mask(tmpkt, &workq); pfr_destroy_kentries(&workq); if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size, flags); + pfr_reset_feedback(addr, size); pfr_destroy_ktable(tmpkt, 0); return (rv); } @@ -398,11 +313,12 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_ktable *kt; struct pfr_kentryworkq workq; struct pfr_kentry *p; - struct pfr_addr ad; - int i, rv, s, xdel = 0, log = 1; + struct pfr_addr *ad; + int i, rv, xdel = 0, log = 1; + + PF_RULES_WASSERT(); - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY | - PFR_FLAG_FEEDBACK); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); @@ -418,7 +334,7 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, * * one is O(N) and is better for large 'n' * one is O(n*LOG(N)) and is better for small 'n' - * + * * following code try to decide which one is best. */ for (i = kt->pfrkt_cnt; i > 0; i >>= 1) @@ -428,56 +344,44 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, pfr_mark_addrs(kt); } else { /* iterate over addresses to delete */ - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - return (EFAULT); - if (pfr_validate_addr(&ad)) + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) return (EINVAL); - p = pfr_lookup_addr(kt, &ad, 1); + p = pfr_lookup_addr(kt, ad, 1); if (p != NULL) p->pfrke_mark = 0; } } SLIST_INIT(&workq); - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - senderr(EFAULT); - if (pfr_validate_addr(&ad)) + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) senderr(EINVAL); - p = pfr_lookup_addr(kt, &ad, 1); + p = pfr_lookup_addr(kt, ad, 1); if (flags & PFR_FLAG_FEEDBACK) { if (p == NULL) - ad.pfra_fback = PFR_FB_NONE; - else if (p->pfrke_not != ad.pfra_not) - ad.pfra_fback = PFR_FB_CONFLICT; + ad->pfra_fback = PFR_FB_NONE; + else if (p->pfrke_not != ad->pfra_not) + ad->pfra_fback = PFR_FB_CONFLICT; else if (p->pfrke_mark) - ad.pfra_fback = PFR_FB_DUPLICATE; + ad->pfra_fback = PFR_FB_DUPLICATE; else - ad.pfra_fback = PFR_FB_DELETED; + ad->pfra_fback = PFR_FB_DELETED; } - if (p != NULL && p->pfrke_not == ad.pfra_not && + if (p != NULL && p->pfrke_not == ad->pfra_not && !p->pfrke_mark) { p->pfrke_mark = 1; SLIST_INSERT_HEAD(&workq, p, pfrke_workq); xdel++; } - if (flags & PFR_FLAG_FEEDBACK) - if (COPYOUT(&ad, addr+i, sizeof(ad), flags)) - senderr(EFAULT); } - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_remove_kentries(kt, &workq); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } if (ndel != NULL) *ndel = xdel; return (0); _bad: if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size, flags); + pfr_reset_feedback(addr, size); return (rv); } @@ -490,11 +394,12 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_kentryworkq addq, delq, changeq; struct pfr_kentry *p, *q; struct pfr_addr ad; - int i, rv, s, xadd = 0, xdel = 0, xchange = 0; + int i, rv, xadd = 0, xdel = 0, xchange = 0; long tzero = time_second; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY | - PFR_FLAG_FEEDBACK); + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); if (pfr_validate_table(tbl, ignore_pfrt_flags, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); @@ -503,8 +408,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, return (ESRCH); if (kt->pfrkt_flags & PFR_TFLAG_CONST) return (EPERM); - tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0, - !(flags & PFR_FLAG_USERIOCTL)); + tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); if (tmpkt == NULL) return (ENOMEM); pfr_mark_addrs(kt); @@ -512,8 +416,11 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, SLIST_INIT(&delq); SLIST_INIT(&changeq); for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - senderr(EFAULT); + /* + * XXXGL: undertand pf_if usage of this function + * and make ad a moving pointer + */ + bcopy(addr + i, &ad, sizeof(ad)); if (pfr_validate_addr(&ad)) senderr(EINVAL); ad.pfra_fback = PFR_FB_NONE; @@ -535,8 +442,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, ad.pfra_fback = PFR_FB_DUPLICATE; goto _skip; } - p = pfr_create_kentry(&ad, - !(flags & PFR_FLAG_USERIOCTL)); + p = pfr_create_kentry(&ad); if (p == NULL) senderr(ENOMEM); if (pfr_route_kentry(tmpkt, p)) { @@ -550,8 +456,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, } _skip: if (flags & PFR_FLAG_FEEDBACK) - if (COPYOUT(&ad, addr+i, sizeof(ad), flags)) - senderr(EFAULT); + bcopy(&ad, addr + i, sizeof(ad)); } pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); if ((flags & PFR_FLAG_FEEDBACK) && *size2) { @@ -563,20 +468,15 @@ _skip: SLIST_FOREACH(p, &delq, pfrke_workq) { pfr_copyout_addr(&ad, p); ad.pfra_fback = PFR_FB_DELETED; - if (COPYOUT(&ad, addr+size+i, sizeof(ad), flags)) - senderr(EFAULT); + bcopy(&ad, addr + size + i, sizeof(ad)); i++; } } pfr_clean_node_mask(tmpkt, &addq); if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); pfr_insert_kentries(kt, &addq, tzero); pfr_remove_kentries(kt, &delq); pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); - if (flags & PFR_FLAG_ATOMIC) - splx(s); } else pfr_destroy_kentries(&addq); if (nadd != NULL) @@ -593,7 +493,7 @@ _bad: pfr_clean_node_mask(tmpkt, &addq); pfr_destroy_kentries(&addq); if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size, flags); + pfr_reset_feedback(addr, size); pfr_destroy_ktable(tmpkt, 0); return (rv); } @@ -604,9 +504,11 @@ pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, { struct pfr_ktable *kt; struct pfr_kentry *p; - struct pfr_addr ad; + struct pfr_addr *ad; int i, xmatch = 0; + PF_RULES_RASSERT(); + ACCEPT_FLAGS(flags, PFR_FLAG_REPLACE); if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); @@ -614,22 +516,18 @@ pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) return (ESRCH); - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - return (EFAULT); - if (pfr_validate_addr(&ad)) + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) return (EINVAL); - if (ADDR_NETWORK(&ad)) + if (ADDR_NETWORK(ad)) return (EINVAL); - p = pfr_lookup_addr(kt, &ad, 0); + p = pfr_lookup_addr(kt, ad, 0); if (flags & PFR_FLAG_REPLACE) - pfr_copyout_addr(&ad, p); - ad.pfra_fback = (p == NULL) ? PFR_FB_NONE : + pfr_copyout_addr(ad, p); + ad->pfra_fback = (p == NULL) ? PFR_FB_NONE : (p->pfrke_not ? PFR_FB_NOTMATCH : PFR_FB_MATCH); if (p != NULL && !p->pfrke_not) xmatch++; - if (COPYOUT(&ad, addr+i, sizeof(ad), flags)) - return (EFAULT); } if (nmatch != NULL) *nmatch = xmatch; @@ -644,6 +542,8 @@ pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, struct pfr_walktree w; int rv; + PF_RULES_RASSERT(); + ACCEPT_FLAGS(flags, 0); if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); @@ -659,27 +559,16 @@ pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, w.pfrw_op = PFRW_GET_ADDRS; w.pfrw_addr = addr; w.pfrw_free = kt->pfrkt_cnt; - w.pfrw_flags = flags; -#ifdef __FreeBSD__ rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#else - rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#endif if (!rv) -#ifdef __FreeBSD__ rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#else - rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#endif if (rv) return (rv); - if (w.pfrw_free) { - printf("pfr_get_addrs: corruption detected (%d).\n", - w.pfrw_free); - return (ENOTTY); - } + KASSERT(w.pfrw_free == 0, ("%s: corruption detected (%d)", __func__, + w.pfrw_free)); + *size = kt->pfrkt_cnt; return (0); } @@ -691,11 +580,13 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, struct pfr_ktable *kt; struct pfr_walktree w; struct pfr_kentryworkq workq; - int rv, s; + int rv; long tzero = time_second; + PF_RULES_RASSERT(); + /* XXX PFR_FLAG_CLSTATS disabled */ - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC); + ACCEPT_FLAGS(flags, 0); if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); kt = pfr_lookup_table(tbl); @@ -710,27 +601,14 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, w.pfrw_op = PFRW_GET_ASTATS; w.pfrw_astats = addr; w.pfrw_free = kt->pfrkt_cnt; - w.pfrw_flags = flags; - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); -#ifdef __FreeBSD__ rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#else - rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#endif if (!rv) -#ifdef __FreeBSD__ - rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, + rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#else - rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#endif if (!rv && (flags & PFR_FLAG_CLSTATS)) { pfr_enqueue_addrs(kt, &workq, NULL, 0); pfr_clstats_kentries(&workq, tzero, 0); } - if (flags & PFR_FLAG_ATOMIC) - splx(s); if (rv) return (rv); @@ -750,28 +628,25 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_ktable *kt; struct pfr_kentryworkq workq; struct pfr_kentry *p; - struct pfr_addr ad; - int i, rv, s, xzero = 0; + struct pfr_addr *ad; + int i, rv, xzero = 0; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY | - PFR_FLAG_FEEDBACK); + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) return (ESRCH); SLIST_INIT(&workq); - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - senderr(EFAULT); - if (pfr_validate_addr(&ad)) + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) senderr(EINVAL); - p = pfr_lookup_addr(kt, &ad, 1); + p = pfr_lookup_addr(kt, ad, 1); if (flags & PFR_FLAG_FEEDBACK) { - ad.pfra_fback = (p != NULL) ? + ad->pfra_fback = (p != NULL) ? PFR_FB_CLEARED : PFR_FB_NONE; - if (COPYOUT(&ad, addr+i, sizeof(ad), flags)) - senderr(EFAULT); } if (p != NULL) { SLIST_INSERT_HEAD(&workq, p, pfrke_workq); @@ -779,23 +654,18 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, } } - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_clstats_kentries(&workq, 0, 0); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } if (nzero != NULL) *nzero = xzero; return (0); _bad: if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size, flags); + pfr_reset_feedback(addr, size); return (rv); } -int +static int pfr_validate_addr(struct pfr_addr *ad) { int i; @@ -829,7 +699,7 @@ pfr_validate_addr(struct pfr_addr *ad) return (0); } -void +static void pfr_enqueue_addrs(struct pfr_ktable *kt, struct pfr_kentryworkq *workq, int *naddr, int sweep) { @@ -840,58 +710,37 @@ pfr_enqueue_addrs(struct pfr_ktable *kt, struct pfr_kentryworkq *workq, w.pfrw_op = sweep ? PFRW_SWEEP : PFRW_ENQUEUE; w.pfrw_workq = workq; if (kt->pfrkt_ip4 != NULL) -#ifdef __FreeBSD__ - if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, + if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) -#else - if (rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) -#endif printf("pfr_enqueue_addrs: IPv4 walktree failed.\n"); if (kt->pfrkt_ip6 != NULL) -#ifdef __FreeBSD__ - if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, + if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) -#else - if (rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) -#endif printf("pfr_enqueue_addrs: IPv6 walktree failed.\n"); if (naddr != NULL) *naddr = w.pfrw_cnt; } -void +static void pfr_mark_addrs(struct pfr_ktable *kt) { struct pfr_walktree w; bzero(&w, sizeof(w)); w.pfrw_op = PFRW_MARK; -#ifdef __FreeBSD__ if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) -#else - if (rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) -#endif printf("pfr_mark_addrs: IPv4 walktree failed.\n"); -#ifdef __FreeBSD__ if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) -#else - if (rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) -#endif printf("pfr_mark_addrs: IPv6 walktree failed.\n"); } -struct pfr_kentry * +static struct pfr_kentry * pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact) { union sockaddr_union sa, mask; -#ifdef __FreeBSD__ struct radix_node_head *head = NULL; -#else - struct radix_node_head *head; -#endif struct pfr_kentry *ke; - int s; bzero(&sa, sizeof(sa)); if (ad->pfra_af == AF_INET) { @@ -903,12 +752,7 @@ pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact) } if (ADDR_NETWORK(ad)) { pfr_prepare_network(&mask, ad->pfra_af, ad->pfra_net); - s = splsoftnet(); /* rn_lookup makes use of globals */ -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#endif ke = (struct pfr_kentry *)rn_lookup(&sa, &mask, head); - splx(s); if (ke && KENTRY_RNF_ROOT(ke)) ke = NULL; } else { @@ -921,19 +765,12 @@ pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact) return (ke); } -struct pfr_kentry * -pfr_create_kentry(struct pfr_addr *ad, int intr) +static struct pfr_kentry * +pfr_create_kentry(struct pfr_addr *ad) { struct pfr_kentry *ke; -#ifdef __FreeBSD__ - ke = pool_get(&V_pfr_kentry_pl, PR_NOWAIT | PR_ZERO); -#else - if (intr) - ke = pool_get(&pfr_kentry_pl, PR_NOWAIT | PR_ZERO); - else - ke = pool_get(&pfr_kentry_pl, PR_WAITOK|PR_ZERO|PR_LIMITFAIL); -#endif + ke = uma_zalloc(V_pfr_kentry_z, M_NOWAIT | M_ZERO); if (ke == NULL) return (NULL); @@ -947,7 +784,7 @@ pfr_create_kentry(struct pfr_addr *ad, int intr) return (ke); } -void +static void pfr_destroy_kentries(struct pfr_kentryworkq *workq) { struct pfr_kentry *p, *q; @@ -958,20 +795,15 @@ pfr_destroy_kentries(struct pfr_kentryworkq *workq) } } -void +static void pfr_destroy_kentry(struct pfr_kentry *ke) { if (ke->pfrke_counters) -#ifdef __FreeBSD__ - pool_put(&V_pfr_kcounters_pl, ke->pfrke_counters); - pool_put(&V_pfr_kentry_pl, ke); -#else - pool_put(&pfr_kcounters_pl, ke->pfrke_counters); - pool_put(&pfr_kentry_pl, ke); -#endif + uma_zfree(V_pfr_kcounters_z, ke->pfrke_counters); + uma_zfree(V_pfr_kentry_z, ke); } -void +static void pfr_insert_kentries(struct pfr_ktable *kt, struct pfr_kentryworkq *workq, long tzero) { @@ -1000,7 +832,7 @@ pfr_insert_kentry(struct pfr_ktable *kt, struct pfr_addr *ad, long tzero) p = pfr_lookup_addr(kt, ad, 1); if (p != NULL) return (0); - p = pfr_create_kentry(ad, 1); + p = pfr_create_kentry(ad); if (p == NULL) return (EINVAL); @@ -1014,7 +846,7 @@ pfr_insert_kentry(struct pfr_ktable *kt, struct pfr_addr *ad, long tzero) return (0); } -void +static void pfr_remove_kentries(struct pfr_ktable *kt, struct pfr_kentryworkq *workq) { @@ -1029,7 +861,7 @@ pfr_remove_kentries(struct pfr_ktable *kt, pfr_destroy_kentries(workq); } -void +static void pfr_clean_node_mask(struct pfr_ktable *kt, struct pfr_kentryworkq *workq) { @@ -1039,45 +871,33 @@ pfr_clean_node_mask(struct pfr_ktable *kt, pfr_unroute_kentry(kt, p); } -void +static void pfr_clstats_kentries(struct pfr_kentryworkq *workq, long tzero, int negchange) { struct pfr_kentry *p; - int s; SLIST_FOREACH(p, workq, pfrke_workq) { - s = splsoftnet(); if (negchange) p->pfrke_not = !p->pfrke_not; if (p->pfrke_counters) { -#ifdef __FreeBSD__ - pool_put(&V_pfr_kcounters_pl, p->pfrke_counters); -#else - pool_put(&pfr_kcounters_pl, p->pfrke_counters); -#endif + uma_zfree(V_pfr_kcounters_z, p->pfrke_counters); p->pfrke_counters = NULL; } - splx(s); p->pfrke_tzero = tzero; } } -void -pfr_reset_feedback(struct pfr_addr *addr, int size, int flags) +static void +pfr_reset_feedback(struct pfr_addr *addr, int size) { - struct pfr_addr ad; + struct pfr_addr *ad; int i; - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - break; - ad.pfra_fback = PFR_FB_NONE; - if (COPYOUT(&ad, addr+i, sizeof(ad), flags)) - break; - } + for (i = 0, ad = addr; i < size; i++, ad++) + ad->pfra_fback = PFR_FB_NONE; } -void +static void pfr_prepare_network(union sockaddr_union *sa, int af, int net) { int i; @@ -1102,17 +922,12 @@ pfr_prepare_network(union sockaddr_union *sa, int af, int net) } } -int +static int pfr_route_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) { union sockaddr_union mask; struct radix_node *rn; -#ifdef __FreeBSD__ struct radix_node_head *head = NULL; -#else - struct radix_node_head *head; -#endif - int s; bzero(ke->pfrke_node, sizeof(ke->pfrke_node)); if (ke->pfrke_af == AF_INET) @@ -1120,63 +935,32 @@ pfr_route_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) else if (ke->pfrke_af == AF_INET6) head = kt->pfrkt_ip6; - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#endif if (KENTRY_NETWORK(ke)) { pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); -#ifdef __FreeBSD__ rn = rn_addroute(&ke->pfrke_sa, &mask, head, ke->pfrke_node); -#else - rn = rn_addroute(&ke->pfrke_sa, &mask, head, ke->pfrke_node, 0); -#endif } else -#ifdef __FreeBSD__ rn = rn_addroute(&ke->pfrke_sa, NULL, head, ke->pfrke_node); -#else - rn = rn_addroute(&ke->pfrke_sa, NULL, head, ke->pfrke_node, 0); -#endif - splx(s); return (rn == NULL ? -1 : 0); } -int +static int pfr_unroute_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) { union sockaddr_union mask; struct radix_node *rn; -#ifdef __FreeBSD__ struct radix_node_head *head = NULL; -#else - struct radix_node_head *head; -#endif - int s; if (ke->pfrke_af == AF_INET) head = kt->pfrkt_ip4; else if (ke->pfrke_af == AF_INET6) head = kt->pfrkt_ip6; - s = splsoftnet(); -#ifdef __FreeBSD__ - PF_LOCK_ASSERT(); -#endif if (KENTRY_NETWORK(ke)) { pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); -#ifdef __FreeBSD__ rn = rn_delete(&ke->pfrke_sa, &mask, head); -#else - rn = rn_delete(&ke->pfrke_sa, &mask, head, NULL); -#endif } else -#ifdef __FreeBSD__ rn = rn_delete(&ke->pfrke_sa, NULL, head); -#else - rn = rn_delete(&ke->pfrke_sa, NULL, head, NULL); -#endif - splx(s); if (rn == NULL) { printf("pfr_unroute_kentry: delete failed.\n"); @@ -1185,7 +969,7 @@ pfr_unroute_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) return (0); } -void +static void pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke) { bzero(ad, sizeof(*ad)); @@ -1200,12 +984,11 @@ pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke) ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr; } -int +static int pfr_walktree(struct radix_node *rn, void *arg) { struct pfr_kentry *ke = (struct pfr_kentry *)rn; struct pfr_walktree *w = arg; - int s, flags = w->pfrw_flags; switch (w->pfrw_op) { case PFRW_MARK: @@ -1221,11 +1004,7 @@ pfr_walktree(struct radix_node *rn, void *arg) break; case PFRW_GET_ADDRS: if (w->pfrw_free-- > 0) { - struct pfr_addr ad; - - pfr_copyout_addr(&ad, ke); - if (copyout(&ad, w->pfrw_addr, sizeof(ad))) - return (EFAULT); + pfr_copyout_addr(w->pfrw_addr, ke); w->pfrw_addr++; } break; @@ -1235,7 +1014,6 @@ pfr_walktree(struct radix_node *rn, void *arg) pfr_copyout_addr(&as.pfras_a, ke); - s = splsoftnet(); if (ke->pfrke_counters) { bcopy(ke->pfrke_counters->pfrkc_packets, as.pfras_packets, sizeof(as.pfras_packets)); @@ -1246,11 +1024,9 @@ pfr_walktree(struct radix_node *rn, void *arg) bzero(as.pfras_bytes, sizeof(as.pfras_bytes)); as.pfras_a.pfra_fback = PFR_FB_NOCOUNT; } - splx(s); as.pfras_tzero = ke->pfrke_tzero; - if (COPYOUT(&as, w->pfrw_astats, sizeof(as), flags)) - return (EFAULT); + bcopy(&as, w->pfrw_astats, sizeof(as)); w->pfrw_astats++; } break; @@ -1263,40 +1039,28 @@ pfr_walktree(struct radix_node *rn, void *arg) } break; case PFRW_DYNADDR_UPDATE: + { + union sockaddr_union pfr_mask; + if (ke->pfrke_af == AF_INET) { if (w->pfrw_dyn->pfid_acnt4++ > 0) break; -#ifdef __FreeBSD__ - pfr_prepare_network(&V_pfr_mask, AF_INET, ke->pfrke_net); -#else pfr_prepare_network(&pfr_mask, AF_INET, ke->pfrke_net); -#endif - w->pfrw_dyn->pfid_addr4 = *SUNION2PF( - &ke->pfrke_sa, AF_INET); - w->pfrw_dyn->pfid_mask4 = *SUNION2PF( -#ifdef __FreeBSD__ - &V_pfr_mask, AF_INET); -#else - &pfr_mask, AF_INET); -#endif + w->pfrw_dyn->pfid_addr4 = *SUNION2PF(&ke->pfrke_sa, + AF_INET); + w->pfrw_dyn->pfid_mask4 = *SUNION2PF(&pfr_mask, + AF_INET); } else if (ke->pfrke_af == AF_INET6){ if (w->pfrw_dyn->pfid_acnt6++ > 0) break; -#ifdef __FreeBSD__ - pfr_prepare_network(&V_pfr_mask, AF_INET6, ke->pfrke_net); -#else pfr_prepare_network(&pfr_mask, AF_INET6, ke->pfrke_net); -#endif - w->pfrw_dyn->pfid_addr6 = *SUNION2PF( - &ke->pfrke_sa, AF_INET6); - w->pfrw_dyn->pfid_mask6 = *SUNION2PF( -#ifdef __FreeBSD__ - &V_pfr_mask, AF_INET6); -#else - &pfr_mask, AF_INET6); -#endif + w->pfrw_dyn->pfid_addr6 = *SUNION2PF(&ke->pfrke_sa, + AF_INET6); + w->pfrw_dyn->pfid_mask6 = *SUNION2PF(&pfr_mask, + AF_INET6); } break; + } } return (0); } @@ -1306,10 +1070,9 @@ pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) { struct pfr_ktableworkq workq; struct pfr_ktable *p; - int s, xdel = 0; + int xdel = 0; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY | - PFR_FLAG_ALLRSETS); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ALLRSETS); if (pfr_fix_anchor(filter->pfrt_anchor)) return (EINVAL); if (pfr_table_count(filter, flags) < 0) @@ -1327,13 +1090,8 @@ pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); xdel++; } - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_setflags_ktables(&workq); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } if (ndel != NULL) *ndel = xdel; return (0); @@ -1344,23 +1102,21 @@ pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) { struct pfr_ktableworkq addq, changeq; struct pfr_ktable *p, *q, *r, key; - int i, rv, s, xadd = 0; + int i, rv, xadd = 0; long tzero = time_second; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); SLIST_INIT(&addq); SLIST_INIT(&changeq); for (i = 0; i < size; i++) { - if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags)) - senderr(EFAULT); + bcopy(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t)); if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK, flags & PFR_FLAG_USERIOCTL)) senderr(EINVAL); key.pfrkt_flags |= PFR_TFLAG_ACTIVE; p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); if (p == NULL) { - p = pfr_create_ktable(&key.pfrkt_t, tzero, 1, - !(flags & PFR_FLAG_USERIOCTL)); + p = pfr_create_ktable(&key.pfrkt_t, tzero, 1); if (p == NULL) senderr(ENOMEM); SLIST_FOREACH(q, &addq, pfrkt_workq) { @@ -1386,8 +1142,7 @@ pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) } } key.pfrkt_flags = 0; - r = pfr_create_ktable(&key.pfrkt_t, 0, 1, - !(flags & PFR_FLAG_USERIOCTL)); + r = pfr_create_ktable(&key.pfrkt_t, 0, 1); if (r == NULL) senderr(ENOMEM); SLIST_INSERT_HEAD(&addq, r, pfrkt_workq); @@ -1405,12 +1160,8 @@ _skip: ; } if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); pfr_insert_ktables(&addq); pfr_setflags_ktables(&changeq); - if (flags & PFR_FLAG_ATOMIC) - splx(s); } else pfr_destroy_ktables(&addq, 0); if (nadd != NULL) @@ -1426,13 +1177,12 @@ pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) { struct pfr_ktableworkq workq; struct pfr_ktable *p, *q, key; - int i, s, xdel = 0; + int i, xdel = 0; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); SLIST_INIT(&workq); for (i = 0; i < size; i++) { - if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags)) - return (EFAULT); + bcopy(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t)); if (pfr_validate_table(&key.pfrkt_t, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); @@ -1449,13 +1199,8 @@ _skip: ; } - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_setflags_ktables(&workq); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } if (ndel != NULL) *ndel = xdel; return (0); @@ -1468,6 +1213,8 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, struct pfr_ktable *p; int n, nn; + PF_RULES_RASSERT(); + ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS); if (pfr_fix_anchor(filter->pfrt_anchor)) return (EINVAL); @@ -1483,13 +1230,11 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, continue; if (n-- <= 0) continue; - if (COPYOUT(&p->pfrkt_t, tbl++, sizeof(*tbl), flags)) - return (EFAULT); - } - if (n) { - printf("pfr_get_tables: corruption detected (%d).\n", n); - return (ENOTTY); + bcopy(&p->pfrkt_t, tbl++, sizeof(*tbl)); } + + KASSERT(n == 0, ("%s: corruption detected (%d)", __func__, n)); + *size = nn; return (0); } @@ -1500,11 +1245,11 @@ pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, { struct pfr_ktable *p; struct pfr_ktableworkq workq; - int s, n, nn; + int n, nn; long tzero = time_second; /* XXX PFR_FLAG_CLSTATS disabled */ - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_ALLRSETS); + ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS); if (pfr_fix_anchor(filter->pfrt_anchor)) return (EINVAL); n = nn = pfr_table_count(filter, flags); @@ -1515,32 +1260,20 @@ pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, return (0); } SLIST_INIT(&workq); - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { if (pfr_skip_table(filter, p, flags)) continue; if (n-- <= 0) continue; - if (!(flags & PFR_FLAG_ATOMIC)) - s = splsoftnet(); - if (COPYOUT(&p->pfrkt_ts, tbl++, sizeof(*tbl), flags)) { - splx(s); - return (EFAULT); - } - if (!(flags & PFR_FLAG_ATOMIC)) - splx(s); + bcopy(&p->pfrkt_ts, tbl++, sizeof(*tbl)); SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); } if (flags & PFR_FLAG_CLSTATS) pfr_clstats_ktables(&workq, tzero, flags & PFR_FLAG_ADDRSTOO); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - if (n) { - printf("pfr_get_tstats: corruption detected (%d).\n", n); - return (ENOTTY); - } + + KASSERT(n == 0, ("%s: corruption detected (%d)", __func__, n)); + *size = nn; return (0); } @@ -1550,15 +1283,13 @@ pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) { struct pfr_ktableworkq workq; struct pfr_ktable *p, key; - int i, s, xzero = 0; + int i, xzero = 0; long tzero = time_second; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY | - PFR_FLAG_ADDRSTOO); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO); SLIST_INIT(&workq); for (i = 0; i < size; i++) { - if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags)) - return (EFAULT); + bcopy(tbl + i, &key.pfrkt_t, sizeof(key.pfrkt_t)); if (pfr_validate_table(&key.pfrkt_t, 0, 0)) return (EINVAL); p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); @@ -1567,13 +1298,8 @@ pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) xzero++; } } - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_clstats_ktables(&workq, tzero, flags & PFR_FLAG_ADDRSTOO); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } if (nzero != NULL) *nzero = xzero; return (0); @@ -1585,17 +1311,16 @@ pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, { struct pfr_ktableworkq workq; struct pfr_ktable *p, *q, key; - int i, s, xchange = 0, xdel = 0; + int i, xchange = 0, xdel = 0; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); if ((setflag & ~PFR_TFLAG_USRMASK) || (clrflag & ~PFR_TFLAG_USRMASK) || (setflag & clrflag)) return (EINVAL); SLIST_INIT(&workq); for (i = 0; i < size; i++) { - if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags)) - return (EFAULT); + bcopy(tbl + i, &key.pfrkt_t, sizeof(key.pfrkt_t)); if (pfr_validate_table(&key.pfrkt_t, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); @@ -1619,13 +1344,8 @@ pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, _skip: ; } - if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); + if (!(flags & PFR_FLAG_DUMMY)) pfr_setflags_ktables(&workq); - if (flags & PFR_FLAG_ATOMIC) - splx(s); - } if (nchange != NULL) *nchange = xchange; if (ndel != NULL) @@ -1674,10 +1394,12 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_kentryworkq addrq; struct pfr_ktable *kt, *rt, *shadow, key; struct pfr_kentry *p; - struct pfr_addr ad; + struct pfr_addr *ad; struct pf_ruleset *rs; int i, rv, xadd = 0, xaddr = 0; + PF_RULES_WASSERT(); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO); if (size && !(flags & PFR_FLAG_ADDRSTOO)) return (EINVAL); @@ -1691,8 +1413,7 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, SLIST_INIT(&tableq); kt = RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl); if (kt == NULL) { - kt = pfr_create_ktable(tbl, 0, 1, - !(flags & PFR_FLAG_USERIOCTL)); + kt = pfr_create_ktable(tbl, 0, 1); if (kt == NULL) return (ENOMEM); SLIST_INSERT_HEAD(&tableq, kt, pfrkt_workq); @@ -1708,8 +1429,7 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, kt->pfrkt_root = rt; goto _skip; } - rt = pfr_create_ktable(&key.pfrkt_t, 0, 1, - !(flags & PFR_FLAG_USERIOCTL)); + rt = pfr_create_ktable(&key.pfrkt_t, 0, 1); if (rt == NULL) { pfr_destroy_ktables(&tableq, 0); return (ENOMEM); @@ -1719,20 +1439,18 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, } else if (!(kt->pfrkt_flags & PFR_TFLAG_INACTIVE)) xadd++; _skip: - shadow = pfr_create_ktable(tbl, 0, 0, !(flags & PFR_FLAG_USERIOCTL)); + shadow = pfr_create_ktable(tbl, 0, 0); if (shadow == NULL) { pfr_destroy_ktables(&tableq, 0); return (ENOMEM); } SLIST_INIT(&addrq); - for (i = 0; i < size; i++) { - if (COPYIN(addr+i, &ad, sizeof(ad), flags)) - senderr(EFAULT); - if (pfr_validate_addr(&ad)) + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) senderr(EINVAL); - if (pfr_lookup_addr(shadow, &ad, 1) != NULL) + if (pfr_lookup_addr(shadow, ad, 1) != NULL) continue; - p = pfr_create_kentry(&ad, 0); + p = pfr_create_kentry(ad); if (p == NULL) senderr(ENOMEM); if (pfr_route_kentry(shadow, p)) { @@ -1776,6 +1494,8 @@ pfr_ina_rollback(struct pfr_table *trs, u_int32_t ticket, int *ndel, int flags) struct pf_ruleset *rs; int xdel = 0; + PF_RULES_WASSERT(); + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); rs = pf_find_ruleset(trs->pfrt_anchor); if (rs == NULL || !rs->topen || ticket != rs->tticket) @@ -1806,10 +1526,12 @@ pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd, struct pfr_ktable *p, *q; struct pfr_ktableworkq workq; struct pf_ruleset *rs; - int s, xadd = 0, xchange = 0; + int xadd = 0, xchange = 0; long tzero = time_second; - ACCEPT_FLAGS(flags, PFR_FLAG_ATOMIC | PFR_FLAG_DUMMY); + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); rs = pf_find_ruleset(trs->pfrt_anchor); if (rs == NULL || !rs->topen || ticket != rs->tticket) return (EBUSY); @@ -1827,14 +1549,10 @@ pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd, } if (!(flags & PFR_FLAG_DUMMY)) { - if (flags & PFR_FLAG_ATOMIC) - s = splsoftnet(); for (p = SLIST_FIRST(&workq); p != NULL; p = q) { q = SLIST_NEXT(p, pfrkt_workq); pfr_commit_ktable(p, tzero); } - if (flags & PFR_FLAG_ATOMIC) - splx(s); rs->topen = 0; pf_remove_if_empty_ruleset(rs); } @@ -1846,12 +1564,14 @@ pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd, return (0); } -void +static void pfr_commit_ktable(struct pfr_ktable *kt, long tzero) { struct pfr_ktable *shadow = kt->pfrkt_shadow; int nflags; + PF_RULES_WASSERT(); + if (shadow->pfrkt_cnt == NO_ADDRESSES) { if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) pfr_clstats_ktable(kt, tzero, 1); @@ -1905,7 +1625,7 @@ pfr_commit_ktable(struct pfr_ktable *kt, long tzero) pfr_setflags_ktable(kt, nflags); } -int +static int pfr_validate_table(struct pfr_table *tbl, int allowedflags, int no_reserved) { int i; @@ -1930,7 +1650,7 @@ pfr_validate_table(struct pfr_table *tbl, int allowedflags, int no_reserved) * Rewrite anchors referenced by tables to remove slashes * and check for validity. */ -int +static int pfr_fix_anchor(char *anchor) { size_t siz = MAXPATHLEN; @@ -1955,11 +1675,13 @@ pfr_fix_anchor(char *anchor) return (0); } -int +static int pfr_table_count(struct pfr_table *filter, int flags) { struct pf_ruleset *rs; + PF_RULES_ASSERT(); + if (flags & PFR_FLAG_ALLRSETS) return (pfr_ktable_cnt); if (filter->pfrt_anchor[0]) { @@ -1969,7 +1691,7 @@ pfr_table_count(struct pfr_table *filter, int flags) return (pf_main_ruleset.tables); } -int +static int pfr_skip_table(struct pfr_table *filter, struct pfr_ktable *kt, int flags) { if (flags & PFR_FLAG_ALLRSETS) @@ -1979,7 +1701,7 @@ pfr_skip_table(struct pfr_table *filter, struct pfr_ktable *kt, int flags) return (0); } -void +static void pfr_insert_ktables(struct pfr_ktableworkq *workq) { struct pfr_ktable *p; @@ -1988,9 +1710,12 @@ pfr_insert_ktables(struct pfr_ktableworkq *workq) pfr_insert_ktable(p); } -void +static void pfr_insert_ktable(struct pfr_ktable *kt) { + + PF_RULES_WASSERT(); + RB_INSERT(pfr_ktablehead, &pfr_ktables, kt); pfr_ktable_cnt++; if (kt->pfrkt_root != NULL) @@ -1999,7 +1724,7 @@ pfr_insert_ktable(struct pfr_ktable *kt) kt->pfrkt_root->pfrkt_flags|PFR_TFLAG_REFDANCHOR); } -void +static void pfr_setflags_ktables(struct pfr_ktableworkq *workq) { struct pfr_ktable *p, *q; @@ -2010,11 +1735,13 @@ pfr_setflags_ktables(struct pfr_ktableworkq *workq) } } -void +static void pfr_setflags_ktable(struct pfr_ktable *kt, int newf) { struct pfr_kentryworkq addrq; + PF_RULES_WASSERT(); + if (!(newf & PFR_TFLAG_REFERENCED) && !(newf & PFR_TFLAG_PERSIST)) newf &= ~PFR_TFLAG_ACTIVE; @@ -2042,7 +1769,7 @@ pfr_setflags_ktable(struct pfr_ktable *kt, int newf) kt->pfrkt_flags = newf; } -void +static void pfr_clstats_ktables(struct pfr_ktableworkq *workq, long tzero, int recurse) { struct pfr_ktable *p; @@ -2051,39 +1778,30 @@ pfr_clstats_ktables(struct pfr_ktableworkq *workq, long tzero, int recurse) pfr_clstats_ktable(p, tzero, recurse); } -void +static void pfr_clstats_ktable(struct pfr_ktable *kt, long tzero, int recurse) { struct pfr_kentryworkq addrq; - int s; if (recurse) { pfr_enqueue_addrs(kt, &addrq, NULL, 0); pfr_clstats_kentries(&addrq, tzero, 0); } - s = splsoftnet(); bzero(kt->pfrkt_packets, sizeof(kt->pfrkt_packets)); bzero(kt->pfrkt_bytes, sizeof(kt->pfrkt_bytes)); kt->pfrkt_match = kt->pfrkt_nomatch = 0; - splx(s); kt->pfrkt_tzero = tzero; } -struct pfr_ktable * -pfr_create_ktable(struct pfr_table *tbl, long tzero, int attachruleset, - int intr) +static struct pfr_ktable * +pfr_create_ktable(struct pfr_table *tbl, long tzero, int attachruleset) { struct pfr_ktable *kt; struct pf_ruleset *rs; -#ifdef __FreeBSD__ - kt = pool_get(&V_pfr_ktable_pl, PR_NOWAIT|PR_ZERO); -#else - if (intr) - kt = pool_get(&pfr_ktable_pl, PR_NOWAIT|PR_ZERO|PR_LIMITFAIL); - else - kt = pool_get(&pfr_ktable_pl, PR_WAITOK|PR_ZERO|PR_LIMITFAIL); -#endif + PF_RULES_WASSERT(); + + kt = malloc(sizeof(*kt), M_PFTABLE, M_NOWAIT|M_ZERO); if (kt == NULL) return (NULL); kt->pfrkt_t = *tbl; @@ -2110,7 +1828,7 @@ pfr_create_ktable(struct pfr_table *tbl, long tzero, int attachruleset, return (kt); } -void +static void pfr_destroy_ktables(struct pfr_ktableworkq *workq, int flushaddr) { struct pfr_ktable *p, *q; @@ -2121,7 +1839,7 @@ pfr_destroy_ktables(struct pfr_ktableworkq *workq, int flushaddr) } } -void +static void pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) { struct pfr_kentryworkq addrq; @@ -2131,7 +1849,6 @@ pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) pfr_clean_node_mask(kt, &addrq); pfr_destroy_kentries(&addrq); } -#if defined(__FreeBSD__) && (__FreeBSD_version >= 500100) if (kt->pfrkt_ip4 != NULL) { RADIX_NODE_HEAD_DESTROY(kt->pfrkt_ip4); free((caddr_t)kt->pfrkt_ip4, M_RTABLE); @@ -2140,26 +1857,16 @@ pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) RADIX_NODE_HEAD_DESTROY(kt->pfrkt_ip6); free((caddr_t)kt->pfrkt_ip6, M_RTABLE); } -#else - if (kt->pfrkt_ip4 != NULL) - free((caddr_t)kt->pfrkt_ip4, M_RTABLE); - if (kt->pfrkt_ip6 != NULL) - free((caddr_t)kt->pfrkt_ip6, M_RTABLE); -#endif if (kt->pfrkt_shadow != NULL) pfr_destroy_ktable(kt->pfrkt_shadow, flushaddr); if (kt->pfrkt_rs != NULL) { kt->pfrkt_rs->tables--; pf_remove_if_empty_ruleset(kt->pfrkt_rs); } -#ifdef __FreeBSD__ - pool_put(&V_pfr_ktable_pl, kt); -#else - pool_put(&pfr_ktable_pl, kt); -#endif + free(kt, M_PFTABLE); } -int +static int pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q) { int d; @@ -2169,7 +1876,7 @@ pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q) return (strcmp(p->pfrkt_anchor, q->pfrkt_anchor)); } -struct pfr_ktable * +static struct pfr_ktable * pfr_lookup_table(struct pfr_table *tbl) { /* struct pfr_ktable start like a struct pfr_table */ @@ -2183,6 +1890,8 @@ pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) struct pfr_kentry *ke = NULL; int match; + PF_RULES_RASSERT(); + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) kt = kt->pfrkt_root; if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -2191,29 +1900,33 @@ pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) switch (af) { #ifdef INET case AF_INET: -#ifdef __FreeBSD__ - V_pfr_sin.sin_addr.s_addr = a->addr32[0]; - ke = (struct pfr_kentry *)rn_match(&V_pfr_sin, kt->pfrkt_ip4); -#else - pfr_sin.sin_addr.s_addr = a->addr32[0]; - ke = (struct pfr_kentry *)rn_match(&pfr_sin, kt->pfrkt_ip4); -#endif + { + struct sockaddr_in sin; + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = a->addr32[0]; + ke = (struct pfr_kentry *)rn_match(&sin, kt->pfrkt_ip4); if (ke && KENTRY_RNF_ROOT(ke)) ke = NULL; break; + } #endif /* INET */ #ifdef INET6 case AF_INET6: -#ifdef __FreeBSD__ - bcopy(a, &V_pfr_sin6.sin6_addr, sizeof(V_pfr_sin6.sin6_addr)); - ke = (struct pfr_kentry *)rn_match(&V_pfr_sin6, kt->pfrkt_ip6); -#else - bcopy(a, &pfr_sin6.sin6_addr, sizeof(pfr_sin6.sin6_addr)); - ke = (struct pfr_kentry *)rn_match(&pfr_sin6, kt->pfrkt_ip6); -#endif + { + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + bcopy(a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + ke = (struct pfr_kentry *)rn_match(&sin6, kt->pfrkt_ip6); if (ke && KENTRY_RNF_ROOT(ke)) ke = NULL; break; + } #endif /* INET6 */ } match = (ke && !ke->pfrke_not); @@ -2238,29 +1951,31 @@ pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, switch (af) { #ifdef INET case AF_INET: -#ifdef __FreeBSD__ - V_pfr_sin.sin_addr.s_addr = a->addr32[0]; - ke = (struct pfr_kentry *)rn_match(&V_pfr_sin, kt->pfrkt_ip4); -#else - pfr_sin.sin_addr.s_addr = a->addr32[0]; - ke = (struct pfr_kentry *)rn_match(&pfr_sin, kt->pfrkt_ip4); -#endif + { + struct sockaddr_in sin; + + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = a->addr32[0]; + ke = (struct pfr_kentry *)rn_match(&sin, kt->pfrkt_ip4); if (ke && KENTRY_RNF_ROOT(ke)) ke = NULL; break; + } #endif /* INET */ #ifdef INET6 case AF_INET6: -#ifdef __FreeBSD__ - bcopy(a, &V_pfr_sin6.sin6_addr, sizeof(V_pfr_sin6.sin6_addr)); - ke = (struct pfr_kentry *)rn_match(&V_pfr_sin6, kt->pfrkt_ip6); -#else - bcopy(a, &pfr_sin6.sin6_addr, sizeof(pfr_sin6.sin6_addr)); - ke = (struct pfr_kentry *)rn_match(&pfr_sin6, kt->pfrkt_ip6); -#endif + { + struct sockaddr_in6 sin6; + + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + bcopy(a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + ke = (struct pfr_kentry *)rn_match(&sin6, kt->pfrkt_ip6); if (ke && KENTRY_RNF_ROOT(ke)) ke = NULL; break; + } #endif /* INET6 */ default: ; @@ -2275,12 +1990,8 @@ pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, if (ke != NULL && op_pass != PFR_OP_XPASS && (kt->pfrkt_flags & PFR_TFLAG_COUNTERS)) { if (ke->pfrke_counters == NULL) -#ifdef __FreeBSD__ - ke->pfrke_counters = pool_get(&V_pfr_kcounters_pl, -#else - ke->pfrke_counters = pool_get(&pfr_kcounters_pl, -#endif - PR_NOWAIT | PR_ZERO); + ke->pfrke_counters = uma_zalloc(V_pfr_kcounters_z, + M_NOWAIT | M_ZERO); if (ke->pfrke_counters != NULL) { ke->pfrke_counters->pfrkc_packets[dir_out][op_pass]++; ke->pfrke_counters->pfrkc_bytes[dir_out][op_pass] += len; @@ -2289,26 +2000,28 @@ pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, } struct pfr_ktable * -pfr_attach_table(struct pf_ruleset *rs, char *name, int intr) +pfr_attach_table(struct pf_ruleset *rs, char *name) { struct pfr_ktable *kt, *rt; struct pfr_table tbl; struct pf_anchor *ac = rs->anchor; + PF_RULES_WASSERT(); + bzero(&tbl, sizeof(tbl)); strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)); if (ac != NULL) strlcpy(tbl.pfrt_anchor, ac->path, sizeof(tbl.pfrt_anchor)); kt = pfr_lookup_table(&tbl); if (kt == NULL) { - kt = pfr_create_ktable(&tbl, time_second, 1, intr); + kt = pfr_create_ktable(&tbl, time_second, 1); if (kt == NULL) return (NULL); if (ac != NULL) { bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor)); rt = pfr_lookup_table(&tbl); if (rt == NULL) { - rt = pfr_create_ktable(&tbl, 0, 1, intr); + rt = pfr_create_ktable(&tbl, 0, 1); if (rt == NULL) { pfr_destroy_ktable(kt, 0); return (NULL); @@ -2327,38 +2040,36 @@ pfr_attach_table(struct pf_ruleset *rs, char *name, int intr) void pfr_detach_table(struct pfr_ktable *kt) { - if (kt->pfrkt_refcnt[PFR_REFCNT_RULE] <= 0) - printf("pfr_detach_table: refcount = %d.\n", - kt->pfrkt_refcnt[PFR_REFCNT_RULE]); - else if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE]) + + PF_RULES_WASSERT(); + KASSERT(kt->pfrkt_refcnt[PFR_REFCNT_RULE] > 0, ("%s: refcount %d\n", + __func__, kt->pfrkt_refcnt[PFR_REFCNT_RULE])); + + if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE]) pfr_setflags_ktable(kt, kt->pfrkt_flags&~PFR_TFLAG_REFERENCED); } int pfr_pool_get(struct pfr_ktable *kt, int *pidx, struct pf_addr *counter, - struct pf_addr **raddr, struct pf_addr **rmask, sa_family_t af) + sa_family_t af) { -#ifdef __FreeBSD__ + struct pf_addr *addr, *cur, *mask; + union sockaddr_union uaddr, umask; struct pfr_kentry *ke, *ke2 = NULL; - struct pf_addr *addr = NULL; -#else - struct pfr_kentry *ke, *ke2; - struct pf_addr *addr; -#endif - union sockaddr_union mask; int idx = -1, use_counter = 0; -#ifdef __FreeBSD__ - if (af == AF_INET) - addr = (struct pf_addr *)&V_pfr_sin.sin_addr; - else if (af == AF_INET6) - addr = (struct pf_addr *)&V_pfr_sin6.sin6_addr; -#else - if (af == AF_INET) - addr = (struct pf_addr *)&pfr_sin.sin_addr; - else if (af == AF_INET6) - addr = (struct pf_addr *)&pfr_sin6.sin6_addr; -#endif + switch (af) { + case AF_INET: + uaddr.sin.sin_len = sizeof(struct sockaddr_in); + uaddr.sin.sin_family = AF_INET; + break; + case AF_INET6: + uaddr.sin6.sin6_len = sizeof(struct sockaddr_in6); + uaddr.sin6.sin6_family = AF_INET6; + break; + } + addr = SUNION2PF(&uaddr, af); + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) kt = kt->pfrkt_root; if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -2377,21 +2088,13 @@ _next_block: kt->pfrkt_nomatch++; return (1); } -#ifdef __FreeBSD__ - pfr_prepare_network(&V_pfr_mask, af, ke->pfrke_net); -#else - pfr_prepare_network(&pfr_mask, af, ke->pfrke_net); -#endif - *raddr = SUNION2PF(&ke->pfrke_sa, af); -#ifdef __FreeBSD__ - *rmask = SUNION2PF(&V_pfr_mask, af); -#else - *rmask = SUNION2PF(&pfr_mask, af); -#endif + pfr_prepare_network(&umask, af, ke->pfrke_net); + cur = SUNION2PF(&ke->pfrke_sa, af); + mask = SUNION2PF(&umask, af); if (use_counter) { /* is supplied address within block? */ - if (!PF_MATCHA(0, *raddr, *rmask, counter, af)) { + if (!PF_MATCHA(0, cur, mask, counter, af)) { /* no, go to next block in table */ idx++; use_counter = 0; @@ -2400,7 +2103,7 @@ _next_block: PF_ACPY(addr, counter, af); } else { /* use first address of block */ - PF_ACPY(addr, *raddr, af); + PF_ACPY(addr, cur, af); } if (!KENTRY_NETWORK(ke)) { @@ -2412,21 +2115,16 @@ _next_block: } for (;;) { /* we don't want to use a nested block */ -#ifdef __FreeBSD__ - if (af == AF_INET) - ke2 = (struct pfr_kentry *)rn_match(&V_pfr_sin, + switch (af) { + case AF_INET: + ke2 = (struct pfr_kentry *)rn_match(&uaddr, kt->pfrkt_ip4); - else if (af == AF_INET6) - ke2 = (struct pfr_kentry *)rn_match(&V_pfr_sin6, - kt->pfrkt_ip6); -#else - if (af == AF_INET) - ke2 = (struct pfr_kentry *)rn_match(&pfr_sin, - kt->pfrkt_ip4); - else if (af == AF_INET6) - ke2 = (struct pfr_kentry *)rn_match(&pfr_sin6, + break; + case AF_INET6: + ke2 = (struct pfr_kentry *)rn_match(&uaddr, kt->pfrkt_ip6); -#endif + break; + } /* no need to check KENTRY_RNF_ROOT() here */ if (ke2 == ke) { /* lookup return the same block - perfect */ @@ -2437,14 +2135,10 @@ _next_block: } /* we need to increase the counter past the nested block */ - pfr_prepare_network(&mask, AF_INET, ke2->pfrke_net); -#ifdef __FreeBSD__ - PF_POOLMASK(addr, addr, SUNION2PF(&mask, af), &V_pfr_ffaddr, af); -#else - PF_POOLMASK(addr, addr, SUNION2PF(&mask, af), &pfr_ffaddr, af); -#endif + pfr_prepare_network(&umask, AF_INET, ke2->pfrke_net); + PF_POOLMASK(addr, addr, SUNION2PF(&umask, af), &pfr_ffaddr, af); PF_AINC(addr, af); - if (!PF_MATCHA(0, *raddr, *rmask, addr, af)) { + if (!PF_MATCHA(0, cur, mask, addr, af)) { /* ok, we reached the end of our main block */ /* go to next block in table */ idx++; @@ -2454,7 +2148,7 @@ _next_block: } } -struct pfr_kentry * +static struct pfr_kentry * pfr_kentry_byidx(struct pfr_ktable *kt, int idx, int af) { struct pfr_walktree w; @@ -2466,20 +2160,12 @@ pfr_kentry_byidx(struct pfr_ktable *kt, int idx, int af) switch (af) { #ifdef INET case AF_INET: -#ifdef __FreeBSD__ kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#else - rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#endif return (w.pfrw_kentry); #endif /* INET */ #ifdef INET6 case AF_INET6: -#ifdef __FreeBSD__ kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#else - rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#endif return (w.pfrw_kentry); #endif /* INET6 */ default: @@ -2491,26 +2177,15 @@ void pfr_dynaddr_update(struct pfr_ktable *kt, struct pfi_dynaddr *dyn) { struct pfr_walktree w; - int s; bzero(&w, sizeof(w)); w.pfrw_op = PFRW_DYNADDR_UPDATE; w.pfrw_dyn = dyn; - s = splsoftnet(); dyn->pfid_acnt4 = 0; dyn->pfid_acnt6 = 0; if (!dyn->pfid_af || dyn->pfid_af == AF_INET) -#ifdef __FreeBSD__ kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#else - rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); -#endif if (!dyn->pfid_af || dyn->pfid_af == AF_INET6) -#ifdef __FreeBSD__ kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#else - rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -#endif - splx(s); } diff --git a/sys/contrib/pf/net/pfvar.h b/sys/contrib/pf/net/pfvar.h index dab70c597a60a..ca4e449f9d362 100644 --- a/sys/contrib/pf/net/pfvar.h +++ b/sys/contrib/pf/net/pfvar.h @@ -34,37 +34,14 @@ #define _NET_PFVAR_H_ #include -#include #include +#include #include -#ifdef __FreeBSD__ -#include -#include -#else -#include -#endif #include -#include -#ifdef __FreeBSD__ -#include -#include -#include -#else -#include -#endif - -#ifdef __FreeBSD__ #include -#endif - -#include -struct ip; -struct ip6_hdr; -#ifdef __FreeBSD__ -struct inpcb; -#endif +#include #define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) #define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) @@ -126,12 +103,12 @@ enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLES, PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; + PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; #define PF_POOL_IDMASK 0x0f enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, - PF_ADDR_TABLE, PF_ADDR_RTLABEL, PF_ADDR_URPFFAILED, + PF_ADDR_TABLE, PF_ADDR_URPFFAILED, PF_ADDR_RANGE }; #define PF_POOL_TYPEMASK 0x0f #define PF_POOL_STICKYADDR 0x20 @@ -173,11 +150,6 @@ struct pf_addr_wrap { } a; char ifname[IFNAMSIZ]; char tblname[PF_TABLE_NAME_SIZE]; -#ifdef __FreeBSD__ -#define RTLABEL_LEN 32 -#endif - char rtlabelname[RTLABEL_LEN]; - u_int32_t rtlabel; } v; union { struct pfi_dynaddr *dyn; @@ -199,7 +171,6 @@ struct pfi_dynaddr { struct pf_addr pfid_mask6; struct pfr_ktable *pfid_kt; struct pfi_kif *pfid_kif; - void *pfid_hook_cookie; int pfid_net; /* mask or 128 */ int pfid_acnt4; /* address count IPv4 */ int pfid_acnt6; /* address count IPv6 */ @@ -210,10 +181,6 @@ struct pfi_dynaddr { /* * Address manipulation macros */ - -#ifdef __FreeBSD__ -#define splsoftnet() splnet() - #define HTONL(x) (x) = htonl((__uint32_t)(x)) #define HTONS(x) (x) = htons((__uint16_t)(x)) #define NTOHL(x) (x) = ntohl((__uint32_t)(x)) @@ -221,46 +188,44 @@ struct pfi_dynaddr { #define PF_NAME "pf" -#define PR_NOWAIT M_NOWAIT -#define PR_WAITOK M_WAITOK -#define PR_ZERO M_ZERO -#define pool_get(p, f) uma_zalloc(*(p), (f)) -#define pool_put(p, o) uma_zfree(*(p), (o)) - -#define UMA_CREATE(var, type, desc) \ - var = uma_zcreate(desc, sizeof(type), \ - NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); \ - if (var == NULL) \ - break -#define UMA_DESTROY(var) \ - if (var) \ - uma_zdestroy(var) - -#ifdef __FreeBSD__ -extern struct mtx pf_task_mtx; - -#define PF_LOCK_ASSERT() mtx_assert(&pf_task_mtx, MA_OWNED) -#define PF_UNLOCK_ASSERT() mtx_assert(&pf_task_mtx, MA_NOTOWNED) -#define PF_LOCK() mtx_lock(&pf_task_mtx) -#define PF_UNLOCK() mtx_unlock(&pf_task_mtx) -#else -#define PF_LOCK_ASSERT() -#define PF_UNLOCK_ASSERT() -#define PF_LOCK() -#define PF_UNLOCK() -#endif /* __FreeBSD__ */ - -#define PF_COPYIN(uaddr, kaddr, len, r) do { \ - PF_UNLOCK(); \ - r = copyin((uaddr), (kaddr), (len)); \ - PF_LOCK(); \ -} while(0) +#define PF_HASHROW_ASSERT(h) mtx_assert(&(h)->lock, MA_OWNED) +#define PF_HASHROW_LOCK(h) mtx_lock(&(h)->lock) +#define PF_HASHROW_UNLOCK(h) mtx_unlock(&(h)->lock) -#define PF_COPYOUT(kaddr, uaddr, len, r) do { \ - PF_UNLOCK(); \ - r = copyout((kaddr), (uaddr), (len)); \ - PF_LOCK(); \ -} while(0) +#define PF_STATE_LOCK(s) \ + do { \ + struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH(s)]; \ + PF_HASHROW_LOCK(_ih); \ + } while (0) + +#define PF_STATE_UNLOCK(s) \ + do { \ + struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH((s))]; \ + PF_HASHROW_UNLOCK(_ih); \ + } while (0) + +#ifdef INVARIANTS +#define PF_STATE_LOCK_ASSERT(s) \ + do { \ + struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH(s)]; \ + PF_HASHROW_ASSERT(_ih); \ + } while (0) +#else /* !INVARIANTS */ +#define PF_STATE_LOCK_ASSERT(s) do {} while (0) +#endif /* INVARIANTS */ + +extern struct mtx pf_unlnkdrules_mtx; +#define PF_UNLNKDRULES_LOCK() mtx_lock(&pf_unlnkdrules_mtx) +#define PF_UNLNKDRULES_UNLOCK() mtx_unlock(&pf_unlnkdrules_mtx) + +extern struct rwlock pf_rules_lock; +#define PF_RULES_RLOCK() rw_rlock(&pf_rules_lock) +#define PF_RULES_RUNLOCK() rw_runlock(&pf_rules_lock) +#define PF_RULES_WLOCK() rw_wlock(&pf_rules_lock) +#define PF_RULES_WUNLOCK() rw_wunlock(&pf_rules_lock) +#define PF_RULES_ASSERT() rw_assert(&pf_rules_lock, RA_LOCKED) +#define PF_RULES_RASSERT() rw_assert(&pf_rules_lock, RA_RLOCKED) +#define PF_RULES_WASSERT() rw_assert(&pf_rules_lock, RA_WLOCKED) #define PF_MODVER 1 #define PFLOG_MODVER 1 @@ -272,7 +237,7 @@ extern struct mtx pf_task_mtx; #define PFSYNC_MINVER 1 #define PFSYNC_PREFVER PFSYNC_MODVER #define PFSYNC_MAXVER 1 -#endif /* __FreeBSD__ */ + #ifdef INET #ifndef INET6 #define PF_INET_ONLY @@ -412,8 +377,6 @@ extern struct mtx pf_task_mtx; pf_routable((x), (af), NULL, (rtid))) || \ (((aw)->type == PF_ADDR_URPFFAILED && (ifp) != NULL && \ pf_routable((x), (af), (ifp), (rtid))) || \ - ((aw)->type == PF_ADDR_RTLABEL && \ - !pf_rtlabel_match((x), (af), (aw), (rtid))) || \ ((aw)->type == PF_ADDR_TABLE && \ !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ ((aw)->type == PF_ADDR_DYNIFTL && \ @@ -473,7 +436,6 @@ struct pf_pool { struct pf_addr counter; int tblidx; u_int16_t proxy_port[2]; - u_int8_t port_op; u_int8_t opts; }; @@ -705,6 +667,7 @@ struct pf_rule { #define PFRULE_NOSYNC 0x0010 #define PFRULE_SRCTRACK 0x0020 /* track source states */ #define PFRULE_RULESRCTRACK 0x0040 /* per rule */ +#define PFRULE_REFS 0x0080 /* rule has references */ /* scrub flags */ #define PFRULE_NODF 0x0100 @@ -717,7 +680,6 @@ struct pf_rule { /* rule flags again */ #define PFRULE_IFBOUND 0x00010000 /* if-bound */ #define PFRULE_STATESLOPPY 0x00020000 /* sloppy state tracking */ -#define PFRULE_PFLOW 0x00040000 #define PFSTATE_HIWAT 10000 /* default state table size */ #define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */ @@ -734,7 +696,7 @@ struct pf_threshold { }; struct pf_src_node { - RB_ENTRY(pf_src_node) entry; + LIST_ENTRY(pf_src_node) entry; struct pf_addr addr; struct pf_addr raddr; union pf_rule_ptr rule; @@ -787,9 +749,7 @@ struct pf_state_peer { u_int8_t pad[1]; }; -TAILQ_HEAD(pf_state_queue, pf_state); - -/* keep synced with struct pf_state_key, used in RB_FIND */ +/* Keep synced with struct pf_state_key. */ struct pf_state_key_cmp { struct pf_addr addr[2]; u_int16_t port[2]; @@ -798,13 +758,6 @@ struct pf_state_key_cmp { u_int8_t pad[2]; }; -struct pf_state_item { - TAILQ_ENTRY(pf_state_item) entry; - struct pf_state *s; -}; - -TAILQ_HEAD(pf_statelisthead, pf_state_item); - struct pf_state_key { struct pf_addr addr[2]; u_int16_t port[2]; @@ -812,13 +765,11 @@ struct pf_state_key { u_int8_t proto; u_int8_t pad[2]; - RB_ENTRY(pf_state_key) entry; - struct pf_statelisthead states; - struct pf_state_key *reverse; - struct inpcb *inp; + LIST_ENTRY(pf_state_key) entry; + TAILQ_HEAD(, pf_state) states[2]; }; -/* keep synced with struct pf_state, used in RB_FIND */ +/* Keep synced with struct pf_state. */ struct pf_state_cmp { u_int64_t id; u_int32_t creatorid; @@ -830,17 +781,12 @@ struct pf_state { u_int64_t id; u_int32_t creatorid; u_int8_t direction; -#ifdef __FreeBSD__ - u_int8_t pad[2]; - u_int8_t local_flags; -#define PFSTATE_EXPIRING 0x01 -#else u_int8_t pad[3]; -#endif + u_int refs; TAILQ_ENTRY(pf_state) sync_list; - TAILQ_ENTRY(pf_state) entry_list; - RB_ENTRY(pf_state) entry_id; + TAILQ_ENTRY(pf_state) key_list[2]; + LIST_ENTRY(pf_state) entry; struct pf_state_peer src; struct pf_state_peer dst; union pf_rule_ptr rule; @@ -862,7 +808,7 @@ struct pf_state { u_int8_t state_flags; #define PFSTATE_ALLOWOPTS 0x01 #define PFSTATE_SLOPPY 0x02 -#define PFSTATE_PFLOW 0x04 +/* was PFSTATE_PFLOW 0x04 */ #define PFSTATE_NOSYNC 0x08 #define PFSTATE_ACK 0x10 u_int8_t timeout; @@ -903,7 +849,7 @@ struct pfsync_state_key { }; struct pfsync_state { - u_int32_t id[2]; + u_int64_t id; char ifname[IFNAMSIZ]; struct pfsync_state_key key[2]; struct pfsync_state_peer src; @@ -920,11 +866,7 @@ struct pfsync_state { sa_family_t af; u_int8_t proto; u_int8_t direction; -#ifdef __FreeBSD__ - u_int8_t local_flags; -#define PFSTATE_EXPIRING 0x01 - u_int8_t pad; -#endif + u_int8_t __spare[2]; u_int8_t log; u_int8_t state_flags; u_int8_t timeout; @@ -932,7 +874,6 @@ struct pfsync_state { u_int8_t updates; } __packed; -#ifdef __FreeBSD__ #ifdef _KERNEL /* pfsync */ typedef int pfsync_state_import_t(struct pfsync_state *, u_int8_t); @@ -940,50 +881,28 @@ typedef void pfsync_insert_state_t(struct pf_state *); typedef void pfsync_update_state_t(struct pf_state *); typedef void pfsync_delete_state_t(struct pf_state *); typedef void pfsync_clear_states_t(u_int32_t, const char *); -typedef int pfsync_state_in_use_t(struct pf_state *); typedef int pfsync_defer_t(struct pf_state *, struct mbuf *); -typedef int pfsync_up_t(void); extern pfsync_state_import_t *pfsync_state_import_ptr; extern pfsync_insert_state_t *pfsync_insert_state_ptr; extern pfsync_update_state_t *pfsync_update_state_ptr; extern pfsync_delete_state_t *pfsync_delete_state_ptr; extern pfsync_clear_states_t *pfsync_clear_states_ptr; -extern pfsync_state_in_use_t *pfsync_state_in_use_ptr; extern pfsync_defer_t *pfsync_defer_ptr; -extern pfsync_up_t *pfsync_up_ptr; void pfsync_state_export(struct pfsync_state *, struct pf_state *); -/* pflow */ -typedef int export_pflow_t(struct pf_state *); - -extern export_pflow_t *export_pflow_ptr; - /* pflog */ struct pf_ruleset; struct pf_pdesc; typedef int pflog_packet_t(struct pfi_kif *, struct mbuf *, sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, - struct pf_ruleset *, struct pf_pdesc *); - + struct pf_ruleset *, struct pf_pdesc *, int); extern pflog_packet_t *pflog_packet_ptr; -/* pf uid hack */ -VNET_DECLARE(int, debug_pfugidhack); -#define V_debug_pfugidhack VNET(debug_pfugidhack) - #define V_pf_end_threads VNET(pf_end_threads) -#endif - -/* Macros to set/clear/test flags. */ -#ifdef _KERNEL -#define SET(t, f) ((t) |= (f)) -#define CLR(t, f) ((t) &= ~(f)) -#define ISSET(t, f) ((t) & (f)) -#endif -#endif +#endif /* _KERNEL */ #define PFSYNC_FLAG_SRCNODE 0x04 #define PFSYNC_FLAG_NATSRCNODE 0x08 @@ -1085,9 +1004,20 @@ RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); #define PFR_TFLAG_REFDANCHOR 0x00000020 #define PFR_TFLAG_COUNTERS 0x00000040 /* Adjust masks below when adding flags. */ -#define PFR_TFLAG_USRMASK 0x00000043 -#define PFR_TFLAG_SETMASK 0x0000003C -#define PFR_TFLAG_ALLMASK 0x0000007F +#define PFR_TFLAG_USRMASK (PFR_TFLAG_PERSIST | \ + PFR_TFLAG_CONST | \ + PFR_TFLAG_COUNTERS) +#define PFR_TFLAG_SETMASK (PFR_TFLAG_ACTIVE | \ + PFR_TFLAG_INACTIVE | \ + PFR_TFLAG_REFERENCED | \ + PFR_TFLAG_REFDANCHOR) +#define PFR_TFLAG_ALLMASK (PFR_TFLAG_PERSIST | \ + PFR_TFLAG_CONST | \ + PFR_TFLAG_ACTIVE | \ + PFR_TFLAG_INACTIVE | \ + PFR_TFLAG_REFERENCED | \ + PFR_TFLAG_REFDANCHOR | \ + PFR_TFLAG_COUNTERS) struct pfr_table { char pfrt_anchor[MAXPATHLEN]; @@ -1158,22 +1088,13 @@ struct pfr_kentry { struct radix_node pfrke_node[2]; union sockaddr_union pfrke_sa; SLIST_ENTRY(pfr_kentry) pfrke_workq; - union { - - struct pfr_kcounters *pfrke_counters; -#if 0 - struct pfr_kroute *pfrke_route; -#endif - } u; + struct pfr_kcounters *pfrke_counters; long pfrke_tzero; u_int8_t pfrke_af; u_int8_t pfrke_net; u_int8_t pfrke_not; u_int8_t pfrke_mark; }; -#define pfrke_counters u.pfrke_counters -#define pfrke_route u.pfrke_route - SLIST_HEAD(pfr_ktableworkq, pfr_ktable); RB_HEAD(pfr_ktablehead, pfr_ktable); @@ -1202,25 +1123,6 @@ struct pfr_ktable { #define pfrkt_nomatch pfrkt_ts.pfrts_nomatch #define pfrkt_tzero pfrkt_ts.pfrts_tzero -RB_HEAD(pf_state_tree, pf_state_key); -RB_PROTOTYPE(pf_state_tree, pf_state_key, entry, pf_state_compare_key); - -RB_HEAD(pf_state_tree_ext_gwy, pf_state_key); -RB_PROTOTYPE(pf_state_tree_ext_gwy, pf_state_key, - entry_ext_gwy, pf_state_compare_ext_gwy); - -RB_HEAD(pfi_ifhead, pfi_kif); - -/* state tables */ -#ifdef __FreeBSD__ -#ifdef _KERNEL -VNET_DECLARE(struct pf_state_tree, pf_statetbl); -#define V_pf_statetbl VNET(pf_statetbl) -#endif -#else -extern struct pf_state_tree pf_statetbl; -#endif - /* keep synced with pfi_kif, used in RB_FIND */ struct pfi_kif_cmp { char pfik_name[IFNAMSIZ]; @@ -1228,25 +1130,23 @@ struct pfi_kif_cmp { struct pfi_kif { char pfik_name[IFNAMSIZ]; - RB_ENTRY(pfi_kif) pfik_tree; + union { + RB_ENTRY(pfi_kif) _pfik_tree; + LIST_ENTRY(pfi_kif) _pfik_list; + } _pfik_glue; +#define pfik_tree _pfik_glue._pfik_tree +#define pfik_list _pfik_glue._pfik_list u_int64_t pfik_packets[2][2][2]; u_int64_t pfik_bytes[2][2][2]; u_int32_t pfik_tzero; - int pfik_flags; - void *pfik_ah_cookie; + u_int pfik_flags; struct ifnet *pfik_ifp; struct ifg_group *pfik_group; - int pfik_states; - int pfik_rules; + u_int pfik_rulerefs; TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; }; -enum pfi_kif_refs { - PFI_KIF_REF_NONE, - PFI_KIF_REF_STATE, - PFI_KIF_REF_RULE -}; - +#define PFI_IFLAG_REFS 0x0001 /* has state references */ #define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ struct pf_pdesc { @@ -1254,7 +1154,6 @@ struct pf_pdesc { int done; uid_t uid; gid_t gid; - pid_t pid; } lookup; u_int64_t tot_len; /* Make Mickey money */ union { @@ -1268,15 +1167,11 @@ struct pf_pdesc { } hdr; struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ - struct ether_header - *eh; struct pf_addr *src; /* src address */ struct pf_addr *dst; /* dst address */ u_int16_t *sport; u_int16_t *dport; -#ifdef __FreeBSD__ struct pf_mtag *pf_mtag; -#endif u_int32_t p_len; /* total length of payload */ @@ -1400,7 +1295,6 @@ struct pf_pdesc { *(a) = (x); \ } while (0) -#ifdef __FreeBSD__ #define REASON_SET(a, x) \ do { \ if ((a) != NULL) \ @@ -1408,15 +1302,6 @@ struct pf_pdesc { if (x < PFRES_MAX) \ V_pf_status.counters[x]++; \ } while (0) -#else -#define REASON_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - if (x < PFRES_MAX) \ - pf_status.counters[x]++; \ - } while (0) -#endif struct pf_status { u_int64_t counters[PFRES_MAX]; @@ -1425,7 +1310,6 @@ struct pf_status { u_int64_t scounters[SCNT_MAX]; u_int64_t pcounters[2][2][3]; u_int64_t bcounters[2][2]; - u_int64_t stateid; u_int32_t running; u_int32_t states; u_int32_t src_nodes; @@ -1485,10 +1369,9 @@ struct pf_altq { u_int32_t parent_qid; /* parent queue id */ u_int32_t bandwidth; /* queue bandwidth */ u_int8_t priority; /* priority */ -#ifdef __FreeBSD__ u_int8_t local_flags; /* dynamic interface */ #define PFALTQ_FLAG_IF_REMOVED 0x01 -#endif + u_int16_t qlimit; /* queue size limit */ u_int16_t flags; /* misc flags */ union { @@ -1500,13 +1383,6 @@ struct pf_altq { u_int32_t qid; /* return value */ }; -struct pf_tagname { - TAILQ_ENTRY(pf_tagname) entries; - char name[PF_TAG_NAME_SIZE]; - u_int16_t tag; - int ref; -}; - struct pf_divert { union { struct in_addr ipv4; @@ -1516,13 +1392,7 @@ struct pf_divert { }; #define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ -#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */ -#define PFFRAG_FRCENT_HIWAT 50000 /* Number of fragment cache entries */ -#define PFFRAG_FRCACHE_HIWAT 10000 /* Number of fragment descriptors */ - -#define PFR_KTABLE_HIWAT 1000 /* Number of tables */ #define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ -#define PFR_KENTRY_HIWAT_SMALL 100000 /* Number of table entries (tiny hosts) */ /* * ioctl parameter structures @@ -1653,7 +1523,7 @@ struct pfioc_trans { } *array; }; -#define PFR_FLAG_ATOMIC 0x00000001 +#define PFR_FLAG_ATOMIC 0x00000001 /* unused */ #define PFR_FLAG_DUMMY 0x00000002 #define PFR_FLAG_FEEDBACK 0x00000004 #define PFR_FLAG_CLSTATS 0x00000008 @@ -1765,56 +1635,54 @@ struct pfioc_iface { #define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) #define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) #define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) -#ifdef __FreeBSD__ struct pf_ifspeed { char ifname[IFNAMSIZ]; u_int32_t baudrate; }; #define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) -#endif #ifdef _KERNEL -RB_HEAD(pf_src_tree, pf_src_node); -RB_PROTOTYPE(pf_src_tree, pf_src_node, entry, pf_src_compare); -#ifdef __FreeBSD__ -VNET_DECLARE(struct pf_src_tree, tree_src_tracking); -#define V_tree_src_tracking VNET(tree_src_tracking) -#else -extern struct pf_src_tree tree_src_tracking; -#endif +struct pf_srchash { + LIST_HEAD(, pf_src_node) nodes; + struct mtx lock; +}; -RB_HEAD(pf_state_tree_id, pf_state); -RB_PROTOTYPE(pf_state_tree_id, pf_state, - entry_id, pf_state_compare_id); -#ifdef __FreeBSD__ -VNET_DECLARE(struct pf_state_tree_id, tree_id); -#define V_tree_id VNET(tree_id) -VNET_DECLARE(struct pf_state_queue, state_list); -#define V_state_list VNET(state_list) -#else -extern struct pf_state_tree_id tree_id; -extern struct pf_state_queue state_list; -#endif +struct pf_keyhash { + LIST_HEAD(, pf_state_key) keys; + struct mtx lock; +}; + +struct pf_idhash { + LIST_HEAD(, pf_state) states; + struct mtx lock; +}; + +#define PF_HASHSIZ (32768) +VNET_DECLARE(struct pf_keyhash *, pf_keyhash); +VNET_DECLARE(struct pf_idhash *, pf_idhash); +VNET_DECLARE(u_long, pf_hashmask); +#define V_pf_keyhash VNET(pf_keyhash) +#define V_pf_idhash VNET(pf_idhash) +#define V_pf_hashmask VNET(pf_hashmask) +VNET_DECLARE(struct pf_srchash *, pf_srchash); +VNET_DECLARE(u_long, pf_srchashmask); +#define V_pf_srchash VNET(pf_srchash) +#define V_pf_srchashmask VNET(pf_srchashmask) + +#define PF_IDHASH(s) (be64toh((s)->id) % (V_pf_hashmask + 1)) + +VNET_DECLARE(void *, pf_swi_cookie); +#define V_pf_swi_cookie VNET(pf_swi_cookie) + +VNET_DECLARE(uint64_t, pf_stateid[MAXCPU]); +#define V_pf_stateid VNET(pf_stateid) -TAILQ_HEAD(pf_poolqueue, pf_pool); -#ifdef __FreeBSD__ -VNET_DECLARE(struct pf_poolqueue, pf_pools[2]); -#define V_pf_pools VNET(pf_pools) -#else -extern struct pf_poolqueue pf_pools[2]; -#endif TAILQ_HEAD(pf_altqqueue, pf_altq); -#ifdef __FreeBSD__ VNET_DECLARE(struct pf_altqqueue, pf_altqs[2]); #define V_pf_altqs VNET(pf_altqs) VNET_DECLARE(struct pf_palist, pf_pabuf); #define V_pf_pabuf VNET(pf_pabuf) -#else -extern struct pf_altqqueue pf_altqs[2]; -extern struct pf_palist pf_pabuf; -#endif -#ifdef __FreeBSD__ VNET_DECLARE(u_int32_t, ticket_altqs_active); #define V_ticket_altqs_active VNET(ticket_altqs_active) VNET_DECLARE(u_int32_t, ticket_altqs_inactive); @@ -1827,147 +1695,120 @@ VNET_DECLARE(struct pf_altqqueue *, pf_altqs_active); #define V_pf_altqs_active VNET(pf_altqs_active) VNET_DECLARE(struct pf_altqqueue *, pf_altqs_inactive); #define V_pf_altqs_inactive VNET(pf_altqs_inactive) -VNET_DECLARE(struct pf_poolqueue *, pf_pools_active); -#define V_pf_pools_active VNET(pf_pools_active) -VNET_DECLARE(struct pf_poolqueue *, pf_pools_inactive); -#define V_pf_pools_inactive VNET(pf_pools_inactive) -#else -extern u_int32_t ticket_altqs_active; -extern u_int32_t ticket_altqs_inactive; -extern int altqs_inactive_open; -extern u_int32_t ticket_pabuf; -extern struct pf_altqqueue *pf_altqs_active; -extern struct pf_altqqueue *pf_altqs_inactive; -extern struct pf_poolqueue *pf_pools_active; -extern struct pf_poolqueue *pf_pools_inactive; -#endif -extern int pf_tbladdr_setup(struct pf_ruleset *, - struct pf_addr_wrap *); -extern void pf_tbladdr_remove(struct pf_addr_wrap *); -extern void pf_tbladdr_copyout(struct pf_addr_wrap *); + +VNET_DECLARE(struct pf_rulequeue, pf_unlinked_rules); +#define V_pf_unlinked_rules VNET(pf_unlinked_rules) + +void pf_initialize(void); +void pf_cleanup(void); + +struct pf_mtag *pf_get_mtag(struct mbuf *); + extern void pf_calc_skip_steps(struct pf_rulequeue *); -#ifdef __FreeBSD__ #ifdef ALTQ extern void pf_altq_ifnet_event(struct ifnet *, int); #endif -VNET_DECLARE(uma_zone_t, pf_src_tree_pl); -#define V_pf_src_tree_pl VNET(pf_src_tree_pl) -VNET_DECLARE(uma_zone_t, pf_rule_pl); -#define V_pf_rule_pl VNET(pf_rule_pl) -VNET_DECLARE(uma_zone_t, pf_state_pl); -#define V_pf_state_pl VNET(pf_state_pl) -VNET_DECLARE(uma_zone_t, pf_state_key_pl); -#define V_pf_state_key_pl VNET(pf_state_key_pl) -VNET_DECLARE(uma_zone_t, pf_state_item_pl); -#define V_pf_state_item_pl VNET(pf_state_item_pl) -VNET_DECLARE(uma_zone_t, pf_altq_pl); -#define V_pf_altq_pl VNET(pf_altq_pl) -VNET_DECLARE(uma_zone_t, pf_pooladdr_pl); -#define V_pf_pooladdr_pl VNET(pf_pooladdr_pl) -VNET_DECLARE(uma_zone_t, pfr_ktable_pl); -#define V_pfr_ktable_pl VNET(pfr_ktable_pl) -VNET_DECLARE(uma_zone_t, pfr_kentry_pl); -#define V_pfr_kentry_pl VNET(pfr_kentry_pl) -VNET_DECLARE(uma_zone_t, pfr_kcounters_pl); -#define V_pfr_kcounters_pl VNET(pfr_kcounters_pl) -VNET_DECLARE(uma_zone_t, pf_cache_pl); -#define V_pf_cache_pl VNET(pf_cache_pl) -VNET_DECLARE(uma_zone_t, pf_cent_pl); -#define V_pf_cent_pl VNET(pf_cent_pl) -VNET_DECLARE(uma_zone_t, pf_state_scrub_pl); -#define V_pf_state_scrub_pl VNET(pf_state_scrub_pl) -VNET_DECLARE(uma_zone_t, pfi_addr_pl); -#define V_pfi_addr_pl VNET(pfi_addr_pl) -#else -extern struct pool pf_src_tree_pl, pf_rule_pl; -extern struct pool pf_state_pl, pf_state_key_pl, pf_state_item_pl, - pf_altq_pl, pf_pooladdr_pl; -extern struct pool pf_state_scrub_pl; -#endif +VNET_DECLARE(uma_zone_t, pf_state_z); +#define V_pf_state_z VNET(pf_state_z) +VNET_DECLARE(uma_zone_t, pf_state_key_z); +#define V_pf_state_key_z VNET(pf_state_key_z) +VNET_DECLARE(uma_zone_t, pf_state_scrub_z); +#define V_pf_state_scrub_z VNET(pf_state_scrub_z) + extern void pf_purge_thread(void *); -#ifdef __FreeBSD__ -extern int pf_purge_expired_src_nodes(int); -extern int pf_purge_expired_states(u_int32_t , int); -#else -extern void pf_purge_expired_src_nodes(int); -extern void pf_purge_expired_states(u_int32_t); -#endif -extern void pf_unlink_state(struct pf_state *); -extern void pf_free_state(struct pf_state *); +extern void pf_intr(void *); +extern void pf_purge_expired_src_nodes(void); + +extern int pf_unlink_state(struct pf_state *, u_int); +#define PF_ENTER_LOCKED 0x00000001 +#define PF_RETURN_LOCKED 0x00000002 extern int pf_state_insert(struct pfi_kif *, struct pf_state_key *, struct pf_state_key *, struct pf_state *); -extern int pf_insert_src_node(struct pf_src_node **, - struct pf_rule *, struct pf_addr *, - sa_family_t); -void pf_src_tree_remove_state(struct pf_state *); -extern struct pf_state *pf_find_state_byid(struct pf_state_cmp *); +extern void pf_free_state(struct pf_state *); + +static __inline u_int +pf_hashsrc(struct pf_addr *addr, sa_family_t af) +{ + u_int h; + +#define ADDR_HASH(a) ((a) ^ ((a) >> 16)) + + switch (af) { + case AF_INET: + h = ADDR_HASH(addr->v4.s_addr); + break; + case AF_INET6: + h = ADDR_HASH(addr->v6.__u6_addr.__u6_addr32[3]); + default: + panic("%s: unknown address family %u", __func__, af); + } +#undef ADDR_HASH + + return (h & V_pf_srchashmask); +} + +static __inline void +pf_ref_state(struct pf_state *s) +{ + + refcount_acquire(&s->refs); +} + +static __inline int +pf_release_state(struct pf_state *s) +{ + + if (refcount_release(&s->refs)) { + pf_free_state(s); + return (1); + } else + return (0); +} + +extern struct pf_state *pf_find_state_byid(uint64_t, uint32_t); extern struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, u_int, int *); +struct pf_src_node *pf_find_src_node(struct pf_addr *, struct pf_rule *, + sa_family_t, int); extern void pf_print_state(struct pf_state *); extern void pf_print_flags(u_int8_t); extern u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, u_int8_t); -#ifdef __FreeBSD__ VNET_DECLARE(struct ifnet *, sync_ifp); #define V_sync_ifp VNET(sync_ifp); VNET_DECLARE(struct pf_rule, pf_default_rule); #define V_pf_default_rule VNET(pf_default_rule) -#else -extern struct ifnet *sync_ifp; -extern struct pf_rule pf_default_rule; -#endif extern void pf_addrcpy(struct pf_addr *, struct pf_addr *, u_int8_t); -void pf_rm_rule(struct pf_rulequeue *, - struct pf_rule *); -#ifndef __FreeBSD__ -struct pf_divert *pf_find_divert(struct mbuf *); -#endif +void pf_free_rule(struct pf_rule *); #ifdef INET -#ifdef __FreeBSD__ -int pf_test(int, struct ifnet *, struct mbuf **, struct ether_header *, - struct inpcb *); -#else -int pf_test(int, struct ifnet *, struct mbuf **, struct ether_header *); -#endif +int pf_test(int, struct ifnet *, struct mbuf **, struct inpcb *); #endif /* INET */ #ifdef INET6 -#ifdef __FreeBSD__ -int pf_test6(int, struct ifnet *, struct mbuf **, struct ether_header *, - struct inpcb *); -#else -int pf_test6(int, struct ifnet *, struct mbuf **, struct ether_header *); -#endif +int pf_test6(int, struct ifnet *, struct mbuf **, struct inpcb *); void pf_poolmask(struct pf_addr *, struct pf_addr*, struct pf_addr *, struct pf_addr *, u_int8_t); void pf_addr_inc(struct pf_addr *, sa_family_t); #endif /* INET6 */ -#ifdef __FreeBSD__ u_int32_t pf_new_isn(struct pf_state *); -#endif void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, sa_family_t); void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); -int pflog_packet(struct pfi_kif *, struct mbuf *, sa_family_t, u_int8_t, - u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *, - struct pf_pdesc *); void pf_send_deferred_syn(struct pf_state *); int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, struct pf_addr *, sa_family_t); int pf_match_addr_range(struct pf_addr *, struct pf_addr *, struct pf_addr *, sa_family_t); -int pf_match(u_int8_t, u_int32_t, u_int32_t, u_int32_t); int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); -int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); -int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); void pf_normalize_init(void); +void pf_normalize_cleanup(void); int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, u_short *, struct pf_pdesc *); int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, u_short *, @@ -1985,25 +1826,17 @@ u_int32_t void pf_purge_expired_fragments(void); int pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *, int); -int pf_rtlabel_match(struct pf_addr *, sa_family_t, struct pf_addr_wrap *, - int); -#ifdef __FreeBSD__ -int pf_socket_lookup(int, struct pf_pdesc *, struct inpcb *); -#else -int pf_socket_lookup(int, struct pf_pdesc *); -#endif +int pf_socket_lookup(int, struct pf_pdesc *, struct mbuf *); struct pf_state_key *pf_alloc_state_key(int); -void pf_pkt_addr_changed(struct mbuf *); -int pf_state_key_attach(struct pf_state_key *, struct pf_state *, int); void pfr_initialize(void); +void pfr_cleanup(void); int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t, u_int64_t, int, int, int); -int pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, - struct pf_addr **, struct pf_addr **, sa_family_t); +int pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, sa_family_t); void pfr_dynaddr_update(struct pfr_ktable *, struct pfi_dynaddr *); struct pfr_ktable * - pfr_attach_table(struct pf_ruleset *, char *, int); + pfr_attach_table(struct pf_ruleset *, char *); void pfr_detach_table(struct pfr_ktable *); int pfr_clr_tables(struct pfr_table *, int *, int); int pfr_add_tables(struct pfr_table *, int, int *, int); @@ -2032,120 +1865,44 @@ int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, int *, int); int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, int *, u_int32_t, int); -#ifdef __FreeBSD__ +MALLOC_DECLARE(PFI_MTYPE); VNET_DECLARE(struct pfi_kif *, pfi_all); #define V_pfi_all VNET(pfi_all) -#else -extern struct pfi_kif *pfi_all; -#endif void pfi_initialize(void); -#ifdef __FreeBSD__ void pfi_cleanup(void); -#endif -struct pfi_kif *pfi_kif_get(const char *); -void pfi_kif_ref(struct pfi_kif *, enum pfi_kif_refs); -void pfi_kif_unref(struct pfi_kif *, enum pfi_kif_refs); +void pfi_kif_ref(struct pfi_kif *); +void pfi_kif_unref(struct pfi_kif *); +struct pfi_kif *pfi_kif_find(const char *); +struct pfi_kif *pfi_kif_attach(struct pfi_kif *, const char *); int pfi_kif_match(struct pfi_kif *, struct pfi_kif *); -void pfi_attach_ifnet(struct ifnet *); -void pfi_detach_ifnet(struct ifnet *); -void pfi_attach_ifgroup(struct ifg_group *); -void pfi_detach_ifgroup(struct ifg_group *); -void pfi_group_change(const char *); +void pfi_kif_purge(void); int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, sa_family_t); int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); -void pfi_dynaddr_remove(struct pf_addr_wrap *); +void pfi_dynaddr_remove(struct pfi_dynaddr *); void pfi_dynaddr_copyout(struct pf_addr_wrap *); void pfi_update_status(const char *, struct pf_status *); -int pfi_get_ifaces(const char *, struct pfi_kif *, int *); +void pfi_get_ifaces(const char *, struct pfi_kif *, int *); int pfi_set_flags(const char *, int); int pfi_clear_flags(const char *, int); -#ifdef __FreeBSD__ -int pf_match_tag(struct mbuf *, struct pf_rule *, int *, - struct pf_mtag *); -#else -int pf_match_tag(struct mbuf *, struct pf_rule *, int *); -#endif -u_int16_t pf_tagname2tag(char *); -void pf_tag2tagname(u_int16_t, char *); -void pf_tag_ref(u_int16_t); -void pf_tag_unref(u_int16_t); -#ifdef __FreeBSD__ -int pf_tag_packet(struct mbuf *, int, int, struct pf_mtag *); -#else -int pf_tag_packet(struct mbuf *, int, int); -#endif -u_int32_t pf_qname2qid(char *); +int pf_match_tag(struct mbuf *, struct pf_rule *, int *, int); +int pf_tag_packet(struct mbuf *, struct pf_pdesc *, int); void pf_qid2qname(u_int32_t, char *); -void pf_qid_unref(u_int32_t); -#ifdef __FreeBSD__ VNET_DECLARE(struct pf_status, pf_status); #define V_pf_status VNET(pf_status) -#else -extern struct pf_status pf_status; -#endif -#ifdef __FreeBSD__ -VNET_DECLARE(uma_zone_t, pf_frent_pl); -#define V_pf_frent_pl VNET(pf_frent_pl) -VNET_DECLARE(uma_zone_t, pf_frag_pl); -#define V_pf_frag_pl VNET(pf_frag_pl) -VNET_DECLARE(struct sx, pf_consistency_lock); -#define V_pf_consistency_lock VNET(pf_consistency_lock) -#else -extern struct pool pf_frent_pl, pf_frag_pl; -extern struct rwlock pf_consistency_lock; -#endif - -struct pf_pool_limit { - void *pp; - unsigned limit; -}; -#ifdef __FreeBSD__ -VNET_DECLARE(struct pf_pool_limit, pf_pool_limits[PF_LIMIT_MAX]); -#define V_pf_pool_limits VNET(pf_pool_limits) -#else -extern struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; -#endif - -#ifdef __FreeBSD__ -struct pf_frent { - LIST_ENTRY(pf_frent) fr_next; - struct ip *fr_ip; - struct mbuf *fr_m; -}; - -struct pf_frcache { - LIST_ENTRY(pf_frcache) fr_next; - uint16_t fr_off; - uint16_t fr_end; -}; - -struct pf_fragment { - RB_ENTRY(pf_fragment) fr_entry; - TAILQ_ENTRY(pf_fragment) frag_next; - struct in_addr fr_src; - struct in_addr fr_dst; - u_int8_t fr_p; /* protocol of this fragment */ - u_int8_t fr_flags; /* status flags */ - u_int16_t fr_id; /* fragment id for reassemble */ - u_int16_t fr_max; /* fragment data max */ - u_int32_t fr_timeout; -#define fr_queue fr_u.fru_queue -#define fr_cache fr_u.fru_cache - union { - LIST_HEAD(pf_fragq, pf_frent) fru_queue; /* buffering */ - LIST_HEAD(pf_cacheq, pf_frcache) fru_cache; /* non-buf */ - } fr_u; +struct pf_limit { + uma_zone_t zone; + u_int limit; }; -#endif /* (__FreeBSD__) */ +VNET_DECLARE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); +#define V_pf_limits VNET(pf_limits) #endif /* _KERNEL */ -#ifdef __FreeBSD__ #ifdef _KERNEL VNET_DECLARE(struct pf_anchor_global, pf_anchors); #define V_pf_anchors VNET(pf_anchors) @@ -2153,11 +1910,6 @@ VNET_DECLARE(struct pf_anchor, pf_main_anchor); #define V_pf_main_anchor VNET(pf_main_anchor) #define pf_main_ruleset V_pf_main_anchor.ruleset #endif -#else -extern struct pf_anchor_global pf_anchors; -extern struct pf_anchor pf_main_anchor; -#define pf_main_ruleset pf_main_anchor.ruleset -#endif /* these ruleset functions can be linked into userland programs (pfctl) */ int pf_get_ruleset_number(u_int8_t); @@ -2168,20 +1920,10 @@ int pf_anchor_copyout(const struct pf_ruleset *, const struct pf_rule *, struct pfioc_rule *); void pf_anchor_remove(struct pf_rule *); void pf_remove_if_empty_ruleset(struct pf_ruleset *); -struct pf_anchor *pf_find_anchor(const char *); struct pf_ruleset *pf_find_ruleset(const char *); struct pf_ruleset *pf_find_or_create_ruleset(const char *); void pf_rs_initialize(void); -#ifndef __FreeBSD__ -#ifdef _KERNEL -int pf_anchor_copyout(const struct pf_ruleset *, - const struct pf_rule *, struct pfioc_rule *); -void pf_anchor_remove(struct pf_rule *); - -#endif /* _KERNEL */ -#endif - /* The fingerprint functions can be linked into userland programs (tcpdump) */ int pf_osfp_add(struct pf_osfp_ioctl *); #ifdef _KERNEL @@ -2189,20 +1931,9 @@ struct pf_osfp_enlist * pf_osfp_fingerprint(struct pf_pdesc *, struct mbuf *, int, const struct tcphdr *); #endif /* _KERNEL */ -struct pf_osfp_enlist * - pf_osfp_fingerprint_hdr(const struct ip *, const struct ip6_hdr *, - const struct tcphdr *); void pf_osfp_flush(void); int pf_osfp_get(struct pf_osfp_ioctl *); -#ifdef __FreeBSD__ -int pf_osfp_initialize(void); -void pf_osfp_cleanup(void); -#else -void pf_osfp_initialize(void); -#endif int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); -struct pf_os_fingerprint * - pf_osfp_validate(void); #ifdef _KERNEL void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); @@ -2219,16 +1950,12 @@ int pf_map_addr(u_int8_t, struct pf_rule *, struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, int, int, struct pfi_kif *, struct pf_src_node **, struct pf_state_key **, struct pf_state_key **, - struct pf_state_key **, struct pf_state_key **, struct pf_addr *, struct pf_addr *, u_int16_t, u_int16_t); -int pf_state_key_setup(struct pf_pdesc *, struct pf_rule *, - struct pf_state_key **, struct pf_state_key **, - struct pf_state_key **, struct pf_state_key **, - struct pf_addr *, struct pf_addr *, - u_int16_t, u_int16_t); +struct pf_state_key *pf_state_key_setup(struct pf_pdesc *, struct pf_addr *, + struct pf_addr *, u_int16_t, u_int16_t); +struct pf_state_key *pf_state_key_clone(struct pf_state_key *); #endif /* _KERNEL */ - #endif /* _NET_PFVAR_H_ */ diff --git a/sys/modules/pf/Makefile b/sys/modules/pf/Makefile index d4b01da9d72e3..2f941e1d90618 100644 --- a/sys/modules/pf/Makefile +++ b/sys/modules/pf/Makefile @@ -7,8 +7,8 @@ KMOD= pf SRCS= pf.c pf_if.c pf_lb.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \ - pf_ruleset.c \ - in4_cksum.c \ + pf_ruleset.c in4_cksum.c \ + bus_if.h device_if.h \ opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h opt_global.h CFLAGS+= -I${.CURDIR}/../../contrib/pf @@ -33,7 +33,6 @@ opt_pf.h: echo "#define DEV_PF 1" > ${.TARGET} echo "#define DEV_PFLOG 1" >> ${.TARGET} echo "#define DEV_PFSYNC 1" >> ${.TARGET} - echo "#define DEV_PFLOW 1" >> ${.TARGET} .if defined(VIMAGE) opt_global.h: diff --git a/sys/net/if.c b/sys/net/if.c index bf332db115fd1..6a2812e66cb60 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1084,6 +1084,7 @@ if_addgroup(struct ifnet *ifp, const char *groupname) struct ifg_list *ifgl; struct ifg_group *ifg = NULL; struct ifg_member *ifgm; + int new = 0; if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' && groupname[strlen(groupname) - 1] <= '9') @@ -1124,8 +1125,8 @@ if_addgroup(struct ifnet *ifp, const char *groupname) strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group)); ifg->ifg_refcnt = 0; TAILQ_INIT(&ifg->ifg_members); - EVENTHANDLER_INVOKE(group_attach_event, ifg); TAILQ_INSERT_TAIL(&V_ifg_head, ifg, ifg_next); + new = 1; } ifg->ifg_refcnt++; @@ -1139,6 +1140,8 @@ if_addgroup(struct ifnet *ifp, const char *groupname) IFNET_WUNLOCK(); + if (new) + EVENTHANDLER_INVOKE(group_attach_event, ifg); EVENTHANDLER_INVOKE(group_change_event, groupname); return (0); @@ -1177,10 +1180,11 @@ if_delgroup(struct ifnet *ifp, const char *groupname) if (--ifgl->ifgl_group->ifg_refcnt == 0) { TAILQ_REMOVE(&V_ifg_head, ifgl->ifgl_group, ifg_next); + IFNET_WUNLOCK(); EVENTHANDLER_INVOKE(group_detach_event, ifgl->ifgl_group); free(ifgl->ifgl_group, M_TEMP); - } - IFNET_WUNLOCK(); + } else + IFNET_WUNLOCK(); free(ifgl, M_TEMP); @@ -1221,11 +1225,12 @@ if_delgroups(struct ifnet *ifp) if (--ifgl->ifgl_group->ifg_refcnt == 0) { TAILQ_REMOVE(&V_ifg_head, ifgl->ifgl_group, ifg_next); + IFNET_WUNLOCK(); EVENTHANDLER_INVOKE(group_detach_event, ifgl->ifgl_group); free(ifgl->ifgl_group, M_TEMP); - } - IFNET_WUNLOCK(); + } else + IFNET_WUNLOCK(); free(ifgl, M_TEMP); diff --git a/sys/netinet/in_gif.c b/sys/netinet/in_gif.c index 22f35dfaad3e1..6c60390aa6a52 100644 --- a/sys/netinet/in_gif.c +++ b/sys/netinet/in_gif.c @@ -256,8 +256,6 @@ in_gif_output(struct ifnet *ifp, int family, struct mbuf *m) #endif } - m_addr_changed(m); - error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index 3260197e7a2ac..6451cb652a315 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -675,8 +675,6 @@ icmp_reflect(struct mbuf *m) goto done; /* Ip_output() will check for broadcast */ } - m_addr_changed(m); - t = ip->ip_dst; ip->ip_dst = ip->ip_src; diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c index 18a9c5af511f0..0dfab1f8ee91c 100644 --- a/sys/netinet/ipfw/ip_fw2.c +++ b/sys/netinet/ipfw/ip_fw2.c @@ -1698,20 +1698,30 @@ do { \ case O_ALTQ: { struct pf_mtag *at; + struct m_tag *mtag; ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; + /* + * ALTQ uses mbuf tags from another + * packet filtering system - pf(4). + * We allocate a tag in its format + * and fill it in, pretending to be pf(4). + */ match = 1; at = pf_find_mtag(m); if (at != NULL && at->qid != 0) break; - at = pf_get_mtag(m); - if (at == NULL) { + mtag = m_tag_get(PACKET_TAG_PF, + sizeof(struct pf_mtag), M_NOWAIT | M_ZERO); + if (mtag == NULL) { /* * Let the packet fall back to the * default ALTQ. */ break; } + m_tag_prepend(m, mtag); + at = (struct pf_mtag *)(mtag + 1); at->qid = altq->qid; at->hdr = ip; break; diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index d2b841a99a358..e48b66637890e 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -100,9 +100,6 @@ void (*ip_divert_ptr)(struct mbuf *, int); int (*ng_ipfw_input_p)(struct mbuf **, int, struct ip_fw_args *, int); -/* Hook for telling pf that the destination address changed */ -void (*m_addr_chg_pf_p)(struct mbuf *m); - #ifdef INET /* * Hooks for multicast routing. They all default to NULL, so leave them not diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 9d35e0ab9bb65..4cc2e69c18d33 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -544,7 +544,6 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m, m_freem(m->m_next); m->m_next = NULL; m->m_data = (caddr_t)ipgen; - m_addr_changed(m); /* m_len is set later */ tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index a1e19d9cae937..202dc05d52f6e 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1177,8 +1177,6 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code) ip6cp.ip6c_src = &icmp6src; ip6cp.ip6c_nxt = nxt; - m_addr_changed(m); - if (icmp6type == ICMP6_PACKET_TOO_BIG) { notifymtu = ntohl(icmp6->icmp6_mtu); ip6cp.ip6c_cmdarg = (void *)¬ifymtu; @@ -2298,8 +2296,6 @@ icmp6_reflect(struct mbuf *m, size_t off) m->m_flags &= ~(M_BCAST|M_MCAST); - m_addr_changed(m); - ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL); if (outif) icmp6_ifoutstat_inc(outif, type, code); diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index c329e11dd1616..573287caa4c34 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -264,8 +264,6 @@ in6_gif_output(struct ifnet *ifp, #endif } - m_addr_changed(m); - #ifdef IPV6_MINMTU /* * force fragmentation to minimum MTU, to avoid path MTU discovery. diff --git a/sys/netipsec/ipsec_input.c b/sys/netipsec/ipsec_input.c index 8b53bf4984c95..a004aef065dcb 100644 --- a/sys/netipsec/ipsec_input.c +++ b/sys/netipsec/ipsec_input.c @@ -473,8 +473,6 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, key_sa_recordxfer(sav, m); /* record data transfer */ - m_addr_changed(m); - #ifdef DEV_ENC encif->if_ipackets++; encif->if_ibytes += m->m_pkthdr.len; diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c index 1a1b6467f4bb7..b03e4b6e9b5ff 100644 --- a/sys/netipsec/ipsec_output.c +++ b/sys/netipsec/ipsec_output.c @@ -190,8 +190,6 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) } key_sa_recordxfer(sav, m); /* record data transfer */ - m_addr_changed(m); - /* * We're done with IPsec processing, transmit the packet using the * appropriate network protocol (IP or IPv6). SPD lookup will be diff --git a/sys/netipsec/xform_ipip.c b/sys/netipsec/xform_ipip.c index 0d5fdb4f81b86..c6f6379b1cf5e 100644 --- a/sys/netipsec/xform_ipip.c +++ b/sys/netipsec/xform_ipip.c @@ -392,8 +392,6 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) panic("%s: bogus ip version %u", __func__, v>>4); } - m_addr_changed(m); - if (netisr_queue(isr, m)) { /* (0) on success. */ V_ipipstat.ipips_qfull++; DPRINTF(("%s: packet dropped because of full queue\n", diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 38adfaf2ffa94..a26d5cae32b4a 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -749,16 +749,6 @@ m_last(struct mbuf *m) return (m); } -extern void (*m_addr_chg_pf_p)(struct mbuf *m); - -static __inline void -m_addr_changed(struct mbuf *m) -{ - - if (m_addr_chg_pf_p) - m_addr_chg_pf_p(m); -} - /* * mbuf, cluster, and external object allocation macros (for compatibility * purposes). @@ -998,7 +988,7 @@ struct mbuf *m_unshare(struct mbuf *, int how); #define PACKET_TAG_DIVERT 17 /* divert info */ #define PACKET_TAG_IPFORWARD 18 /* ipforward info */ #define PACKET_TAG_MACLABEL (19 | MTAG_PERSISTENT) /* MAC label */ -#define PACKET_TAG_PF 21 /* PF + ALTQ information */ +#define PACKET_TAG_PF (21 | MTAG_PERSISTENT) /* PF/ALTQ information */ #define PACKET_TAG_RTSOCKFAM 25 /* rtsock sa family */ #define PACKET_TAG_IPOPTIONS 27 /* Saved IP options */ #define PACKET_TAG_CARP 28 /* CARP info */ diff --git a/sys/sys/param.h b/sys/sys/param.h index a3bf8972d8c7e..a079980bc076f 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1000017 /* Master, propagated to newvers */ +#define __FreeBSD_version 1000018 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c index c08fbf008337d..854844fb637b7 100644 --- a/usr.bin/netstat/if.c +++ b/usr.bin/netstat/if.c @@ -81,6 +81,32 @@ static void catchalarm(int); static char addr_buf[NI_MAXHOST]; /* for getnameinfo() */ #endif +static const char* pfsyncacts[] = { + /* PFSYNC_ACT_CLR */ "clear all request", + /* PFSYNC_ACT_INS */ "state insert", + /* PFSYNC_ACT_INS_ACK */ "state inserted ack", + /* PFSYNC_ACT_UPD */ "state update", + /* PFSYNC_ACT_UPD_C */ "compressed state update", + /* PFSYNC_ACT_UPD_REQ */ "uncompressed state request", + /* PFSYNC_ACT_DEL */ "state delete", + /* PFSYNC_ACT_DEL_C */ "compressed state delete", + /* PFSYNC_ACT_INS_F */ "fragment insert", + /* PFSYNC_ACT_DEL_F */ "fragment delete", + /* PFSYNC_ACT_BUS */ "bulk update mark", + /* PFSYNC_ACT_TDB */ "TDB replay counter update", + /* PFSYNC_ACT_EOF */ "end of frame mark", +}; + +static void +pfsync_acts_stats(const char *fmt, uint64_t *a) +{ + int i; + + for (i = 0; i < PFSYNC_ACT_MAX; i++, a++) + if (*a || sflag <= 1) + printf(fmt, *a, pfsyncacts[i], plural(*a)); +} + /* * Dump pfsync statistics structure. */ @@ -106,11 +132,11 @@ pfsync_stats(u_long off, const char *name, int af1 __unused, int proto __unused) #define p(f, m) if (pfsyncstat.f || sflag <= 1) \ printf(m, (uintmax_t)pfsyncstat.f, plural(pfsyncstat.f)) -#define p2(f, m) if (pfsyncstat.f || sflag <= 1) \ - printf(m, (uintmax_t)pfsyncstat.f) p(pfsyncs_ipackets, "\t%ju packet%s received (IPv4)\n"); p(pfsyncs_ipackets6, "\t%ju packet%s received (IPv6)\n"); + pfsync_acts_stats("\t %ju %s%s received\n", + &pfsyncstat.pfsyncs_iacts[0]); p(pfsyncs_badif, "\t\t%ju packet%s discarded for bad interface\n"); p(pfsyncs_badttl, "\t\t%ju packet%s discarded for bad ttl\n"); p(pfsyncs_hdrops, "\t\t%ju packet%s shorter than header\n"); @@ -123,10 +149,11 @@ pfsync_stats(u_long off, const char *name, int af1 __unused, int proto __unused) p(pfsyncs_badstate, "\t\t%ju failed state lookup/insert%s\n"); p(pfsyncs_opackets, "\t%ju packet%s sent (IPv4)\n"); p(pfsyncs_opackets6, "\t%ju packet%s sent (IPv6)\n"); - p2(pfsyncs_onomem, "\t\t%ju send failed due to mbuf memory error\n"); - p2(pfsyncs_oerrors, "\t\t%ju send error\n"); + pfsync_acts_stats("\t %ju %s%s sent\n", + &pfsyncstat.pfsyncs_oacts[0]); + p(pfsyncs_onomem, "\t\t%ju failure%s due to mbuf memory error\n"); + p(pfsyncs_oerrors, "\t\t%ju send error%s\n"); #undef p -#undef p2 } /* diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt index 0b90bb24dbfa2..d2b247f5ee18a 100644 --- a/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt +++ b/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt @@ -585,7 +585,7 @@ PfInterfacesIfEntry ::= SEQUENCE { pfInterfacesIfDescr OCTET STRING, pfInterfacesIfType INTEGER, pfInterfacesIfTZero TimeTicks, - pfInterfacesIfRefsState Unsigned32, + pfInterfacesIfRefsState Null, pfInterfacesIfRefsRule Unsigned32, pfInterfacesIf4BytesInPass Counter64, pfInterfacesIf4BytesInBlock Counter64, diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c index bc4bc35555c2e..1048ffe66c9b2 100644 --- a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c +++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c @@ -586,11 +586,8 @@ pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val, val->v.uint32 = (time(NULL) - e->pfi.pfik_tzero) * 100; break; - case LEAF_pfInterfacesIfRefsState: - val->v.uint32 = e->pfi.pfik_states; - break; case LEAF_pfInterfacesIfRefsRule: - val->v.uint32 = e->pfi.pfik_rules; + val->v.uint32 = e->pfi.pfik_rulerefs; break; case LEAF_pfInterfacesIf4BytesInPass: val->v.counter64 = diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def b/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def index 7b791b3bd3e5a..1dfa14ce4c2a6 100644 --- a/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def +++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def @@ -108,7 +108,7 @@ (2 pfInterfacesIfDescr OCTETSTRING GET) (3 pfInterfacesIfType ENUM ( 0 group 1 instance 2 detached ) GET) (4 pfInterfacesIfTZero TIMETICKS GET) - (5 pfInterfacesIfRefsState UNSIGNED32 GET) + (5 pfInterfacesIfRefsState NULL GET) (6 pfInterfacesIfRefsRule UNSIGNED32 GET) (7 pfInterfacesIf4BytesInPass COUNTER64 GET) (8 pfInterfacesIf4BytesInBlock COUNTER64 GET) -- cgit v1.3 From 7ca72d7e5dc404dbf89ddd0a56c1827c7b880a61 Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sat, 8 Sep 2012 08:46:43 +0000 Subject: mdoc fixes. --- usr.sbin/ppp/ppp.8.m4 | 62 +++++++++++++++++---------------------------------- 1 file changed, 21 insertions(+), 41 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4 index 935814e79ef1d..7d3f6d241c49c 100644 --- a/usr.sbin/ppp/ppp.8.m4 +++ b/usr.sbin/ppp/ppp.8.m4 @@ -292,7 +292,7 @@ In direct mode, acts as server which accepts incoming .Em PPP connections on stdin/stdout. -.It "Supports PAP and CHAP (rfc 1994, 2433 and 2759) authentication. +.It Supports PAP and CHAP (rfc 1994, 2433 and 2759) authentication. With PAP or CHAP, it is possible to skip the Unix style .Xr login 1 procedure, and use the @@ -1019,12 +1019,11 @@ This behaviour can be changed using the .Dq set redial command: .Pp -.No set redial Ar secs Ns Xo +.No set redial Ar secs Ns .Oo + Ns Ar inc Ns -.Op - Ns Ar max Ns -.Oc Ns Op . Ns Ar next +.Oo - Ns Ar max Ns Oc Oc Ns +.Op . Ns Ar next .Op Ar attempts -.Xc .Pp .Bl -tag -width attempts -compact .It Ar secs @@ -1083,7 +1082,6 @@ are specified, the total number of attempts is still 4 (it does not attempt each number 4 times). .Pp Alternatively, -.Pp .Bd -literal -offset indent set redial 10+10-5.3 20 .Ed @@ -2029,11 +2027,8 @@ dialing and redialing separated by either a pipe or a colon .Pq Dq \&: : .Bd -ragged -offset indent -.No set phone Ar telno Ns Xo -.Oo \&| Ns Ar backupnumber -.Oc Ns ... Ns Oo : Ns Ar nextnumber -.Oc Ns ... -.Xc +.No set phone Ar telno Ns +.Oo \&| Ns Ar backupnumber Oc Ns ... Ns Oo : Ns Ar nextnumber Oc Ns ... .Ed .Pp Numbers after the first in a pipe-separated list are only used if the @@ -2530,7 +2525,6 @@ will show the same information at the link level. .Pp Armed with this information, the following configuration might be used: -.Pp .Bd -literal -offset indent mp: set timeout 0 @@ -2554,13 +2548,11 @@ Usually, the link will be configured first, then cloned. If you wish all links to be up all the time, you can add the following line to the end of your configuration. -.Pp .Bd -literal -offset indent link 1,2,3 set mode ddial .Ed .Pp If you want the links to dial on demand, this command could be used: -.Pp .Bd -literal -offset indent link * set mode auto .Ed @@ -2570,7 +2562,6 @@ Links may be tied to specific names by removing the line above, and specifying the following after the .Dq clone command: -.Pp .Bd -literal -offset indent link 1 set device /dev/cuad0 link 2 set device /dev/cuad1 @@ -3218,9 +3209,7 @@ Disabling this option will prevent the re-application of sticky routes, although the .Sq stick route list will still be maintained. -.It Op tcp Ns Xo -.No mssfixup -.Xc +.It Oo tcp Oc Ns No mssfixup Default: Enabled. This option tells .Nm @@ -3474,7 +3463,7 @@ be logged to the file .Ar remotePort Ns .Oo .No - Ns Ar remotePort -.Oc Ns +.Oc .Oc .Xc This command causes incoming @@ -3606,7 +3595,7 @@ These commands are also discussed in the file .Pa README.nat which comes with the source distribution. .Pp -.It Op !\& Ns Xo +.It Oo !\& Oc Ns Xo .No bg Ar command .Xc The given @@ -3808,7 +3797,7 @@ command is used .Dq !\& ) , .Nm will not complain if the route does not already exist. -.It dial|call Op Ar label Ns Xo +.It dial|call Oo Ar label Oc Ns Xo .No ... .Xc This command is the equivalent of @@ -3945,10 +3934,9 @@ When invoked with .Ar sub-command , only the synopsis for the given sub-command is shown. .El -.It Op data Ns Xo +.It Oo data Oc Ns Xo .No link -.Ar name Ns Op , Ns Ar name Ns -.No ... Ar command Op Ar args +.Ar name Ns Oo , Ns Ar name Oc Ns ... Ar command Op Ar args .Xc This command may prefix any other command if the user wishes to specify which link the command should affect. @@ -3970,7 +3958,7 @@ is .Dq * , .Ar command is executed on all links. -.It load Op Ar label Ns Xo +.It load Oo Ar label Oc Ns Xo .No ... .Xc Load the given @@ -4196,7 +4184,7 @@ certain characters from end to end (such as XON/XOFF etc). .Pp For the XON/XOFF scenario, use .Dq set accmap 000a0000 . -.It set Op auth Ns Xo +.It set Oo auth Oc Ns Xo .No key Ar value .Xc This sets the authentication key (or password) used in client mode @@ -4360,7 +4348,6 @@ as an option. The .Ar option Ns No s are as follows (in this order of preference): -.Pp .Bl -tag -width Ds .It auth The callee is expected to decide the callback number based on @@ -4993,11 +4980,8 @@ If you wish to assign a dynamic IP number to the peer, .Ar hisaddr may also be specified as a range of IP numbers in the format .Bd -ragged -offset indent -.Ar \&IP Ns Oo \&- Ns Ar \&IP Ns Xo -.Oc Ns Oo , Ns Ar \&IP Ns -.Op \&- Ns Ar \&IP Ns -.Oc Ns ... -.Xc +.Ar \&IP Ns Oo \&- Ns Ar \&IP Ns Oc Ns Oo , Ns Ar \&IP Ns +.Oo \&- Ns Ar \&IP Ns Oc Oc Ns ... .Ed .Pp for example: @@ -5319,10 +5303,7 @@ This allows the line parity to be set. The default value is .Ar none . .It set phone Ar telno Ns Xo -.Oo \&| Ns Ar backupnumber -.Oc Ns ... Ns Oo : Ns Ar nextnumber -.Oc Ns ... -.Xc +.Oo \&| Ns Ar backupnumber Oc Ns ... Ns Oo : Ns Ar nextnumber Oc Ns ... Xc This allows the specification of the phone number to be used in place of the \\\\T string in the dial and login chat scripts. Multiple phone numbers may be given separated either by a pipe @@ -5350,7 +5331,7 @@ This option configures the underlying .Xr ng_pppoe 4 node to either standard RFC2516 PPPoE or proprietary 3Com mode. If not set the system default will be used. -.It set Op proc Ns Xo +.It set Oo proc Oc Ns Xo .No title Op Ar value .Xc The current process title as displayed by @@ -5599,7 +5580,6 @@ NAS-Port-Id. The .Ar option Ns No s are as follows: -.Pp .Bl -tag -width Ds .It pid PID of the corresponding tunnel. @@ -5634,8 +5614,8 @@ If is unspecified or zero, the default kernel controlled value is used. .It set redial Ar secs Ns Xo .Oo + Ns Ar inc Ns -.Op - Ns Ar max Ns -.Oc Ns Op . Ns Ar next +.Oo - Ns Ar max Ns Oc Oc Ns +.Op . Ns Ar next .Op Ar attempts .Xc .Nm @@ -5935,7 +5915,7 @@ Show the current IPCP statistics. Show the protocol layers currently in use. .It show lcp Show the current LCP statistics. -.It show Op data Ns Xo +.It show Oo data Oc Ns Xo .No link .Xc Show high level link information. -- cgit v1.3 From 061c816530644e8483e88ae1a58ec308e04a7df5 Mon Sep 17 00:00:00 2001 From: Kevin Lo Date: Tue, 11 Sep 2012 07:35:24 +0000 Subject: Remove dead return code --- usr.sbin/rpc.lockd/lockd_lock.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/rpc.lockd/lockd_lock.c b/usr.sbin/rpc.lockd/lockd_lock.c index 6c409adc57819..dfb0d2a5c9df3 100644 --- a/usr.sbin/rpc.lockd/lockd_lock.c +++ b/usr.sbin/rpc.lockd/lockd_lock.c @@ -494,8 +494,6 @@ regions_overlap(start1, len1, start2, len2) } else { return 1; } - - return (result); } /* -- cgit v1.3 From 3dedcef7538823790b66175b812427d08eb98a44 Mon Sep 17 00:00:00 2001 From: Kevin Lo Date: Tue, 11 Sep 2012 07:54:41 +0000 Subject: Remove unused values --- sbin/ipfw/nat.c | 6 +++--- usr.sbin/lpr/lpd/printjob.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c index 9b4ac84b27e11..bff28e156c776 100644 --- a/sbin/ipfw/nat.c +++ b/sbin/ipfw/nat.c @@ -421,7 +421,7 @@ setup_redir_port(char *buf, int *ac, char ***av) /* * Extract local address. */ - if ((sep = strchr(**av, ',')) != NULL) { + if (strchr(**av, ',') != NULL) { r->laddr.s_addr = INADDR_NONE; r->lport = ~0; numLocalPorts = 1; @@ -454,7 +454,7 @@ setup_redir_port(char *buf, int *ac, char ***av) /* * Extract public port and optionally address. */ - if ((sep = strchr(**av, ':')) != NULL) { + if (strchr(**av, ':') != NULL) { if (StrToAddrAndPortRange(**av, &r->paddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " @@ -482,7 +482,7 @@ setup_redir_port(char *buf, int *ac, char ***av) * option for this redirect entry, else stop here processing arg[cv]. */ if (*ac != 0 && isdigit(***av)) { - if ((sep = strchr(**av, ':')) != NULL) { + if (strchr(**av, ':') != NULL) { if (StrToAddrAndPortRange(**av, &r->raddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c index 2407c3c115844..61300547fd447 100644 --- a/usr.sbin/lpr/lpd/printjob.c +++ b/usr.sbin/lpr/lpd/printjob.c @@ -1788,7 +1788,7 @@ openpr(const struct printer *pp) of_pid = 0; return; } else if (*pp->lp) { - if ((cp = strchr(pp->lp, '@')) != NULL) + if (strchr(pp->lp, '@') != NULL) opennet(pp); else opentty(pp); -- cgit v1.3 From 5739f2975d0039af9c4a7ffe131807f866312cd8 Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Tue, 11 Sep 2012 08:28:58 +0000 Subject: Spelling. --- usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c index 1f8264820b37f..f1a2f09e440fd 100644 --- a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c @@ -319,7 +319,7 @@ kld_file_stat_to_swrun(const struct kld_file_stat *kfs, } /** - * Get all visible proceses including the kernel visible threads + * Get all visible processes including the kernel visible threads */ static void swrun_OS_get_procs(void) -- cgit v1.3 From 6a9e7628f8fee493940b8336b9c3c3cec241e75b Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Tue, 11 Sep 2012 08:33:16 +0000 Subject: Do not count kernel threads as processes for hrSystemProcesses OID. PR: bin/160494 Submitted by: Jeremy Chadwick --- usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c index 87b78e85760ff..ee7d4b1b9f1f1 100644 --- a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c @@ -238,7 +238,7 @@ OS_getSystemProcesses(uint32_t *proc_count) if (hr_kd == NULL) return (SNMP_ERR_GENERR); - if (kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &pc) == NULL) { + if (kvm_getprocs(hr_kd, KERN_PROC_PROC, 0, &pc) == NULL) { syslog(LOG_ERR, "kvm_getprocs failed: %m"); return (SNMP_ERR_GENERR); } -- cgit v1.3 From 8840ea45aecce1d053ed45e1b91e8135ddc79e7f Mon Sep 17 00:00:00 2001 From: Kevin Lo Date: Wed, 12 Sep 2012 10:16:39 +0000 Subject: Add missing braces Obtained from: DragonFly --- usr.sbin/timed/timed/timed.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/timed/timed/timed.c b/usr.sbin/timed/timed/timed.c index e3397a9da854d..4b0030c17bcb4 100644 --- a/usr.sbin/timed/timed/timed.c +++ b/usr.sbin/timed/timed/timed.c @@ -418,9 +418,10 @@ main(argc, argv) justquit = 1; } for (ntp = nettab; ntp != NULL; ntp = ntp->next) { - if (ntp->status == MASTER) + if (ntp->status == MASTER) { rmnetmachs(ntp); ntp->status = NOMASTER; + } } checkignorednets(); pickslavenet(0); -- cgit v1.3 From 2c0a5e3ceefbbf8642f71e106b7cc901a88cff2a Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Wed, 12 Sep 2012 10:39:47 +0000 Subject: Add support for ipv6 addresses as destination PR: bin/150530 Submitted by: andy white Tested by: Olivier Cochard-Labbe MFC after: 1 month --- usr.sbin/syslogd/syslogd.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index d1c9fd16e28b2..2f42975cbcd9b 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -1931,6 +1931,7 @@ cfline(const char *line, struct filed *f, const char *prog, const char *host) case '@': { char *tp; + char endkey = ':'; /* * scan forward to see if there is a port defined. * so we can't use strlcpy.. @@ -1939,9 +1940,19 @@ cfline(const char *line, struct filed *f, const char *prog, const char *host) tp = f->f_un.f_forw.f_hname; p++; - while (*p && (*p != ':') && (i-- > 0)) { + /* + * an ipv6 address should start with a '[' in that case + * we should scan for a ']' + */ + if (*p == '[') { + p++; + endkey = ']'; + } + while (*p && (*p != endkey) && (i-- > 0)) { *tp++ = *p++; } + if (endkey == ']' && *p == endkey) + p++; *tp = '\0'; } /* See if we copied a domain and have a port */ -- cgit v1.3 From b5bec59148802a8bc658f4193999236f1a888e2c Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Wed, 12 Sep 2012 16:58:42 +0000 Subject: Add documentation for IPv6 support PR: docs/171580 Submitted by: bdrewery Reviewed by: wblock Approved by: gjb MFC after: 1 month X-MFC-With: r240389 --- usr.sbin/syslogd/syslog.conf.5 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/syslogd/syslog.conf.5 b/usr.sbin/syslogd/syslog.conf.5 index f9d40243fc577..3378aa08cda34 100644 --- a/usr.sbin/syslogd/syslog.conf.5 +++ b/usr.sbin/syslogd/syslog.conf.5 @@ -28,7 +28,7 @@ .\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd December 23, 2008 +.Dd September 12, 2012 .Dt SYSLOG.CONF 5 .Os .Sh NAME @@ -347,6 +347,14 @@ If a port number is added after a colon .Pq Ql :\& then that port will be used as the destination port rather than the usual syslog port. +IPv6 addresses can be used +by surrounding the address portion with +square brackets +.Po +.Ql [\& +and +.Ql ]\& +.Pc . .It A comma separated list of users. Selected messages are written to those users -- cgit v1.3 From 60149b5cce13419bafa045e91925869b79440ea1 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Thu, 13 Sep 2012 19:08:31 +0000 Subject: - Denote PCI-e endpoints that support FLR. - Make parsing of PCI-e extended capabilities assume that future version numbers are backwards compatible. - Add new AER error descriptions. - Add descriptions for more PCI-e extended capabilities. MFC after: 1 week --- usr.sbin/pciconf/cap.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++--- usr.sbin/pciconf/err.c | 6 +++++ 2 files changed, 72 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index 51e789ebf4e81..77f116caedfd9 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -412,6 +412,8 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) printf(" max data %d(%d)", MAX_PAYLOAD((flags & PCIM_EXP_CTL_MAX_PAYLOAD) >> 5), MAX_PAYLOAD(val & PCIM_EXP_CAP_MAX_PAYLOAD)); + if (val & PCIM_EXP_CAP_FLR) + printf(" FLR"); val = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_LINK_CAP, 4); flags = read_config(fd, &p->pc_sel, ptr+ PCIR_EXPRESS_LINK_STA, 2); printf(" link x%d(x%d)", (flags & PCIM_LINK_STA_WIDTH) >> 4, @@ -561,7 +563,7 @@ ecap_aer(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) uint32_t sta, mask; printf("AER %d", ver); - if (ver != 1) + if (ver < 1) return; sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_STATUS, 4); mask = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_SEVERITY, 4); @@ -577,7 +579,7 @@ ecap_vc(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) uint32_t cap1; printf("VC %d", ver); - if (ver != 1) + if (ver < 1) return; cap1 = read_config(fd, &p->pc_sel, ptr + PCIR_VC_CAP1, 4); printf(" max VC%d", cap1 & PCIM_VC_CAP1_EXT_COUNT); @@ -592,18 +594,66 @@ ecap_sernum(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) uint32_t high, low; printf("Serial %d", ver); - if (ver != 1) + if (ver < 1) return; low = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_LOW, 4); high = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_HIGH, 4); printf(" %08x%08x", high, low); } +static void +ecap_vendor(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) +{ + uint32_t val; + + printf("Vendor %d", ver); + if (ver < 1) + return; + val = read_config(fd, &p->pc_sel, ptr + 4, 4); + printf(" ID %d", val & 0xffff); +} + +static void +ecap_sec_pcie(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) +{ + uint32_t val; + + printf("PCIe Sec %d", ver); + if (ver < 1) + return; + val = read_config(fd, &p->pc_sel, ptr + 8, 4); + printf(" lane errors %#x", val); +} + +struct { + uint16_t id; + const char *name; +} ecap_names[] = { + { PCIZ_PWRBDGT, "Power Budgeting" }, + { PCIZ_RCLINK_DCL, "Root Complex Link Declaration" }, + { PCIZ_RCLINK_CTL, "Root Complex Internal Link Control" }, + { PCIZ_RCEC_ASSOC, "Root Complex Event Collector ASsociation" }, + { PCIZ_MFVC, "MFVC" }, + { PCIZ_RCRB, "RCRB" }, + { PCIZ_ACS, "ACS" }, + { PCIZ_ARI, "ARI" }, + { PCIZ_ATS, "ATS" }, + { PCIZ_SRIOV, "SRIOV" }, + { PCIZ_MULTICAST, "Multicast" }, + { PCIZ_RESIZE_BAR, "Resizable BAR" }, + { PCIZ_DPA, "DPA" }, + { PCIZ_TPH_REQ, "TPH Requester" }, + { PCIZ_LTR, "LTR" }, + { 0, NULL } +}; + static void list_ecaps(int fd, struct pci_conf *p) { + const char *name; uint32_t ecap; uint16_t ptr; + int i; ptr = PCIR_EXTCAP; ecap = read_config(fd, &p->pc_sel, ptr, 4); @@ -621,8 +671,20 @@ list_ecaps(int fd, struct pci_conf *p) case PCIZ_SERNUM: ecap_sernum(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; + case PCIZ_VENDOR: + ecap_vendor(fd, p, ptr, PCI_EXTCAP_VER(ecap)); + break; + case PCIZ_SEC_PCIE: + ecap_sec_pcie(fd, p, ptr, PCI_EXTCAP_VER(ecap)); + break; default: - printf("unknown %d", PCI_EXTCAP_VER(ecap)); + name = "unknown"; + for (i = 0; ecap_names[i].name != NULL; i++) + if (ecap_names[i].id == PCI_EXTCAP_ID(ecap)) { + name = ecap_names[i].name; + break; + } + printf("%s %d", name, PCI_EXTCAP_VER(ecap)); break; } printf("\n"); diff --git a/usr.sbin/pciconf/err.c b/usr.sbin/pciconf/err.c index 62445b185ef6d..6b364f6c51db6 100644 --- a/usr.sbin/pciconf/err.c +++ b/usr.sbin/pciconf/err.c @@ -91,6 +91,10 @@ static struct bit_table aer_uc[] = { { PCIM_AER_UC_ECRC_ERROR, "ECRC Error" }, { PCIM_AER_UC_UNSUPPORTED_REQUEST, "Unsupported Request" }, { PCIM_AER_UC_ACS_VIOLATION, "ACS Violation" }, + { PCIM_AER_UC_INTERNAL_ERROR, "Uncorrectable Internal Error" }, + { PCIM_AER_UC_MC_BLOCKED_TLP, "MC Blocked TLP" }, + { PCIM_AER_UC_ATOMIC_EGRESS_BLK, "AtomicOp Egress Blocked" }, + { PCIM_AER_UC_TLP_PREFIX_BLOCKED, "TLP Prefix Blocked Error" }, { 0, NULL }, }; @@ -102,6 +106,8 @@ static struct bit_table aer_cor[] = { { PCIM_AER_COR_REPLAY_ROLLOVER, "REPLAY_NUM Rollover" }, { PCIM_AER_COR_REPLAY_TIMEOUT, "Replay Timer Timeout" }, { PCIM_AER_COR_ADVISORY_NF_ERROR, "Advisory Non-Fatal Error" }, + { PCIM_AER_COR_INTERNAL_ERROR, "Corrected Internal Error" }, + { PCIM_AER_COR_HEADER_LOG_OVFLOW, "Header Log Overflow" }, { 0, NULL }, }; -- cgit v1.3 From d902fa0fa496c0147bbd25165e84180775421def Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Fri, 14 Sep 2012 00:19:06 +0000 Subject: Do not change owner, group, or mode when package database directory and its contents are created with pkg_add(1). It may happen when the packing list contains @owner, @group, or @mode. Reported by: Donald Bostrom (dbostrom at niksun dot com) Reviewed by: portmgr (bapt) --- usr.sbin/pkg_install/add/add.h | 2 +- usr.sbin/pkg_install/add/extract.c | 2 +- usr.sbin/pkg_install/add/futil.c | 5 +++-- usr.sbin/pkg_install/add/perform.c | 2 +- usr.sbin/pkg_install/lib/lib.h | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pkg_install/add/add.h b/usr.sbin/pkg_install/add/add.h index d9892ad799ddf..e796120e98170 100644 --- a/usr.sbin/pkg_install/add/add.h +++ b/usr.sbin/pkg_install/add/add.h @@ -41,7 +41,7 @@ extern char *PkgAddCmd; extern char FirstPen[]; extern add_mode_t AddMode; -int make_hierarchy(char *); +int make_hierarchy(char *, Boolean); void extract_plist(const char *, Package *); void apply_perms(const char *, const char *); diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c index 90c33c011ed3b..97694648e8cff 100644 --- a/usr.sbin/pkg_install/add/extract.c +++ b/usr.sbin/pkg_install/add/extract.c @@ -225,7 +225,7 @@ extract_plist(const char *home, Package *pkg) printf("extract: CWD to %s\n", p->name); PUSHOUT(Directory); if (strcmp(p->name, ".")) { - if (!Fake && make_hierarchy(p->name) == FAIL) { + if (!Fake && make_hierarchy(p->name, TRUE) == FAIL) { cleanup(0); errx(2, "%s: unable to cwd to '%s'", __func__, p->name); } diff --git a/usr.sbin/pkg_install/add/futil.c b/usr.sbin/pkg_install/add/futil.c index 8f9d369a3a984..82217f51543d0 100644 --- a/usr.sbin/pkg_install/add/futil.c +++ b/usr.sbin/pkg_install/add/futil.c @@ -31,7 +31,7 @@ __FBSDID("$FreeBSD$"); */ int -make_hierarchy(char *dir) +make_hierarchy(char *dir, Boolean set_perm) { char *cp1, *cp2; @@ -55,7 +55,8 @@ make_hierarchy(char *dir) *cp2 = '/'; return FAIL; } - apply_perms(NULL, dir); + if (set_perm) + apply_perms(NULL, dir); } /* Put it back */ if (cp2) { diff --git a/usr.sbin/pkg_install/add/perform.c b/usr.sbin/pkg_install/add/perform.c index 24637eba92ddb..638e046277509 100644 --- a/usr.sbin/pkg_install/add/perform.c +++ b/usr.sbin/pkg_install/add/perform.c @@ -511,7 +511,7 @@ pkg_do(char *pkg) zapLogDir = 1; if (Verbose) printf("Attempting to record package into %s..\n", LogDir); - if (make_hierarchy(LogDir)) { + if (make_hierarchy(LogDir, FALSE)) { warnx("can't record package into '%s', you're on your own!", LogDir); bzero(LogDir, FILENAME_MAX); diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index 7890254636ce3..e368b809d825f 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -99,7 +99,7 @@ * Version of the package tools - increase whenever you make a change * in the code that is not cosmetic only. */ -#define PKG_INSTALL_VERSION 20120530 +#define PKG_INSTALL_VERSION 20120913 #define PKG_WRAPCONF_FNAME "/var/db/pkg_install.conf" #define main(argc, argv) real_main(argc, argv) -- cgit v1.3 From 3b3a8eb937bf8045231e8364bfd1b94cd4a95979 Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Fri, 14 Sep 2012 11:51:49 +0000 Subject: o Create directory sys/netpfil, where all packet filters should reside, and move there ipfw(4) and pf(4). o Move most modified parts of pf out of contrib. Actual movements: sys/contrib/pf/net/*.c -> sys/netpfil/pf/ sys/contrib/pf/net/*.h -> sys/net/ contrib/pf/pfctl/*.c -> sbin/pfctl contrib/pf/pfctl/*.h -> sbin/pfctl contrib/pf/pfctl/pfctl.8 -> sbin/pfctl contrib/pf/pfctl/*.4 -> share/man/man4 contrib/pf/pfctl/*.5 -> share/man/man5 sys/netinet/ipfw -> sys/netpfil/ipfw The arguable movement is pf/net/*.h -> sys/net. There are future plans to refactor pf includes, so I decided not to break things twice. Not modified bits of pf left in contrib: authpf, ftp-proxy, tftp-proxy, pflogd. The ipfw(4) movement is planned to be merged to stable/9, to make head and stable match. Discussed with: bz, luigi --- contrib/pf/man/pf.4 | 1175 ----- contrib/pf/man/pf.conf.5 | 3066 ------------- contrib/pf/man/pf.os.5 | 223 - contrib/pf/man/pflog.4 | 107 - contrib/pf/man/pfsync.4 | 229 - contrib/pf/pfctl/parse.y | 6038 ------------------------- contrib/pf/pfctl/pf_print_state.c | 375 -- contrib/pf/pfctl/pfctl.8 | 687 --- contrib/pf/pfctl/pfctl.c | 2391 ---------- contrib/pf/pfctl/pfctl.h | 130 - contrib/pf/pfctl/pfctl_altq.c | 1258 ------ contrib/pf/pfctl/pfctl_optimize.c | 1655 ------- contrib/pf/pfctl/pfctl_osfp.c | 1108 ----- contrib/pf/pfctl/pfctl_parser.c | 1746 ------- contrib/pf/pfctl/pfctl_parser.h | 305 -- contrib/pf/pfctl/pfctl_qstats.c | 449 -- contrib/pf/pfctl/pfctl_radix.c | 585 --- contrib/pf/pfctl/pfctl_table.c | 634 --- include/Makefile | 8 - sbin/pfctl/Makefile | 14 +- sbin/pfctl/missing/altq/altq.h | 204 - sbin/pfctl/missing/altq/altq_cbq.h | 232 - sbin/pfctl/missing/altq/altq_classq.h | 203 - sbin/pfctl/missing/altq/altq_hfsc.h | 325 -- sbin/pfctl/missing/altq/altq_priq.h | 172 - sbin/pfctl/missing/altq/altq_red.h | 199 - sbin/pfctl/missing/altq/altq_rio.h | 145 - sbin/pfctl/missing/altq/altq_rmclass.h | 263 -- sbin/pfctl/missing/altq/altq_rmclass_debug.h | 113 - sbin/pfctl/missing/altq/altq_var.h | 267 -- sbin/pfctl/missing/altq/altq_wfq.h | 129 - sbin/pfctl/parse.y | 6038 +++++++++++++++++++++++++ sbin/pfctl/pf_print_state.c | 375 ++ sbin/pfctl/pfctl.8 | 687 +++ sbin/pfctl/pfctl.c | 2391 ++++++++++ sbin/pfctl/pfctl.h | 130 + sbin/pfctl/pfctl_altq.c | 1258 ++++++ sbin/pfctl/pfctl_optimize.c | 1655 +++++++ sbin/pfctl/pfctl_osfp.c | 1108 +++++ sbin/pfctl/pfctl_parser.c | 1746 +++++++ sbin/pfctl/pfctl_parser.h | 305 ++ sbin/pfctl/pfctl_qstats.c | 449 ++ sbin/pfctl/pfctl_radix.c | 585 +++ sbin/pfctl/pfctl_table.c | 634 +++ share/man/man4/Makefile | 3 + share/man/man4/pf.4 | 1175 +++++ share/man/man4/pflog.4 | 107 + share/man/man4/pfsync.4 | 229 + share/man/man5/Makefile | 2 + share/man/man5/pf.conf.5 | 3066 +++++++++++++ share/man/man5/pf.os.5 | 223 + sys/conf/files | 83 +- sys/conf/kern.pre.mk | 3 - sys/contrib/pf/net/if_pflog.c | 290 -- sys/contrib/pf/net/if_pflog.h | 66 - sys/contrib/pf/net/if_pfsync.c | 2397 ---------- sys/contrib/pf/net/if_pfsync.h | 298 -- sys/contrib/pf/net/pf.c | 6270 ------------------------- sys/contrib/pf/net/pf_if.c | 859 ---- sys/contrib/pf/net/pf_ioctl.c | 3774 ---------------- sys/contrib/pf/net/pf_lb.c | 663 --- sys/contrib/pf/net/pf_mtag.h | 62 - sys/contrib/pf/net/pf_norm.c | 1999 -------- sys/contrib/pf/net/pf_osfp.c | 526 --- sys/contrib/pf/net/pf_ruleset.c | 424 -- sys/contrib/pf/net/pf_table.c | 2191 --------- sys/contrib/pf/net/pfvar.h | 1961 -------- sys/contrib/pf/netinet/in4_cksum.c | 120 - sys/modules/dummynet/Makefile | 2 +- sys/modules/ipfw/Makefile | 3 +- sys/modules/ipfw_nat/Makefile | 2 +- sys/modules/pf/Makefile | 5 +- sys/modules/pflog/Makefile | 4 +- sys/modules/pfsync/Makefile | 4 +- sys/net/if_pflog.h | 66 + sys/net/if_pfsync.h | 298 ++ sys/net/pf_mtag.h | 62 + sys/net/pfvar.h | 1961 ++++++++ sys/netgraph/ng_ipfw.c | 3 +- sys/netinet/ipfw/dn_heap.c | 552 --- sys/netinet/ipfw/dn_heap.h | 191 - sys/netinet/ipfw/dn_sched.h | 191 - sys/netinet/ipfw/dn_sched_fifo.c | 120 - sys/netinet/ipfw/dn_sched_prio.c | 229 - sys/netinet/ipfw/dn_sched_qfq.c | 864 ---- sys/netinet/ipfw/dn_sched_rr.c | 307 -- sys/netinet/ipfw/dn_sched_wf2q.c | 373 -- sys/netinet/ipfw/dummynet.txt | 860 ---- sys/netinet/ipfw/ip_dn_glue.c | 845 ---- sys/netinet/ipfw/ip_dn_io.c | 858 ---- sys/netinet/ipfw/ip_dn_private.h | 403 -- sys/netinet/ipfw/ip_dummynet.c | 2314 ---------- sys/netinet/ipfw/ip_fw2.c | 2790 ------------ sys/netinet/ipfw/ip_fw_dynamic.c | 1244 ----- sys/netinet/ipfw/ip_fw_log.c | 552 --- sys/netinet/ipfw/ip_fw_nat.c | 661 --- sys/netinet/ipfw/ip_fw_pfil.c | 588 --- sys/netinet/ipfw/ip_fw_private.h | 309 -- sys/netinet/ipfw/ip_fw_sockopt.c | 1448 ------ sys/netinet/ipfw/ip_fw_table.c | 761 ---- sys/netinet/ipfw/test/Makefile | 51 - sys/netinet/ipfw/test/dn_test.h | 175 - sys/netinet/ipfw/test/main.c | 636 --- sys/netinet/ipfw/test/mylist.h | 49 - sys/netinet/ipfw/test/test_dn_heap.c | 162 - sys/netinet/ipfw/test/test_dn_sched.c | 89 - sys/netpfil/ipfw/dn_heap.c | 552 +++ sys/netpfil/ipfw/dn_heap.h | 191 + sys/netpfil/ipfw/dn_sched.h | 191 + sys/netpfil/ipfw/dn_sched_fifo.c | 120 + sys/netpfil/ipfw/dn_sched_prio.c | 229 + sys/netpfil/ipfw/dn_sched_qfq.c | 864 ++++ sys/netpfil/ipfw/dn_sched_rr.c | 307 ++ sys/netpfil/ipfw/dn_sched_wf2q.c | 373 ++ sys/netpfil/ipfw/dummynet.txt | 860 ++++ sys/netpfil/ipfw/ip_dn_glue.c | 846 ++++ sys/netpfil/ipfw/ip_dn_io.c | 857 ++++ sys/netpfil/ipfw/ip_dn_private.h | 403 ++ sys/netpfil/ipfw/ip_dummynet.c | 2315 ++++++++++ sys/netpfil/ipfw/ip_fw2.c | 2791 ++++++++++++ sys/netpfil/ipfw/ip_fw_dynamic.c | 1245 +++++ sys/netpfil/ipfw/ip_fw_log.c | 553 +++ sys/netpfil/ipfw/ip_fw_nat.c | 662 +++ sys/netpfil/ipfw/ip_fw_pfil.c | 590 +++ sys/netpfil/ipfw/ip_fw_private.h | 309 ++ sys/netpfil/ipfw/ip_fw_sockopt.c | 1449 ++++++ sys/netpfil/ipfw/ip_fw_table.c | 762 ++++ sys/netpfil/ipfw/test/Makefile | 51 + sys/netpfil/ipfw/test/dn_test.h | 175 + sys/netpfil/ipfw/test/main.c | 636 +++ sys/netpfil/ipfw/test/mylist.h | 49 + sys/netpfil/ipfw/test/test_dn_heap.c | 162 + sys/netpfil/ipfw/test/test_dn_sched.c | 89 + sys/netpfil/pf/if_pflog.c | 290 ++ sys/netpfil/pf/if_pfsync.c | 2397 ++++++++++ sys/netpfil/pf/in4_cksum.c | 120 + sys/netpfil/pf/pf.c | 6271 ++++++++++++++++++++++++++ sys/netpfil/pf/pf_if.c | 859 ++++ sys/netpfil/pf/pf_ioctl.c | 3774 ++++++++++++++++ sys/netpfil/pf/pf_lb.c | 663 +++ sys/netpfil/pf/pf_norm.c | 1999 ++++++++ sys/netpfil/pf/pf_osfp.c | 526 +++ sys/netpfil/pf/pf_ruleset.c | 424 ++ sys/netpfil/pf/pf_table.c | 2191 +++++++++ usr.sbin/authpf/Makefile | 3 - usr.sbin/ftp-proxy/ftp-proxy/Makefile | 1 - 146 files changed, 61746 insertions(+), 64022 deletions(-) delete mode 100644 contrib/pf/man/pf.4 delete mode 100644 contrib/pf/man/pf.conf.5 delete mode 100644 contrib/pf/man/pf.os.5 delete mode 100644 contrib/pf/man/pflog.4 delete mode 100644 contrib/pf/man/pfsync.4 delete mode 100644 contrib/pf/pfctl/parse.y delete mode 100644 contrib/pf/pfctl/pf_print_state.c delete mode 100644 contrib/pf/pfctl/pfctl.8 delete mode 100644 contrib/pf/pfctl/pfctl.c delete mode 100644 contrib/pf/pfctl/pfctl.h delete mode 100644 contrib/pf/pfctl/pfctl_altq.c delete mode 100644 contrib/pf/pfctl/pfctl_optimize.c delete mode 100644 contrib/pf/pfctl/pfctl_osfp.c delete mode 100644 contrib/pf/pfctl/pfctl_parser.c delete mode 100644 contrib/pf/pfctl/pfctl_parser.h delete mode 100644 contrib/pf/pfctl/pfctl_qstats.c delete mode 100644 contrib/pf/pfctl/pfctl_radix.c delete mode 100644 contrib/pf/pfctl/pfctl_table.c delete mode 100644 sbin/pfctl/missing/altq/altq.h delete mode 100644 sbin/pfctl/missing/altq/altq_cbq.h delete mode 100644 sbin/pfctl/missing/altq/altq_classq.h delete mode 100644 sbin/pfctl/missing/altq/altq_hfsc.h delete mode 100644 sbin/pfctl/missing/altq/altq_priq.h delete mode 100644 sbin/pfctl/missing/altq/altq_red.h delete mode 100644 sbin/pfctl/missing/altq/altq_rio.h delete mode 100644 sbin/pfctl/missing/altq/altq_rmclass.h delete mode 100644 sbin/pfctl/missing/altq/altq_rmclass_debug.h delete mode 100644 sbin/pfctl/missing/altq/altq_var.h delete mode 100644 sbin/pfctl/missing/altq/altq_wfq.h create mode 100644 sbin/pfctl/parse.y create mode 100644 sbin/pfctl/pf_print_state.c create mode 100644 sbin/pfctl/pfctl.8 create mode 100644 sbin/pfctl/pfctl.c create mode 100644 sbin/pfctl/pfctl.h create mode 100644 sbin/pfctl/pfctl_altq.c create mode 100644 sbin/pfctl/pfctl_optimize.c create mode 100644 sbin/pfctl/pfctl_osfp.c create mode 100644 sbin/pfctl/pfctl_parser.c create mode 100644 sbin/pfctl/pfctl_parser.h create mode 100644 sbin/pfctl/pfctl_qstats.c create mode 100644 sbin/pfctl/pfctl_radix.c create mode 100644 sbin/pfctl/pfctl_table.c create mode 100644 share/man/man4/pf.4 create mode 100644 share/man/man4/pflog.4 create mode 100644 share/man/man4/pfsync.4 create mode 100644 share/man/man5/pf.conf.5 create mode 100644 share/man/man5/pf.os.5 delete mode 100644 sys/contrib/pf/net/if_pflog.c delete mode 100644 sys/contrib/pf/net/if_pflog.h delete mode 100644 sys/contrib/pf/net/if_pfsync.c delete mode 100644 sys/contrib/pf/net/if_pfsync.h delete mode 100644 sys/contrib/pf/net/pf.c delete mode 100644 sys/contrib/pf/net/pf_if.c delete mode 100644 sys/contrib/pf/net/pf_ioctl.c delete mode 100644 sys/contrib/pf/net/pf_lb.c delete mode 100644 sys/contrib/pf/net/pf_mtag.h delete mode 100644 sys/contrib/pf/net/pf_norm.c delete mode 100644 sys/contrib/pf/net/pf_osfp.c delete mode 100644 sys/contrib/pf/net/pf_ruleset.c delete mode 100644 sys/contrib/pf/net/pf_table.c delete mode 100644 sys/contrib/pf/net/pfvar.h delete mode 100644 sys/contrib/pf/netinet/in4_cksum.c create mode 100644 sys/net/if_pflog.h create mode 100644 sys/net/if_pfsync.h create mode 100644 sys/net/pf_mtag.h create mode 100644 sys/net/pfvar.h delete mode 100644 sys/netinet/ipfw/dn_heap.c delete mode 100644 sys/netinet/ipfw/dn_heap.h delete mode 100644 sys/netinet/ipfw/dn_sched.h delete mode 100644 sys/netinet/ipfw/dn_sched_fifo.c delete mode 100644 sys/netinet/ipfw/dn_sched_prio.c delete mode 100644 sys/netinet/ipfw/dn_sched_qfq.c delete mode 100644 sys/netinet/ipfw/dn_sched_rr.c delete mode 100644 sys/netinet/ipfw/dn_sched_wf2q.c delete mode 100644 sys/netinet/ipfw/dummynet.txt delete mode 100644 sys/netinet/ipfw/ip_dn_glue.c delete mode 100644 sys/netinet/ipfw/ip_dn_io.c delete mode 100644 sys/netinet/ipfw/ip_dn_private.h delete mode 100644 sys/netinet/ipfw/ip_dummynet.c delete mode 100644 sys/netinet/ipfw/ip_fw2.c delete mode 100644 sys/netinet/ipfw/ip_fw_dynamic.c delete mode 100644 sys/netinet/ipfw/ip_fw_log.c delete mode 100644 sys/netinet/ipfw/ip_fw_nat.c delete mode 100644 sys/netinet/ipfw/ip_fw_pfil.c delete mode 100644 sys/netinet/ipfw/ip_fw_private.h delete mode 100644 sys/netinet/ipfw/ip_fw_sockopt.c delete mode 100644 sys/netinet/ipfw/ip_fw_table.c delete mode 100644 sys/netinet/ipfw/test/Makefile delete mode 100644 sys/netinet/ipfw/test/dn_test.h delete mode 100644 sys/netinet/ipfw/test/main.c delete mode 100644 sys/netinet/ipfw/test/mylist.h delete mode 100644 sys/netinet/ipfw/test/test_dn_heap.c delete mode 100644 sys/netinet/ipfw/test/test_dn_sched.c create mode 100644 sys/netpfil/ipfw/dn_heap.c create mode 100644 sys/netpfil/ipfw/dn_heap.h create mode 100644 sys/netpfil/ipfw/dn_sched.h create mode 100644 sys/netpfil/ipfw/dn_sched_fifo.c create mode 100644 sys/netpfil/ipfw/dn_sched_prio.c create mode 100644 sys/netpfil/ipfw/dn_sched_qfq.c create mode 100644 sys/netpfil/ipfw/dn_sched_rr.c create mode 100644 sys/netpfil/ipfw/dn_sched_wf2q.c create mode 100644 sys/netpfil/ipfw/dummynet.txt create mode 100644 sys/netpfil/ipfw/ip_dn_glue.c create mode 100644 sys/netpfil/ipfw/ip_dn_io.c create mode 100644 sys/netpfil/ipfw/ip_dn_private.h create mode 100644 sys/netpfil/ipfw/ip_dummynet.c create mode 100644 sys/netpfil/ipfw/ip_fw2.c create mode 100644 sys/netpfil/ipfw/ip_fw_dynamic.c create mode 100644 sys/netpfil/ipfw/ip_fw_log.c create mode 100644 sys/netpfil/ipfw/ip_fw_nat.c create mode 100644 sys/netpfil/ipfw/ip_fw_pfil.c create mode 100644 sys/netpfil/ipfw/ip_fw_private.h create mode 100644 sys/netpfil/ipfw/ip_fw_sockopt.c create mode 100644 sys/netpfil/ipfw/ip_fw_table.c create mode 100644 sys/netpfil/ipfw/test/Makefile create mode 100644 sys/netpfil/ipfw/test/dn_test.h create mode 100644 sys/netpfil/ipfw/test/main.c create mode 100644 sys/netpfil/ipfw/test/mylist.h create mode 100644 sys/netpfil/ipfw/test/test_dn_heap.c create mode 100644 sys/netpfil/ipfw/test/test_dn_sched.c create mode 100644 sys/netpfil/pf/if_pflog.c create mode 100644 sys/netpfil/pf/if_pfsync.c create mode 100644 sys/netpfil/pf/in4_cksum.c create mode 100644 sys/netpfil/pf/pf.c create mode 100644 sys/netpfil/pf/pf_if.c create mode 100644 sys/netpfil/pf/pf_ioctl.c create mode 100644 sys/netpfil/pf/pf_lb.c create mode 100644 sys/netpfil/pf/pf_norm.c create mode 100644 sys/netpfil/pf/pf_osfp.c create mode 100644 sys/netpfil/pf/pf_ruleset.c create mode 100644 sys/netpfil/pf/pf_table.c (limited to 'usr.sbin') diff --git a/contrib/pf/man/pf.4 b/contrib/pf/man/pf.4 deleted file mode 100644 index 635078dbee249..0000000000000 --- a/contrib/pf/man/pf.4 +++ /dev/null @@ -1,1175 +0,0 @@ -.\" $OpenBSD: pf.4,v 1.62 2008/09/10 14:57:37 jmc Exp $ -.\" -.\" Copyright (C) 2001, Kjell Wooding. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd June 29 2012 -.Dt PF 4 -.Os -.Sh NAME -.Nm pf -.Nd packet filter -.Sh SYNOPSIS -.Cd "device pf" -.Sh DESCRIPTION -Packet filtering takes place in the kernel. -A pseudo-device, -.Pa /dev/pf , -allows userland processes to control the -behavior of the packet filter through an -.Xr ioctl 2 -interface. -There are commands to enable and disable the filter, load rulesets, -add and remove individual rules or state table entries, -and retrieve statistics. -The most commonly used functions are covered by -.Xr pfctl 8 . -.Pp -Manipulations like loading a ruleset that involve more than a single -.Xr ioctl 2 -call require a so-called -.Em ticket , -which prevents the occurrence of -multiple concurrent manipulations. -.Pp -Fields of -.Xr ioctl 2 -parameter structures that refer to packet data (like -addresses and ports) are generally expected in network byte-order. -.Pp -Rules and address tables are contained in so-called -.Em anchors . -When servicing an -.Xr ioctl 2 -request, if the anchor field of the argument structure is empty, -the kernel will use the default anchor (i.e., the main ruleset) -in operations. -Anchors are specified by name and may be nested, with components -separated by -.Sq / -characters, similar to how file system hierarchies are laid out. -The final component of the anchor path is the anchor under which -operations will be performed. -.Sh SYSCTL VARIABLES AND LOADER TUNABLES -The following -.Xr loader 8 -tunables are available. -.Bl -tag -width indent -.It Va net.pf.states_hashsize -Size of hash tables that store states. -Should be power of 2. -Default value is 32768. -.It Va net.pf.source_nodes_hashsize -Size of hash table that store source nodes. -Should be power of 2. -Default value is 8192. -.El -.Pp -Read only -.Xr sysctl 8 -variables with matching names are provided to obtain current values -at runtime. -.Sh IOCTL INTERFACE -.Nm -supports the following -.Xr ioctl 2 -commands, available through -.Aq Pa net/pfvar.h : -.Bl -tag -width xxxxxx -.It Dv DIOCSTART -Start the packet filter. -.It Dv DIOCSTOP -Stop the packet filter. -.It Dv DIOCSTARTALTQ -Start the ALTQ bandwidth control system (see -.Xr altq 9 ) . -.It Dv DIOCSTOPALTQ -Stop the ALTQ bandwidth control system. -.It Dv DIOCBEGINADDRS Fa "struct pfioc_pooladdr *pp" -.Bd -literal -struct pfioc_pooladdr { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - u_int32_t r_num; - u_int8_t r_action; - u_int8_t r_last; - u_int8_t af; - char anchor[MAXPATHLEN]; - struct pf_pooladdr addr; -}; -.Ed -.Pp -Clear the buffer address pool and get a -.Va ticket -for subsequent -.Dv DIOCADDADDR , -.Dv DIOCADDRULE , -and -.Dv DIOCCHANGERULE -calls. -.It Dv DIOCADDADDR Fa "struct pfioc_pooladdr *pp" -.Pp -Add the pool address -.Va addr -to the buffer address pool to be used in the following -.Dv DIOCADDRULE -or -.Dv DIOCCHANGERULE -call. -All other members of the structure are ignored. -.It Dv DIOCADDRULE Fa "struct pfioc_rule *pr" -.Bd -literal -struct pfioc_rule { - u_int32_t action; - u_int32_t ticket; - u_int32_t pool_ticket; - u_int32_t nr; - char anchor[MAXPATHLEN]; - char anchor_call[MAXPATHLEN]; - struct pf_rule rule; -}; -.Ed -.Pp -Add -.Va rule -at the end of the inactive ruleset. -This call requires a -.Va ticket -obtained through a preceding -.Dv DIOCXBEGIN -call and a -.Va pool_ticket -obtained through a -.Dv DIOCBEGINADDRS -call. -.Dv DIOCADDADDR -must also be called if any pool addresses are required. -The optional -.Va anchor -name indicates the anchor in which to append the rule. -.Va nr -and -.Va action -are ignored. -.It Dv DIOCADDALTQ Fa "struct pfioc_altq *pa" -Add an ALTQ discipline or queue. -.Bd -literal -struct pfioc_altq { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - struct pf_altq altq; -}; -.Ed -.It Dv DIOCGETRULES Fa "struct pfioc_rule *pr" -Get a -.Va ticket -for subsequent -.Dv DIOCGETRULE -calls and the number -.Va nr -of rules in the active ruleset. -.It Dv DIOCGETRULE Fa "struct pfioc_rule *pr" -Get a -.Va rule -by its number -.Va nr -using the -.Va ticket -obtained through a preceding -.Dv DIOCGETRULES -call. -If -.Va action -is set to -.Dv PF_GET_CLR_CNTR , -the per-rule statistics on the requested rule are cleared. -.It Dv DIOCGETADDRS Fa "struct pfioc_pooladdr *pp" -Get a -.Va ticket -for subsequent -.Dv DIOCGETADDR -calls and the number -.Va nr -of pool addresses in the rule specified with -.Va r_action , -.Va r_num , -and -.Va anchor . -.It Dv DIOCGETADDR Fa "struct pfioc_pooladdr *pp" -Get the pool address -.Va addr -by its number -.Va nr -from the rule specified with -.Va r_action , -.Va r_num , -and -.Va anchor -using the -.Va ticket -obtained through a preceding -.Dv DIOCGETADDRS -call. -.It Dv DIOCGETALTQS Fa "struct pfioc_altq *pa" -Get a -.Va ticket -for subsequent -.Dv DIOCGETALTQ -calls and the number -.Va nr -of queues in the active list. -.It Dv DIOCGETALTQ Fa "struct pfioc_altq *pa" -Get the queueing discipline -.Va altq -by its number -.Va nr -using the -.Va ticket -obtained through a preceding -.Dv DIOCGETALTQS -call. -.It Dv DIOCGETQSTATS Fa "struct pfioc_qstats *pq" -Get the statistics on a queue. -.Bd -literal -struct pfioc_qstats { - u_int32_t ticket; - u_int32_t nr; - void *buf; - int nbytes; - u_int8_t scheduler; -}; -.Ed -.Pp -This call fills in a pointer to the buffer of statistics -.Va buf , -of length -.Va nbytes , -for the queue specified by -.Va nr . -.It Dv DIOCGETRULESETS Fa "struct pfioc_ruleset *pr" -.Bd -literal -struct pfioc_ruleset { - u_int32_t nr; - char path[MAXPATHLEN]; - char name[PF_ANCHOR_NAME_SIZE]; -}; -.Ed -.Pp -Get the number -.Va nr -of rulesets (i.e., anchors) directly attached to the anchor named by -.Va path -for use in subsequent -.Dv DIOCGETRULESET -calls. -Nested anchors, since they are not directly attached to the given -anchor, will not be included. -This ioctl returns -.Er EINVAL -if the given anchor does not exist. -.It Dv DIOCGETRULESET Fa "struct pfioc_ruleset *pr" -Get a ruleset (i.e., an anchor) -.Va name -by its number -.Va nr -from the given anchor -.Va path , -the maximum number of which can be obtained from a preceding -.Dv DIOCGETRULESETS -call. -This ioctl returns -.Er EINVAL -if the given anchor does not exist or -.Er EBUSY -if another process is concurrently updating a ruleset. -.It Dv DIOCADDSTATE Fa "struct pfioc_state *ps" -Add a state entry. -.Bd -literal -struct pfioc_state { - struct pfsync_state state; -}; -.Ed -.It Dv DIOCGETSTATE Fa "struct pfioc_state *ps" -Extract the entry identified by the -.Va id -and -.Va creatorid -fields of the -.Va state -structure from the state table. -.It Dv DIOCKILLSTATES Fa "struct pfioc_state_kill *psk" -Remove matching entries from the state table. -This ioctl returns the number of killed states in -.Va psk_killed . -.Bd -literal -struct pfioc_state_kill { - struct pf_state_cmp psk_pfcmp; - sa_family_t psk_af; - int psk_proto; - struct pf_rule_addr psk_src; - struct pf_rule_addr psk_dst; - char psk_ifname[IFNAMSIZ]; - char psk_label[PF_RULE_LABEL_SIZE]; - u_int psk_killed; -}; -.Ed -.It Dv DIOCCLRSTATES Fa "struct pfioc_state_kill *psk" -Clear all states. -It works like -.Dv DIOCKILLSTATES , -but ignores the -.Va psk_af , -.Va psk_proto , -.Va psk_src , -and -.Va psk_dst -fields of the -.Vt pfioc_state_kill -structure. -.It Dv DIOCSETSTATUSIF Fa "struct pfioc_if *pi" -Specify the interface for which statistics are accumulated. -.Bd -literal -struct pfioc_if { - char ifname[IFNAMSIZ]; -}; -.Ed -.It Dv DIOCGETSTATUS Fa "struct pf_status *s" -Get the internal packet filter statistics. -.Bd -literal -struct pf_status { - u_int64_t counters[PFRES_MAX]; - u_int64_t lcounters[LCNT_MAX]; - u_int64_t fcounters[FCNT_MAX]; - u_int64_t scounters[SCNT_MAX]; - u_int64_t pcounters[2][2][3]; - u_int64_t bcounters[2][2]; - u_int32_t running; - u_int32_t states; - u_int32_t src_nodes; - u_int32_t since; - u_int32_t debug; - u_int32_t hostid; - char ifname[IFNAMSIZ]; - u_int8_t pf_chksum[MD5_DIGEST_LENGTH]; -}; -.Ed -.It Dv DIOCCLRSTATUS -Clear the internal packet filter statistics. -.It Dv DIOCNATLOOK Fa "struct pfioc_natlook *pnl" -Look up a state table entry by source and destination addresses and ports. -.Bd -literal -struct pfioc_natlook { - struct pf_addr saddr; - struct pf_addr daddr; - struct pf_addr rsaddr; - struct pf_addr rdaddr; - u_int16_t sport; - u_int16_t dport; - u_int16_t rsport; - u_int16_t rdport; - sa_family_t af; - u_int8_t proto; - u_int8_t direction; -}; -.Ed -.It Dv DIOCSETDEBUG Fa "u_int32_t *level" -Set the debug level. -.Bd -literal -enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, - PF_DEBUG_NOISY }; -.Ed -.It Dv DIOCGETSTATES Fa "struct pfioc_states *ps" -Get state table entries. -.Bd -literal -struct pfioc_states { - int ps_len; - union { - caddr_t psu_buf; - struct pf_state *psu_states; - } ps_u; -#define ps_buf ps_u.psu_buf -#define ps_states ps_u.psu_states -}; -.Ed -.Pp -If -.Va ps_len -is non-zero on entry, as many states as possible that can fit into this -size will be copied into the supplied buffer -.Va ps_states . -On exit, -.Va ps_len -is always set to the total size required to hold all state table entries -(i.e., it is set to -.Li sizeof(struct pf_state) * nr ) . -.It Dv DIOCCHANGERULE Fa "struct pfioc_rule *pcr" -Add or remove the -.Va rule -in the ruleset specified by -.Va rule.action . -.Pp -The type of operation to be performed is indicated by -.Va action , -which can be any of the following: -.Bd -literal -enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, - PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, - PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; -.Ed -.Pp -.Va ticket -must be set to the value obtained with -.Dv PF_CHANGE_GET_TICKET -for all actions except -.Dv PF_CHANGE_GET_TICKET . -.Va pool_ticket -must be set to the value obtained with the -.Dv DIOCBEGINADDRS -call for all actions except -.Dv PF_CHANGE_REMOVE -and -.Dv PF_CHANGE_GET_TICKET . -.Va anchor -indicates to which anchor the operation applies. -.Va nr -indicates the rule number against which -.Dv PF_CHANGE_ADD_BEFORE , -.Dv PF_CHANGE_ADD_AFTER , -or -.Dv PF_CHANGE_REMOVE -actions are applied. -.\" It Dv DIOCCHANGEALTQ Fa "struct pfioc_altq *pcr" -.It Dv DIOCCHANGEADDR Fa "struct pfioc_pooladdr *pca" -Add or remove the pool address -.Va addr -from the rule specified by -.Va r_action , -.Va r_num , -and -.Va anchor . -.It Dv DIOCSETTIMEOUT Fa "struct pfioc_tm *pt" -.Bd -literal -struct pfioc_tm { - int timeout; - int seconds; -}; -.Ed -.Pp -Set the state timeout of -.Va timeout -to -.Va seconds . -The old value will be placed into -.Va seconds . -For possible values of -.Va timeout , -consult the -.Dv PFTM_* -values in -.Aq Pa net/pfvar.h . -.It Dv DIOCGETTIMEOUT Fa "struct pfioc_tm *pt" -Get the state timeout of -.Va timeout . -The value will be placed into the -.Va seconds -field. -.It Dv DIOCCLRRULECTRS -Clear per-rule statistics. -.It Dv DIOCSETLIMIT Fa "struct pfioc_limit *pl" -Set the hard limits on the memory pools used by the packet filter. -.Bd -literal -struct pfioc_limit { - int index; - unsigned limit; -}; - -enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; -.Ed -.It Dv DIOCGETLIMIT Fa "struct pfioc_limit *pl" -Get the hard -.Va limit -for the memory pool indicated by -.Va index . -.It Dv DIOCRCLRTABLES Fa "struct pfioc_table *io" -Clear all tables. -All the ioctls that manipulate radix tables -use the same structure described below. -For -.Dv DIOCRCLRTABLES , -.Va pfrio_ndel -contains on exit the number of tables deleted. -.Bd -literal -struct pfioc_table { - struct pfr_table pfrio_table; - void *pfrio_buffer; - int pfrio_esize; - int pfrio_size; - int pfrio_size2; - int pfrio_nadd; - int pfrio_ndel; - int pfrio_nchange; - int pfrio_flags; - u_int32_t pfrio_ticket; -}; -#define pfrio_exists pfrio_nadd -#define pfrio_nzero pfrio_nadd -#define pfrio_nmatch pfrio_nadd -#define pfrio_naddr pfrio_size2 -#define pfrio_setflag pfrio_size2 -#define pfrio_clrflag pfrio_nadd -.Ed -.It Dv DIOCRADDTABLES Fa "struct pfioc_table *io" -Create one or more tables. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_table . -On exit, -.Va pfrio_nadd -contains the number of tables effectively created. -.Bd -literal -struct pfr_table { - char pfrt_anchor[MAXPATHLEN]; - char pfrt_name[PF_TABLE_NAME_SIZE]; - u_int32_t pfrt_flags; - u_int8_t pfrt_fback; -}; -.Ed -.It Dv DIOCRDELTABLES Fa "struct pfioc_table *io" -Delete one or more tables. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_table . -On exit, -.Va pfrio_ndel -contains the number of tables effectively deleted. -.It Dv DIOCRGETTABLES Fa "struct pfioc_table *io" -Get the list of all tables. -On entry, -.Va pfrio_buffer[pfrio_size] -contains a valid writeable buffer for -.Vt pfr_table -structures. -On exit, -.Va pfrio_size -contains the number of tables written into the buffer. -If the buffer is too small, the kernel does not store anything but just -returns the required buffer size, without error. -.It Dv DIOCRGETTSTATS Fa "struct pfioc_table *io" -This call is like -.Dv DIOCRGETTABLES -but is used to get an array of -.Vt pfr_tstats -structures. -.Bd -literal -struct pfr_tstats { - struct pfr_table pfrts_t; - u_int64_t pfrts_packets - [PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_bytes - [PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_match; - u_int64_t pfrts_nomatch; - long pfrts_tzero; - int pfrts_cnt; - int pfrts_refcnt[PFR_REFCNT_MAX]; -}; -#define pfrts_name pfrts_t.pfrt_name -#define pfrts_flags pfrts_t.pfrt_flags -.Ed -.It Dv DIOCRCLRTSTATS Fa "struct pfioc_table *io" -Clear the statistics of one or more tables. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_table . -On exit, -.Va pfrio_nzero -contains the number of tables effectively cleared. -.It Dv DIOCRCLRADDRS Fa "struct pfioc_table *io" -Clear all addresses in a table. -On entry, -.Va pfrio_table -contains the table to clear. -On exit, -.Va pfrio_ndel -contains the number of addresses removed. -.It Dv DIOCRADDADDRS Fa "struct pfioc_table *io" -Add one or more addresses to a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements to add to the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, -.Va pfrio_nadd -contains the number of addresses effectively added. -.Bd -literal -struct pfr_addr { - union { - struct in_addr _pfra_ip4addr; - struct in6_addr _pfra_ip6addr; - } pfra_u; - u_int8_t pfra_af; - u_int8_t pfra_net; - u_int8_t pfra_not; - u_int8_t pfra_fback; -}; -#define pfra_ip4addr pfra_u._pfra_ip4addr -#define pfra_ip6addr pfra_u._pfra_ip6addr -.Ed -.It Dv DIOCRDELADDRS Fa "struct pfioc_table *io" -Delete one or more addresses from a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements to delete from the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, -.Va pfrio_ndel -contains the number of addresses effectively deleted. -.It Dv DIOCRSETADDRS Fa "struct pfioc_table *io" -Replace the content of a table by a new address list. -This is the most complicated command, which uses all the structure members. -.Pp -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements which become the new contents of the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -Additionally, if -.Va pfrio_size2 -is non-zero, -.Va pfrio_buffer[pfrio_size..pfrio_size2] -must be a writeable buffer, into which the kernel can copy the -addresses that have been deleted during the replace operation. -On exit, -.Va pfrio_ndel , -.Va pfrio_nadd , -and -.Va pfrio_nchange -contain the number of addresses deleted, added, and changed by the -kernel. -If -.Va pfrio_size2 -was set on entry, -.Va pfrio_size2 -will point to the size of the buffer used, exactly like -.Dv DIOCRGETADDRS . -.It Dv DIOCRGETADDRS Fa "struct pfioc_table *io" -Get all the addresses of a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer[pfrio_size] -contains a valid writeable buffer for -.Vt pfr_addr -structures. -On exit, -.Va pfrio_size -contains the number of addresses written into the buffer. -If the buffer was too small, the kernel does not store anything but just -returns the required buffer size, without returning an error. -.It Dv DIOCRGETASTATS Fa "struct pfioc_table *io" -This call is like -.Dv DIOCRGETADDRS -but is used to get an array of -.Vt pfr_astats -structures. -.Bd -literal -struct pfr_astats { - struct pfr_addr pfras_a; - u_int64_t pfras_packets - [PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfras_bytes - [PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - long pfras_tzero; -}; -.Ed -.It Dv DIOCRCLRASTATS Fa "struct pfioc_table *io" -Clear the statistics of one or more addresses. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements to be cleared from the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, -.Va pfrio_nzero -contains the number of addresses effectively cleared. -.It Dv DIOCRTSTADDRS Fa "struct pfioc_table *io" -Test if the given addresses match a table. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_addr -containing at least -.Vt pfrio_size -elements, each of which will be tested for a match in the table. -.Vt pfrio_esize -must be the size of -.Vt struct pfr_addr . -On exit, the kernel updates the -.Vt pfr_addr -array by setting the -.Va pfra_fback -member appropriately. -.It Dv DIOCRSETTFLAGS Fa "struct pfioc_table *io" -Change the -.Dv PFR_TFLAG_CONST -or -.Dv PFR_TFLAG_PERSIST -flags of a table. -On entry, -.Va pfrio_buffer -must point to an array of -.Vt struct pfr_table -containing at least -.Vt pfrio_size -elements. -.Va pfrio_esize -must be the size of -.Vt struct pfr_table . -.Va pfrio_setflag -must contain the flags to add, while -.Va pfrio_clrflag -must contain the flags to remove. -On exit, -.Va pfrio_nchange -and -.Va pfrio_ndel -contain the number of tables altered or deleted by the kernel. -Yes, tables can be deleted if one removes the -.Dv PFR_TFLAG_PERSIST -flag of an unreferenced table. -.It Dv DIOCRINADEFINE Fa "struct pfioc_table *io" -Defines a table in the inactive set. -On entry, -.Va pfrio_table -contains the table ID and -.Va pfrio_buffer[pfrio_size] -contains an array of -.Vt pfr_addr -structures to put in the table. -A valid ticket must also be supplied to -.Va pfrio_ticket . -On exit, -.Va pfrio_nadd -contains 0 if the table was already defined in the inactive list -or 1 if a new table has been created. -.Va pfrio_naddr -contains the number of addresses effectively put in the table. -.It Dv DIOCXBEGIN Fa "struct pfioc_trans *io" -.Bd -literal -struct pfioc_trans { - int size; /* number of elements */ - int esize; /* size of each element in bytes */ - struct pfioc_trans_e { - int rs_num; - char anchor[MAXPATHLEN]; - u_int32_t ticket; - } *array; -}; -.Ed -.Pp -Clear all the inactive rulesets specified in the -.Vt pfioc_trans_e -array. -For each ruleset, a ticket is returned for subsequent "add rule" ioctls, -as well as for the -.Dv DIOCXCOMMIT -and -.Dv DIOCXROLLBACK -calls. -.Pp -Ruleset types, identified by -.Va rs_num , -include the following: -.Pp -.Bl -tag -width PF_RULESET_FILTER -offset ind -compact -.It Dv PF_RULESET_SCRUB -Scrub (packet normalization) rules. -.It Dv PF_RULESET_FILTER -Filter rules. -.It Dv PF_RULESET_NAT -NAT (Network Address Translation) rules. -.It Dv PF_RULESET_BINAT -Bidirectional NAT rules. -.It Dv PF_RULESET_RDR -Redirect rules. -.It Dv PF_RULESET_ALTQ -ALTQ disciplines. -.It Dv PF_RULESET_TABLE -Address tables. -.El -.It Dv DIOCXCOMMIT Fa "struct pfioc_trans *io" -Atomically switch a vector of inactive rulesets to the active rulesets. -This call is implemented as a standard two-phase commit, which will either -fail for all rulesets or completely succeed. -All tickets need to be valid. -This ioctl returns -.Er EBUSY -if another process is concurrently updating some of the same rulesets. -.It Dv DIOCXROLLBACK Fa "struct pfioc_trans *io" -Clean up the kernel by undoing all changes that have taken place on the -inactive rulesets since the last -.Dv DIOCXBEGIN . -.Dv DIOCXROLLBACK -will silently ignore rulesets for which the ticket is invalid. -.It Dv DIOCSETHOSTID Fa "u_int32_t *hostid" -Set the host ID, which is used by -.Xr pfsync 4 -to identify which host created state table entries. -.It Dv DIOCOSFPFLUSH -Flush the passive OS fingerprint table. -.It Dv DIOCOSFPADD Fa "struct pf_osfp_ioctl *io" -.Bd -literal -struct pf_osfp_ioctl { - struct pf_osfp_entry { - SLIST_ENTRY(pf_osfp_entry) fp_entry; - pf_osfp_t fp_os; - char fp_class_nm[PF_OSFP_LEN]; - char fp_version_nm[PF_OSFP_LEN]; - char fp_subtype_nm[PF_OSFP_LEN]; - } fp_os; - pf_tcpopts_t fp_tcpopts; - u_int16_t fp_wsize; - u_int16_t fp_psize; - u_int16_t fp_mss; - u_int16_t fp_flags; - u_int8_t fp_optcnt; - u_int8_t fp_wscale; - u_int8_t fp_ttl; - int fp_getnum; -}; -.Ed -.Pp -Add a passive OS fingerprint to the table. -Set -.Va fp_os.fp_os -to the packed fingerprint, -.Va fp_os.fp_class_nm -to the name of the class (Linux, Windows, etc), -.Va fp_os.fp_version_nm -to the name of the version (NT, 95, 98), and -.Va fp_os.fp_subtype_nm -to the name of the subtype or patchlevel. -The members -.Va fp_mss , -.Va fp_wsize , -.Va fp_psize , -.Va fp_ttl , -.Va fp_optcnt , -and -.Va fp_wscale -are set to the TCP MSS, the TCP window size, the IP length, the IP TTL, -the number of TCP options, and the TCP window scaling constant of the -TCP SYN packet, respectively. -.Pp -The -.Va fp_flags -member is filled according to the -.Aq Pa net/pfvar.h -include file -.Dv PF_OSFP_* -defines. -The -.Va fp_tcpopts -member contains packed TCP options. -Each option uses -.Dv PF_OSFP_TCPOPT_BITS -bits in the packed value. -Options include any of -.Dv PF_OSFP_TCPOPT_NOP , -.Dv PF_OSFP_TCPOPT_SACK , -.Dv PF_OSFP_TCPOPT_WSCALE , -.Dv PF_OSFP_TCPOPT_MSS , -or -.Dv PF_OSFP_TCPOPT_TS . -.Pp -The -.Va fp_getnum -member is not used with this ioctl. -.Pp -The structure's slack space must be zeroed for correct operation; -.Xr memset 3 -the whole structure to zero before filling and sending to the kernel. -.It Dv DIOCOSFPGET Fa "struct pf_osfp_ioctl *io" -Get the passive OS fingerprint number -.Va fp_getnum -from the kernel's fingerprint list. -The rest of the structure members will come back filled. -Get the whole list by repeatedly incrementing the -.Va fp_getnum -number until the ioctl returns -.Er EBUSY . -.It Dv DIOCGETSRCNODES Fa "struct pfioc_src_nodes *psn" -.Bd -literal -struct pfioc_src_nodes { - int psn_len; - union { - caddr_t psu_buf; - struct pf_src_node *psu_src_nodes; - } psn_u; -#define psn_buf psn_u.psu_buf -#define psn_src_nodes psn_u.psu_src_nodes -}; -.Ed -.Pp -Get the list of source nodes kept by sticky addresses and source -tracking. -The ioctl must be called once with -.Va psn_len -set to 0. -If the ioctl returns without error, -.Va psn_len -will be set to the size of the buffer required to hold all the -.Va pf_src_node -structures held in the table. -A buffer of this size should then be allocated, and a pointer to this buffer -placed in -.Va psn_buf . -The ioctl must then be called again to fill this buffer with the actual -source node data. -After that call, -.Va psn_len -will be set to the length of the buffer actually used. -.It Dv DIOCCLRSRCNODES -Clear the tree of source tracking nodes. -.It Dv DIOCIGETIFACES Fa "struct pfioc_iface *io" -Get the list of interfaces and interface drivers known to -.Nm . -All the ioctls that manipulate interfaces -use the same structure described below: -.Bd -literal -struct pfioc_iface { - char pfiio_name[IFNAMSIZ]; - void *pfiio_buffer; - int pfiio_esize; - int pfiio_size; - int pfiio_nzero; - int pfiio_flags; -}; -.Ed -.Pp -If not empty, -.Va pfiio_name -can be used to restrict the search to a specific interface or driver. -.Va pfiio_buffer[pfiio_size] -is the user-supplied buffer for returning the data. -On entry, -.Va pfiio_size -contains the number of -.Vt pfi_kif -entries that can fit into the buffer. -The kernel will replace this value by the real number of entries it wants -to return. -.Va pfiio_esize -should be set to -.Li sizeof(struct pfi_kif) . -.Pp -The data is returned in the -.Vt pfi_kif -structure described below: -.Bd -literal -struct pfi_kif { - RB_ENTRY(pfi_kif) pfik_tree; - char pfik_name[IFNAMSIZ]; - u_int64_t pfik_packets[2][2][2]; - u_int64_t pfik_bytes[2][2][2]; - u_int32_t pfik_tzero; - int pfik_flags; - struct pf_state_tree_lan_ext pfik_lan_ext; - struct pf_state_tree_ext_gwy pfik_ext_gwy; - TAILQ_ENTRY(pfi_kif) pfik_w_states; - void *pfik_ah_cookie; - struct ifnet *pfik_ifp; - struct ifg_group *pfik_group; - int pfik_states; - int pfik_rules; - TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; -}; -.Ed -.It Dv DIOCSETIFFLAG Fa "struct pfioc_iface *io" -Set the user setable flags (described above) of the -.Nm -internal interface description. -The filtering process is the same as for -.Dv DIOCIGETIFACES . -.Bd -literal -#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ -.Ed -.It Dv DIOCCLRIFFLAG Fa "struct pfioc_iface *io" -Works as -.Dv DIOCSETIFFLAG -above but clears the flags. -.It Dv DIOCKILLSRCNODES Fa "struct pfioc_iface *io" -Explicitly remove source tracking nodes. -.El -.Sh FILES -.Bl -tag -width /dev/pf -compact -.It Pa /dev/pf -packet filtering device. -.El -.Sh EXAMPLES -The following example demonstrates how to use the -.Dv DIOCNATLOOK -command to find the internal host/port of a NATed connection: -.Bd -literal -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -u_int32_t -read_address(const char *s) -{ - int a, b, c, d; - - sscanf(s, "%i.%i.%i.%i", &a, &b, &c, &d); - return htonl(a << 24 | b << 16 | c << 8 | d); -} - -void -print_address(u_int32_t a) -{ - a = ntohl(a); - printf("%d.%d.%d.%d", a >> 24 & 255, a >> 16 & 255, - a >> 8 & 255, a & 255); -} - -int -main(int argc, char *argv[]) -{ - struct pfioc_natlook nl; - int dev; - - if (argc != 5) { - printf("%s \\n", - argv[0]); - return 1; - } - - dev = open("/dev/pf", O_RDWR); - if (dev == -1) - err(1, "open(\\"/dev/pf\\") failed"); - - memset(&nl, 0, sizeof(struct pfioc_natlook)); - nl.saddr.v4.s_addr = read_address(argv[1]); - nl.sport = htons(atoi(argv[2])); - nl.daddr.v4.s_addr = read_address(argv[3]); - nl.dport = htons(atoi(argv[4])); - nl.af = AF_INET; - nl.proto = IPPROTO_TCP; - nl.direction = PF_IN; - - if (ioctl(dev, DIOCNATLOOK, &nl)) - err(1, "DIOCNATLOOK"); - - printf("internal host "); - print_address(nl.rsaddr.v4.s_addr); - printf(":%u\\n", ntohs(nl.rsport)); - return 0; -} -.Ed -.Sh SEE ALSO -.Xr ioctl 2 , -.Xr altq 4 , -.Xr if_bridge 4 , -.Xr pflog 4 , -.Xr pflow 4 , -.Xr pfsync 4 , -.Xr pfctl 8 , -.Xr altq 9 -.Sh HISTORY -The -.Nm -packet filtering mechanism first appeared in -.Ox 3.0 -and then -.Fx 5.2 . -.Pp -This implementation matches -.Ox 4.5 . diff --git a/contrib/pf/man/pf.conf.5 b/contrib/pf/man/pf.conf.5 deleted file mode 100644 index fc86111372138..0000000000000 --- a/contrib/pf/man/pf.conf.5 +++ /dev/null @@ -1,3066 +0,0 @@ -.\" $FreeBSD$ -.\" $OpenBSD: pf.conf.5,v 1.406 2009/01/31 19:37:12 sobrado Exp $ -.\" -.\" Copyright (c) 2002, Daniel Hartmeier -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" -.\" - Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" - Redistributions in binary form must reproduce the above -.\" copyright notice, this list of conditions and the following -.\" disclaimer in the documentation and/or other materials provided -.\" with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -.\" POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd June 29 2012 -.Dt PF.CONF 5 -.Os -.Sh NAME -.Nm pf.conf -.Nd packet filter configuration file -.Sh DESCRIPTION -The -.Xr pf 4 -packet filter modifies, drops or passes packets according to rules or -definitions specified in -.Nm pf.conf . -.Sh STATEMENT ORDER -There are seven types of statements in -.Nm pf.conf : -.Bl -tag -width xxxx -.It Cm Macros -User-defined variables may be defined and used later, simplifying -the configuration file. -Macros must be defined before they are referenced in -.Nm pf.conf . -.It Cm Tables -Tables provide a mechanism for increasing the performance and flexibility of -rules with large numbers of source or destination addresses. -.It Cm Options -Options tune the behaviour of the packet filtering engine. -.It Cm Traffic Normalization Li (e.g. Em scrub ) -Traffic normalization protects internal machines against inconsistencies -in Internet protocols and implementations. -.It Cm Queueing -Queueing provides rule-based bandwidth control. -.It Cm Translation Li (Various forms of NAT) -Translation rules specify how addresses are to be mapped or redirected to -other addresses. -.It Cm Packet Filtering -Packet filtering provides rule-based blocking or passing of packets. -.El -.Pp -With the exception of -.Cm macros -and -.Cm tables , -the types of statements should be grouped and appear in -.Nm pf.conf -in the order shown above, as this matches the operation of the underlying -packet filtering engine. -By default -.Xr pfctl 8 -enforces this order (see -.Ar set require-order -below). -.Pp -Comments can be put anywhere in the file using a hash mark -.Pq Sq # , -and extend to the end of the current line. -.Pp -Additional configuration files can be included with the -.Ic include -keyword, for example: -.Bd -literal -offset indent -include "/etc/pf/sub.filter.conf" -.Ed -.Sh MACROS -Macros can be defined that will later be expanded in context. -Macro names must start with a letter, and may contain letters, digits -and underscores. -Macro names may not be reserved words (for example -.Ar pass , -.Ar in , -.Ar out ) . -Macros are not expanded inside quotes. -.Pp -For example, -.Bd -literal -offset indent -ext_if = \&"kue0\&" -all_ifs = \&"{\&" $ext_if lo0 \&"}\&" -pass out on $ext_if from any to any -pass in on $ext_if proto tcp from any to any port 25 -.Ed -.Sh TABLES -Tables are named structures which can hold a collection of addresses and -networks. -Lookups against tables in -.Xr pf 4 -are relatively fast, making a single rule with tables much more efficient, -in terms of -processor usage and memory consumption, than a large number of rules which -differ only in IP address (either created explicitly or automatically by rule -expansion). -.Pp -Tables can be used as the source or destination of filter rules, -.Ar scrub -rules -or -translation rules such as -.Ar nat -or -.Ar rdr -(see below for details on the various rule types). -Tables can also be used for the redirect address of -.Ar nat -and -.Ar rdr -rules and in the routing options of filter rules, but only for -.Ar round-robin -pools. -.Pp -Tables can be defined with any of the following -.Xr pfctl 8 -mechanisms. -As with macros, reserved words may not be used as table names. -.Bl -tag -width "manually" -.It Ar manually -Persistent tables can be manually created with the -.Ar add -or -.Ar replace -option of -.Xr pfctl 8 , -before or after the ruleset has been loaded. -.It Pa pf.conf -Table definitions can be placed directly in this file, and loaded at the -same time as other rules are loaded, atomically. -Table definitions inside -.Nm pf.conf -use the -.Ar table -statement, and are especially useful to define non-persistent tables. -The contents of a pre-existing table defined without a list of addresses -to initialize it is not altered when -.Nm pf.conf -is loaded. -A table initialized with the empty list, -.Li { } , -will be cleared on load. -.El -.Pp -Tables may be defined with the following attributes: -.Bl -tag -width persist -.It Ar persist -The -.Ar persist -flag forces the kernel to keep the table even when no rules refer to it. -If the flag is not set, the kernel will automatically remove the table -when the last rule referring to it is flushed. -.It Ar const -The -.Ar const -flag prevents the user from altering the contents of the table once it -has been created. -Without that flag, -.Xr pfctl 8 -can be used to add or remove addresses from the table at any time, even -when running with -.Xr securelevel 7 -= 2. -.It Ar counters -The -.Ar counters -flag enables per-address packet and byte counters which can be displayed with -.Xr pfctl 8 . -.El -.Pp -For example, -.Bd -literal -offset indent -table \*(Ltprivate\*(Gt const { 10/8, 172.16/12, 192.168/16 } -table \*(Ltbadhosts\*(Gt persist -block on fxp0 from { \*(Ltprivate\*(Gt, \*(Ltbadhosts\*(Gt } to any -.Ed -.Pp -creates a table called private, to hold RFC 1918 private network -blocks, and a table called badhosts, which is initially empty. -A filter rule is set up to block all traffic coming from addresses listed in -either table. -The private table cannot have its contents changed and the badhosts table -will exist even when no active filter rules reference it. -Addresses may later be added to the badhosts table, so that traffic from -these hosts can be blocked by using -.Bd -literal -offset indent -# pfctl -t badhosts -Tadd 204.92.77.111 -.Ed -.Pp -A table can also be initialized with an address list specified in one or more -external files, using the following syntax: -.Bd -literal -offset indent -table \*(Ltspam\*(Gt persist file \&"/etc/spammers\&" file \&"/etc/openrelays\&" -block on fxp0 from \*(Ltspam\*(Gt to any -.Ed -.Pp -The files -.Pa /etc/spammers -and -.Pa /etc/openrelays -list IP addresses, one per line. -Any lines beginning with a # are treated as comments and ignored. -In addition to being specified by IP address, hosts may also be -specified by their hostname. -When the resolver is called to add a hostname to a table, -.Em all -resulting IPv4 and IPv6 addresses are placed into the table. -IP addresses can also be entered in a table by specifying a valid interface -name, a valid interface group or the -.Em self -keyword, in which case all addresses assigned to the interface(s) will be -added to the table. -.Sh OPTIONS -.Xr pf 4 -may be tuned for various situations using the -.Ar set -command. -.Bl -tag -width xxxx -.It Ar set timeout -.Pp -.Bl -tag -width "src.track" -compact -.It Ar interval -Interval between purging expired states and fragments. -.It Ar frag -Seconds before an unassembled fragment is expired. -.It Ar src.track -Length of time to retain a source tracking entry after the last state -expires. -.El -.Pp -When a packet matches a stateful connection, the seconds to live for the -connection will be updated to that of the -.Ar proto.modifier -which corresponds to the connection state. -Each packet which matches this state will reset the TTL. -Tuning these values may improve the performance of the -firewall at the risk of dropping valid idle connections. -.Pp -.Bl -tag -width xxxx -compact -.It Ar tcp.first -The state after the first packet. -.It Ar tcp.opening -The state before the destination host ever sends a packet. -.It Ar tcp.established -The fully established state. -.It Ar tcp.closing -The state after the first FIN has been sent. -.It Ar tcp.finwait -The state after both FINs have been exchanged and the connection is closed. -Some hosts (notably web servers on Solaris) send TCP packets even after closing -the connection. -Increasing -.Ar tcp.finwait -(and possibly -.Ar tcp.closing ) -can prevent blocking of such packets. -.It Ar tcp.closed -The state after one endpoint sends an RST. -.El -.Pp -ICMP and UDP are handled in a fashion similar to TCP, but with a much more -limited set of states: -.Pp -.Bl -tag -width xxxx -compact -.It Ar udp.first -The state after the first packet. -.It Ar udp.single -The state if the source host sends more than one packet but the destination -host has never sent one back. -.It Ar udp.multiple -The state if both hosts have sent packets. -.It Ar icmp.first -The state after the first packet. -.It Ar icmp.error -The state after an ICMP error came back in response to an ICMP packet. -.El -.Pp -Other protocols are handled similarly to UDP: -.Pp -.Bl -tag -width xxxx -compact -.It Ar other.first -.It Ar other.single -.It Ar other.multiple -.El -.Pp -Timeout values can be reduced adaptively as the number of state table -entries grows. -.Pp -.Bl -tag -width xxxx -compact -.It Ar adaptive.start -When the number of state entries exceeds this value, adaptive scaling -begins. -All timeout values are scaled linearly with factor -(adaptive.end - number of states) / (adaptive.end - adaptive.start). -.It Ar adaptive.end -When reaching this number of state entries, all timeout values become -zero, effectively purging all state entries immediately. -This value is used to define the scale factor, it should not actually -be reached (set a lower state limit, see below). -.El -.Pp -Adaptive timeouts are enabled by default, with an adaptive.start value -equal to 60% of the state limit, and an adaptive.end value equal to -120% of the state limit. -They can be disabled by setting both adaptive.start and adaptive.end to 0. -.Pp -The adaptive timeout values can be defined both globally and for each rule. -When used on a per-rule basis, the values relate to the number of -states created by the rule, otherwise to the total number of -states. -.Pp -For example: -.Bd -literal -offset indent -set timeout tcp.first 120 -set timeout tcp.established 86400 -set timeout { adaptive.start 6000, adaptive.end 12000 } -set limit states 10000 -.Ed -.Pp -With 9000 state table entries, the timeout values are scaled to 50% -(tcp.first 60, tcp.established 43200). -.Pp -.It Ar set loginterface -Enable collection of packet and byte count statistics for the given -interface or interface group. -These statistics can be viewed using -.Bd -literal -offset indent -# pfctl -s info -.Ed -.Pp -In this example -.Xr pf 4 -collects statistics on the interface named dc0: -.Bd -literal -offset indent -set loginterface dc0 -.Ed -.Pp -One can disable the loginterface using: -.Bd -literal -offset indent -set loginterface none -.Ed -.Pp -.It Ar set limit -Sets hard limits on the memory pools used by the packet filter. -See -.Xr zone 9 -for an explanation of memory pools. -.Pp -For example, -.Bd -literal -offset indent -set limit states 20000 -.Ed -.Pp -sets the maximum number of entries in the memory pool used by state table -entries (generated by -.Ar pass -rules which do not specify -.Ar no state ) -to 20000. -Using -.Bd -literal -offset indent -set limit frags 20000 -.Ed -.Pp -sets the maximum number of entries in the memory pool used for fragment -reassembly (generated by -.Ar scrub -rules) to 20000. -Using -.Bd -literal -offset indent -set limit src-nodes 2000 -.Ed -.Pp -sets the maximum number of entries in the memory pool used for tracking -source IP addresses (generated by the -.Ar sticky-address -and -.Ar src.track -options) to 2000. -Using -.Bd -literal -offset indent -set limit tables 1000 -set limit table-entries 100000 -.Ed -.Pp -sets limits on the memory pools used by tables. -The first limits the number of tables that can exist to 1000. -The second limits the overall number of addresses that can be stored -in tables to 100000. -.Pp -Various limits can be combined on a single line: -.Bd -literal -offset indent -set limit { states 20000, frags 20000, src-nodes 2000 } -.Ed -.Pp -.It Ar set ruleset-optimization -.Bl -tag -width xxxxxxxx -compact -.It Ar none -Disable the ruleset optimizer. -.It Ar basic -Enable basic ruleset optimization. -This is the default behaviour. -Basic ruleset optimization does four things to improve the -performance of ruleset evaluations: -.Pp -.Bl -enum -compact -.It -remove duplicate rules -.It -remove rules that are a subset of another rule -.It -combine multiple rules into a table when advantageous -.It -re-order the rules to improve evaluation performance -.El -.Pp -.It Ar profile -Uses the currently loaded ruleset as a feedback profile to tailor the -ordering of quick rules to actual network traffic. -.El -.Pp -It is important to note that the ruleset optimizer will modify the ruleset -to improve performance. -A side effect of the ruleset modification is that per-rule accounting -statistics will have different meanings than before. -If per-rule accounting is important for billing purposes or whatnot, -either the ruleset optimizer should not be used or a label field should -be added to all of the accounting rules to act as optimization barriers. -.Pp -Optimization can also be set as a command-line argument to -.Xr pfctl 8 , -overriding the settings in -.Nm . -.It Ar set optimization -Optimize state timeouts for one of the following network environments: -.Pp -.Bl -tag -width xxxx -compact -.It Ar normal -A normal network environment. -Suitable for almost all networks. -.It Ar high-latency -A high-latency environment (such as a satellite connection). -.It Ar satellite -Alias for -.Ar high-latency . -.It Ar aggressive -Aggressively expire connections. -This can greatly reduce the memory usage of the firewall at the cost of -dropping idle connections early. -.It Ar conservative -Extremely conservative settings. -Avoid dropping legitimate connections at the -expense of greater memory utilization (possibly much greater on a busy -network) and slightly increased processor utilization. -.El -.Pp -For example: -.Bd -literal -offset indent -set optimization aggressive -.Ed -.Pp -.It Ar set block-policy -The -.Ar block-policy -option sets the default behaviour for the packet -.Ar block -action: -.Pp -.Bl -tag -width xxxxxxxx -compact -.It Ar drop -Packet is silently dropped. -.It Ar return -A TCP RST is returned for blocked TCP packets, -an ICMP UNREACHABLE is returned for blocked UDP packets, -and all other packets are silently dropped. -.El -.Pp -For example: -.Bd -literal -offset indent -set block-policy return -.Ed -.It Ar set state-policy -The -.Ar state-policy -option sets the default behaviour for states: -.Pp -.Bl -tag -width group-bound -compact -.It Ar if-bound -States are bound to interface. -.It Ar floating -States can match packets on any interfaces (the default). -.El -.Pp -For example: -.Bd -literal -offset indent -set state-policy if-bound -.Ed -.It Ar set state-defaults -The -.Ar state-defaults -option sets the state options for states created from rules -without an explicit -.Ar keep state . -For example: -.Bd -literal -offset indent -set state-defaults pflow, no-sync -.Ed -.It Ar set hostid -The 32-bit -.Ar hostid -identifies this firewall's state table entries to other firewalls -in a -.Xr pfsync 4 -failover cluster. -By default the hostid is set to a pseudo-random value, however it may be -desirable to manually configure it, for example to more easily identify the -source of state table entries. -.Bd -literal -offset indent -set hostid 1 -.Ed -.Pp -The hostid may be specified in either decimal or hexadecimal. -.It Ar set require-order -By default -.Xr pfctl 8 -enforces an ordering of the statement types in the ruleset to: -.Em options , -.Em normalization , -.Em queueing , -.Em translation , -.Em filtering . -Setting this option to -.Ar no -disables this enforcement. -There may be non-trivial and non-obvious implications to an out of -order ruleset. -Consider carefully before disabling the order enforcement. -.It Ar set fingerprints -Load fingerprints of known operating systems from the given filename. -By default fingerprints of known operating systems are automatically -loaded from -.Xr pf.os 5 -in -.Pa /etc -but can be overridden via this option. -Setting this option may leave a small period of time where the fingerprints -referenced by the currently active ruleset are inconsistent until the new -ruleset finishes loading. -.Pp -For example: -.Pp -.Dl set fingerprints \&"/etc/pf.os.devel\&" -.Pp -.It Ar set skip on Aq Ar ifspec -List interfaces for which packets should not be filtered. -Packets passing in or out on such interfaces are passed as if pf was -disabled, i.e. pf does not process them in any way. -This can be useful on loopback and other virtual interfaces, when -packet filtering is not desired and can have unexpected effects. -For example: -.Pp -.Dl set skip on lo0 -.Pp -.It Ar set debug -Set the debug -.Ar level -to one of the following: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Ar none -Don't generate debug messages. -.It Ar urgent -Generate debug messages only for serious errors. -.It Ar misc -Generate debug messages for various errors. -.It Ar loud -Generate debug messages for common conditions. -.El -.El -.Sh TRAFFIC NORMALIZATION -Traffic normalization is used to sanitize packet content in such -a way that there are no ambiguities in packet interpretation on -the receiving side. -The normalizer does IP fragment reassembly to prevent attacks -that confuse intrusion detection systems by sending overlapping -IP fragments. -Packet normalization is invoked with the -.Ar scrub -directive. -.Pp -.Ar scrub -has the following options: -.Bl -tag -width xxxx -.It Ar no-df -Clears the -.Ar dont-fragment -bit from a matching IP packet. -Some operating systems are known to generate fragmented packets with the -.Ar dont-fragment -bit set. -This is particularly true with NFS. -.Ar Scrub -will drop such fragmented -.Ar dont-fragment -packets unless -.Ar no-df -is specified. -.Pp -Unfortunately some operating systems also generate their -.Ar dont-fragment -packets with a zero IP identification field. -Clearing the -.Ar dont-fragment -bit on packets with a zero IP ID may cause deleterious results if an -upstream router later fragments the packet. -Using the -.Ar random-id -modifier (see below) is recommended in combination with the -.Ar no-df -modifier to ensure unique IP identifiers. -.It Ar min-ttl Aq Ar number -Enforces a minimum TTL for matching IP packets. -.It Ar max-mss Aq Ar number -Enforces a maximum MSS for matching TCP packets. -.It Xo Ar set-tos Aq Ar string -.No \*(Ba Aq Ar number -.Xc -Enforces a -.Em TOS -for matching IP packets. -.Em TOS -may be -given as one of -.Ar lowdelay , -.Ar throughput , -.Ar reliability , -or as either hex or decimal. -.It Ar random-id -Replaces the IP identification field with random values to compensate -for predictable values generated by many hosts. -This option only applies to packets that are not fragmented -after the optional fragment reassembly. -.It Ar fragment reassemble -Using -.Ar scrub -rules, fragments can be reassembled by normalization. -In this case, fragments are buffered until they form a complete -packet, and only the completed packet is passed on to the filter. -The advantage is that filter rules have to deal only with complete -packets, and can ignore fragments. -The drawback of caching fragments is the additional memory cost. -But the full reassembly method is the only method that currently works -with NAT. -This is the default behavior of a -.Ar scrub -rule if no fragmentation modifier is supplied. -.It Ar fragment crop -The default fragment reassembly method is expensive, hence the option -to crop is provided. -In this case, -.Xr pf 4 -will track the fragments and cache a small range descriptor. -Duplicate fragments are dropped and overlaps are cropped. -Thus data will only occur once on the wire with ambiguities resolving to -the first occurrence. -Unlike the -.Ar fragment reassemble -modifier, fragments are not buffered, they are passed as soon as they -are received. -The -.Ar fragment crop -reassembly mechanism does not yet work with NAT. -.Pp -.It Ar fragment drop-ovl -This option is similar to the -.Ar fragment crop -modifier except that all overlapping or duplicate fragments will be -dropped, and all further corresponding fragments will be -dropped as well. -.It Ar reassemble tcp -Statefully normalizes TCP connections. -.Ar scrub reassemble tcp -rules may not have the direction (in/out) specified. -.Ar reassemble tcp -performs the following normalizations: -.Pp -.Bl -tag -width timeout -compact -.It ttl -Neither side of the connection is allowed to reduce their IP TTL. -An attacker may send a packet such that it reaches the firewall, affects -the firewall state, and expires before reaching the destination host. -.Ar reassemble tcp -will raise the TTL of all packets back up to the highest value seen on -the connection. -.It timestamp modulation -Modern TCP stacks will send a timestamp on every TCP packet and echo -the other endpoint's timestamp back to them. -Many operating systems will merely start the timestamp at zero when -first booted, and increment it several times a second. -The uptime of the host can be deduced by reading the timestamp and multiplying -by a constant. -Also observing several different timestamps can be used to count hosts -behind a NAT device. -And spoofing TCP packets into a connection requires knowing or guessing -valid timestamps. -Timestamps merely need to be monotonically increasing and not derived off a -guessable base time. -.Ar reassemble tcp -will cause -.Ar scrub -to modulate the TCP timestamps with a random number. -.It extended PAWS checks -There is a problem with TCP on long fat pipes, in that a packet might get -delayed for longer than it takes the connection to wrap its 32-bit sequence -space. -In such an occurrence, the old packet would be indistinguishable from a -new packet and would be accepted as such. -The solution to this is called PAWS: Protection Against Wrapped Sequence -numbers. -It protects against it by making sure the timestamp on each packet does -not go backwards. -.Ar reassemble tcp -also makes sure the timestamp on the packet does not go forward more -than the RFC allows. -By doing this, -.Xr pf 4 -artificially extends the security of TCP sequence numbers by 10 to 18 -bits when the host uses appropriately randomized timestamps, since a -blind attacker would have to guess the timestamp as well. -.El -.El -.Pp -For example, -.Bd -literal -offset indent -scrub in on $ext_if all fragment reassemble -.Ed -.Pp -The -.Ar no -option prefixed to a scrub rule causes matching packets to remain unscrubbed, -much in the same way as -.Ar drop quick -works in the packet filter (see below). -This mechanism should be used when it is necessary to exclude specific packets -from broader scrub rules. -.Sh QUEUEING -The ALTQ system is currently not available in the GENERIC kernel nor as -loadable modules. -In order to use the herein after called queueing options one has to use a -custom built kernel. -Please refer to -.Xr altq 4 -to learn about the related kernel options. -.Pp -Packets can be assigned to queues for the purpose of bandwidth -control. -At least two declarations are required to configure queues, and later -any packet filtering rule can reference the defined queues by name. -During the filtering component of -.Nm pf.conf , -the last referenced -.Ar queue -name is where any packets from -.Ar pass -rules will be queued, while for -.Ar block -rules it specifies where any resulting ICMP or TCP RST -packets should be queued. -The -.Ar scheduler -defines the algorithm used to decide which packets get delayed, dropped, or -sent out immediately. -There are three -.Ar schedulers -currently supported. -.Bl -tag -width xxxx -.It Ar cbq -Class Based Queueing. -.Ar Queues -attached to an interface build a tree, thus each -.Ar queue -can have further child -.Ar queues . -Each queue can have a -.Ar priority -and a -.Ar bandwidth -assigned. -.Ar Priority -mainly controls the time packets take to get sent out, while -.Ar bandwidth -has primarily effects on throughput. -.Ar cbq -achieves both partitioning and sharing of link bandwidth -by hierarchically structured classes. -Each class has its own -.Ar queue -and is assigned its share of -.Ar bandwidth . -A child class can borrow bandwidth from its parent class -as long as excess bandwidth is available -(see the option -.Ar borrow , -below). -.It Ar priq -Priority Queueing. -.Ar Queues -are flat attached to the interface, thus, -.Ar queues -cannot have further child -.Ar queues . -Each -.Ar queue -has a unique -.Ar priority -assigned, ranging from 0 to 15. -Packets in the -.Ar queue -with the highest -.Ar priority -are processed first. -.It Ar hfsc -Hierarchical Fair Service Curve. -.Ar Queues -attached to an interface build a tree, thus each -.Ar queue -can have further child -.Ar queues . -Each queue can have a -.Ar priority -and a -.Ar bandwidth -assigned. -.Ar Priority -mainly controls the time packets take to get sent out, while -.Ar bandwidth -primarily affects throughput. -.Ar hfsc -supports both link-sharing and guaranteed real-time services. -It employs a service curve based QoS model, -and its unique feature is an ability to decouple -.Ar delay -and -.Ar bandwidth -allocation. -.El -.Pp -The interfaces on which queueing should be activated are declared using -the -.Ar altq on -declaration. -.Ar altq on -has the following keywords: -.Bl -tag -width xxxx -.It Aq Ar interface -Queueing is enabled on the named interface. -.It Aq Ar scheduler -Specifies which queueing scheduler to use. -Currently supported values -are -.Ar cbq -for Class Based Queueing, -.Ar priq -for Priority Queueing and -.Ar hfsc -for the Hierarchical Fair Service Curve scheduler. -.It Ar bandwidth Aq Ar bw -The maximum bitrate for all queues on an -interface may be specified using the -.Ar bandwidth -keyword. -The value can be specified as an absolute value or as a -percentage of the interface bandwidth. -When using an absolute value, the suffixes -.Ar b , -.Ar Kb , -.Ar Mb , -and -.Ar Gb -are used to represent bits, kilobits, megabits, and -gigabits per second, respectively. -The value must not exceed the interface bandwidth. -If -.Ar bandwidth -is not specified, the interface bandwidth is used -(but take note that some interfaces do not know their bandwidth, -or can adapt their bandwidth rates). -.It Ar qlimit Aq Ar limit -The maximum number of packets held in the queue. -The default is 50. -.It Ar tbrsize Aq Ar size -Adjusts the size, in bytes, of the token bucket regulator. -If not specified, heuristics based on the -interface bandwidth are used to determine the size. -.It Ar queue Aq Ar list -Defines a list of subqueues to create on an interface. -.El -.Pp -In the following example, the interface dc0 -should queue up to 5Mbps in four second-level queues using -Class Based Queueing. -Those four queues will be shown in a later example. -.Bd -literal -offset indent -altq on dc0 cbq bandwidth 5Mb queue { std, http, mail, ssh } -.Ed -.Pp -Once interfaces are activated for queueing using the -.Ar altq -directive, a sequence of -.Ar queue -directives may be defined. -The name associated with a -.Ar queue -must match a queue defined in the -.Ar altq -directive (e.g. mail), or, except for the -.Ar priq -.Ar scheduler , -in a parent -.Ar queue -declaration. -The following keywords can be used: -.Bl -tag -width xxxx -.It Ar on Aq Ar interface -Specifies the interface the queue operates on. -If not given, it operates on all matching interfaces. -.It Ar bandwidth Aq Ar bw -Specifies the maximum bitrate to be processed by the queue. -This value must not exceed the value of the parent -.Ar queue -and can be specified as an absolute value or a percentage of the parent -queue's bandwidth. -If not specified, defaults to 100% of the parent queue's bandwidth. -The -.Ar priq -scheduler does not support bandwidth specification. -.It Ar priority Aq Ar level -Between queues a priority level can be set. -For -.Ar cbq -and -.Ar hfsc , -the range is 0 to 7 and for -.Ar priq , -the range is 0 to 15. -The default for all is 1. -.Ar Priq -queues with a higher priority are always served first. -.Ar Cbq -and -.Ar Hfsc -queues with a higher priority are preferred in the case of overload. -.It Ar qlimit Aq Ar limit -The maximum number of packets held in the queue. -The default is 50. -.El -.Pp -The -.Ar scheduler -can get additional parameters with -.Xo Aq Ar scheduler -.Pf ( Aq Ar parameters ) . -.Xc -Parameters are as follows: -.Bl -tag -width Fl -.It Ar default -Packets not matched by another queue are assigned to this one. -Exactly one default queue is required. -.It Ar red -Enable RED (Random Early Detection) on this queue. -RED drops packets with a probability proportional to the average -queue length. -.It Ar rio -Enables RIO on this queue. -RIO is RED with IN/OUT, thus running -RED two times more than RIO would achieve the same effect. -RIO is currently not supported in the GENERIC kernel. -.It Ar ecn -Enables ECN (Explicit Congestion Notification) on this queue. -ECN implies RED. -.El -.Pp -The -.Ar cbq -.Ar scheduler -supports an additional option: -.Bl -tag -width Fl -.It Ar borrow -The queue can borrow bandwidth from the parent. -.El -.Pp -The -.Ar hfsc -.Ar scheduler -supports some additional options: -.Bl -tag -width Fl -.It Ar realtime Aq Ar sc -The minimum required bandwidth for the queue. -.It Ar upperlimit Aq Ar sc -The maximum allowed bandwidth for the queue. -.It Ar linkshare Aq Ar sc -The bandwidth share of a backlogged queue. -.El -.Pp -.Aq Ar sc -is an acronym for -.Ar service curve . -.Pp -The format for service curve specifications is -.Ar ( m1 , d , m2 ) . -.Ar m2 -controls the bandwidth assigned to the queue. -.Ar m1 -and -.Ar d -are optional and can be used to control the initial bandwidth assignment. -For the first -.Ar d -milliseconds the queue gets the bandwidth given as -.Ar m1 , -afterwards the value given in -.Ar m2 . -.Pp -Furthermore, with -.Ar cbq -and -.Ar hfsc , -child queues can be specified as in an -.Ar altq -declaration, thus building a tree of queues using a part of -their parent's bandwidth. -.Pp -Packets can be assigned to queues based on filter rules by using the -.Ar queue -keyword. -Normally only one -.Ar queue -is specified; when a second one is specified it will instead be used for -packets which have a -.Em TOS -of -.Em lowdelay -and for TCP ACKs with no data payload. -.Pp -To continue the previous example, the examples below would specify the -four referenced -queues, plus a few child queues. -Interactive -.Xr ssh 1 -sessions get priority over bulk transfers like -.Xr scp 1 -and -.Xr sftp 1 . -The queues may then be referenced by filtering rules (see -.Sx PACKET FILTERING -below). -.Bd -literal -queue std bandwidth 10% cbq(default) -queue http bandwidth 60% priority 2 cbq(borrow red) \e - { employees, developers } -queue developers bandwidth 75% cbq(borrow) -queue employees bandwidth 15% -queue mail bandwidth 10% priority 0 cbq(borrow ecn) -queue ssh bandwidth 20% cbq(borrow) { ssh_interactive, ssh_bulk } -queue ssh_interactive bandwidth 50% priority 7 cbq(borrow) -queue ssh_bulk bandwidth 50% priority 0 cbq(borrow) - -block return out on dc0 inet all queue std -pass out on dc0 inet proto tcp from $developerhosts to any port 80 \e - queue developers -pass out on dc0 inet proto tcp from $employeehosts to any port 80 \e - queue employees -pass out on dc0 inet proto tcp from any to any port 22 \e - queue(ssh_bulk, ssh_interactive) -pass out on dc0 inet proto tcp from any to any port 25 \e - queue mail -.Ed -.Sh TRANSLATION -Translation rules modify either the source or destination address of the -packets associated with a stateful connection. -A stateful connection is automatically created to track packets matching -such a rule as long as they are not blocked by the filtering section of -.Nm pf.conf . -The translation engine modifies the specified address and/or port in the -packet, recalculates IP, TCP and UDP checksums as necessary, and passes it to -the packet filter for evaluation. -.Pp -Since translation occurs before filtering the filter -engine will see packets as they look after any -addresses and ports have been translated. -Filter rules will therefore have to filter based on the translated -address and port number. -Packets that match a translation rule are only automatically passed if -the -.Ar pass -modifier is given, otherwise they are -still subject to -.Ar block -and -.Ar pass -rules. -.Pp -The state entry created permits -.Xr pf 4 -to keep track of the original address for traffic associated with that state -and correctly direct return traffic for that connection. -.Pp -Various types of translation are possible with pf: -.Bl -tag -width xxxx -.It Ar binat -A -.Ar binat -rule specifies a bidirectional mapping between an external IP netblock -and an internal IP netblock. -.It Ar nat -A -.Ar nat -rule specifies that IP addresses are to be changed as the packet -traverses the given interface. -This technique allows one or more IP addresses -on the translating host to support network traffic for a larger range of -machines on an "inside" network. -Although in theory any IP address can be used on the inside, it is strongly -recommended that one of the address ranges defined by RFC 1918 be used. -These netblocks are: -.Bd -literal -10.0.0.0 - 10.255.255.255 (all of net 10, i.e., 10/8) -172.16.0.0 - 172.31.255.255 (i.e., 172.16/12) -192.168.0.0 - 192.168.255.255 (i.e., 192.168/16) -.Ed -.It Pa rdr -The packet is redirected to another destination and possibly a -different port. -.Ar rdr -rules can optionally specify port ranges instead of single ports. -rdr ... port 2000:2999 -\*(Gt ... port 4000 -redirects ports 2000 to 2999 (inclusive) to port 4000. -rdr ... port 2000:2999 -\*(Gt ... port 4000:* -redirects port 2000 to 4000, 2001 to 4001, ..., 2999 to 4999. -.El -.Pp -In addition to modifying the address, some translation rules may modify -source or destination ports for -.Xr tcp 4 -or -.Xr udp 4 -connections; implicitly in the case of -.Ar nat -rules and explicitly in the case of -.Ar rdr -rules. -Port numbers are never translated with a -.Ar binat -rule. -.Pp -Evaluation order of the translation rules is dependent on the type -of the translation rules and of the direction of a packet. -.Ar binat -rules are always evaluated first. -Then either the -.Ar rdr -rules are evaluated on an inbound packet or the -.Ar nat -rules on an outbound packet. -Rules of the same type are evaluated in the same order in which they -appear in the ruleset. -The first matching rule decides what action is taken. -.Pp -The -.Ar no -option prefixed to a translation rule causes packets to remain untranslated, -much in the same way as -.Ar drop quick -works in the packet filter (see below). -If no rule matches the packet it is passed to the filter engine unmodified. -.Pp -Translation rules apply only to packets that pass through -the specified interface, and if no interface is specified, -translation is applied to packets on all interfaces. -For instance, redirecting port 80 on an external interface to an internal -web server will only work for connections originating from the outside. -Connections to the address of the external interface from local hosts will -not be redirected, since such packets do not actually pass through the -external interface. -Redirections cannot reflect packets back through the interface they arrive -on, they can only be redirected to hosts connected to different interfaces -or to the firewall itself. -.Pp -Note that redirecting external incoming connections to the loopback -address, as in -.Bd -literal -offset indent -rdr on ne3 inet proto tcp to port smtp -\*(Gt 127.0.0.1 port spamd -.Ed -.Pp -will effectively allow an external host to connect to daemons -bound solely to the loopback address, circumventing the traditional -blocking of such connections on a real interface. -Unless this effect is desired, any of the local non-loopback addresses -should be used as redirection target instead, which allows external -connections only to daemons bound to this address or not bound to -any address. -.Pp -See -.Sx TRANSLATION EXAMPLES -below. -.Sh PACKET FILTERING -.Xr pf 4 -has the ability to -.Ar block -and -.Ar pass -packets based on attributes of their layer 3 (see -.Xr ip 4 -and -.Xr ip6 4 ) -and layer 4 (see -.Xr icmp 4 , -.Xr icmp6 4 , -.Xr tcp 4 , -.Xr udp 4 ) -headers. -In addition, packets may also be -assigned to queues for the purpose of bandwidth control. -.Pp -For each packet processed by the packet filter, the filter rules are -evaluated in sequential order, from first to last. -The last matching rule decides what action is taken. -If no rule matches the packet, the default action is to pass -the packet. -.Pp -The following actions can be used in the filter: -.Bl -tag -width xxxx -.It Ar block -The packet is blocked. -There are a number of ways in which a -.Ar block -rule can behave when blocking a packet. -The default behaviour is to -.Ar drop -packets silently, however this can be overridden or made -explicit either globally, by setting the -.Ar block-policy -option, or on a per-rule basis with one of the following options: -.Pp -.Bl -tag -width xxxx -compact -.It Ar drop -The packet is silently dropped. -.It Ar return-rst -This applies only to -.Xr tcp 4 -packets, and issues a TCP RST which closes the -connection. -.It Ar return-icmp -.It Ar return-icmp6 -This causes ICMP messages to be returned for packets which match the rule. -By default this is an ICMP UNREACHABLE message, however this -can be overridden by specifying a message as a code or number. -.It Ar return -This causes a TCP RST to be returned for -.Xr tcp 4 -packets and an ICMP UNREACHABLE for UDP and other packets. -.El -.Pp -Options returning ICMP packets currently have no effect if -.Xr pf 4 -operates on a -.Xr if_bridge 4 , -as the code to support this feature has not yet been implemented. -.Pp -The simplest mechanism to block everything by default and only pass -packets that match explicit rules is specify a first filter rule of: -.Bd -literal -offset indent -block all -.Ed -.It Ar pass -The packet is passed; -state is created unless the -.Ar no state -option is specified. -.El -.Pp -By default -.Xr pf 4 -filters packets statefully; the first time a packet matches a -.Ar pass -rule, a state entry is created; for subsequent packets the filter checks -whether the packet matches any state. -If it does, the packet is passed without evaluation of any rules. -After the connection is closed or times out, the state entry is automatically -removed. -.Pp -This has several advantages. -For TCP connections, comparing a packet to a state involves checking -its sequence numbers, as well as TCP timestamps if a -.Ar scrub reassemble tcp -rule applies to the connection. -If these values are outside the narrow windows of expected -values, the packet is dropped. -This prevents spoofing attacks, such as when an attacker sends packets with -a fake source address/port but does not know the connection's sequence -numbers. -Similarly, -.Xr pf 4 -knows how to match ICMP replies to states. -For example, -.Bd -literal -offset indent -pass out inet proto icmp all icmp-type echoreq -.Ed -.Pp -allows echo requests (such as those created by -.Xr ping 8 ) -out statefully, and matches incoming echo replies correctly to states. -.Pp -Also, looking up states is usually faster than evaluating rules. -If there are 50 rules, all of them are evaluated sequentially in O(n). -Even with 50000 states, only 16 comparisons are needed to match a -state, since states are stored in a binary search tree that allows -searches in O(log2 n). -.Pp -Furthermore, correct handling of ICMP error messages is critical to -many protocols, particularly TCP. -.Xr pf 4 -matches ICMP error messages to the correct connection, checks them against -connection parameters, and passes them if appropriate. -For example if an ICMP source quench message referring to a stateful TCP -connection arrives, it will be matched to the state and get passed. -.Pp -Finally, state tracking is required for -.Ar nat , binat No and Ar rdr -rules, in order to track address and port translations and reverse the -translation on returning packets. -.Pp -.Xr pf 4 -will also create state for other protocols which are effectively stateless by -nature. -UDP packets are matched to states using only host addresses and ports, -and other protocols are matched to states using only the host addresses. -.Pp -If stateless filtering of individual packets is desired, -the -.Ar no state -keyword can be used to specify that state will not be created -if this is the last matching rule. -A number of parameters can also be set to affect how -.Xr pf 4 -handles state tracking. -See -.Sx STATEFUL TRACKING OPTIONS -below for further details. -.Sh PARAMETERS -The rule parameters specify the packets to which a rule applies. -A packet always comes in on, or goes out through, one interface. -Most parameters are optional. -If a parameter is specified, the rule only applies to packets with -matching attributes. -Certain parameters can be expressed as lists, in which case -.Xr pfctl 8 -generates all needed rule combinations. -.Bl -tag -width xxxx -.It Ar in No or Ar out -This rule applies to incoming or outgoing packets. -If neither -.Ar in -nor -.Ar out -are specified, the rule will match packets in both directions. -.It Ar log -In addition to the action specified, a log message is generated. -Only the packet that establishes the state is logged, -unless the -.Ar no state -option is specified. -The logged packets are sent to a -.Xr pflog 4 -interface, by default -.Ar pflog0 . -This interface is monitored by the -.Xr pflogd 8 -logging daemon, which dumps the logged packets to the file -.Pa /var/log/pflog -in -.Xr pcap 3 -binary format. -.It Ar log (all) -Used to force logging of all packets for a connection. -This is not necessary when -.Ar no state -is explicitly specified. -As with -.Ar log , -packets are logged to -.Xr pflog 4 . -.It Ar log (user) -Logs the -.Ux -user ID of the user that owns the socket and the PID of the process that -has the socket open where the packet is sourced from or destined to -(depending on which socket is local). -This is in addition to the normal information logged. -.Pp -Only the first packet -logged via -.Ar log (all, user) -will have the user credentials logged when using stateful matching. -.It Ar log (to Aq Ar interface ) -Send logs to the specified -.Xr pflog 4 -interface instead of -.Ar pflog0 . -.It Ar quick -If a packet matches a rule which has the -.Ar quick -option set, this rule -is considered the last matching rule, and evaluation of subsequent rules -is skipped. -.It Ar on Aq Ar interface -This rule applies only to packets coming in on, or going out through, this -particular interface or interface group. -For more information on interface groups, -see the -.Ic group -keyword in -.Xr ifconfig 8 . -.It Aq Ar af -This rule applies only to packets of this address family. -Supported values are -.Ar inet -and -.Ar inet6 . -.It Ar proto Aq Ar protocol -This rule applies only to packets of this protocol. -Common protocols are -.Xr icmp 4 , -.Xr icmp6 4 , -.Xr tcp 4 , -and -.Xr udp 4 . -For a list of all the protocol name to number mappings used by -.Xr pfctl 8 , -see the file -.Em /etc/protocols . -.It Xo -.Ar from Aq Ar source -.Ar port Aq Ar source -.Ar os Aq Ar source -.Ar to Aq Ar dest -.Ar port Aq Ar dest -.Xc -This rule applies only to packets with the specified source and destination -addresses and ports. -.Pp -Addresses can be specified in CIDR notation (matching netblocks), as -symbolic host names, interface names or interface group names, or as any -of the following keywords: -.Pp -.Bl -tag -width xxxxxxxxxxxxxx -compact -.It Ar any -Any address. -.It Ar no-route -Any address which is not currently routable. -.It Ar urpf-failed -Any source address that fails a unicast reverse path forwarding (URPF) -check, i.e. packets coming in on an interface other than that which holds -the route back to the packet's source address. -.It Aq Ar table -Any address that matches the given table. -.El -.Pp -Ranges of addresses are specified by using the -.Sq - -operator. -For instance: -.Dq 10.1.1.10 - 10.1.1.12 -means all addresses from 10.1.1.10 to 10.1.1.12, -hence addresses 10.1.1.10, 10.1.1.11, and 10.1.1.12. -.Pp -Interface names and interface group names can have modifiers appended: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Ar :network -Translates to the network(s) attached to the interface. -.It Ar :broadcast -Translates to the interface's broadcast address(es). -.It Ar :peer -Translates to the point-to-point interface's peer address(es). -.It Ar :0 -Do not include interface aliases. -.El -.Pp -Host names may also have the -.Ar :0 -option appended to restrict the name resolution to the first of each -v4 and v6 address found. -.Pp -Host name resolution and interface to address translation are done at -ruleset load-time. -When the address of an interface (or host name) changes (under DHCP or PPP, -for instance), the ruleset must be reloaded for the change to be reflected -in the kernel. -Surrounding the interface name (and optional modifiers) in parentheses -changes this behaviour. -When the interface name is surrounded by parentheses, the rule is -automatically updated whenever the interface changes its address. -The ruleset does not need to be reloaded. -This is especially useful with -.Ar nat . -.Pp -Ports can be specified either by number or by name. -For example, port 80 can be specified as -.Em www . -For a list of all port name to number mappings used by -.Xr pfctl 8 , -see the file -.Pa /etc/services . -.Pp -Ports and ranges of ports are specified by using these operators: -.Bd -literal -offset indent -= (equal) -!= (unequal) -\*(Lt (less than) -\*(Le (less than or equal) -\*(Gt (greater than) -\*(Ge (greater than or equal) -: (range including boundaries) -\*(Gt\*(Lt (range excluding boundaries) -\*(Lt\*(Gt (except range) -.Ed -.Pp -.Sq \*(Gt\*(Lt , -.Sq \*(Lt\*(Gt -and -.Sq \&: -are binary operators (they take two arguments). -For instance: -.Bl -tag -width Fl -.It Ar port 2000:2004 -means -.Sq all ports \*(Ge 2000 and \*(Le 2004 , -hence ports 2000, 2001, 2002, 2003 and 2004. -.It Ar port 2000 \*(Gt\*(Lt 2004 -means -.Sq all ports \*(Gt 2000 and \*(Lt 2004 , -hence ports 2001, 2002 and 2003. -.It Ar port 2000 \*(Lt\*(Gt 2004 -means -.Sq all ports \*(Lt 2000 or \*(Gt 2004 , -hence ports 1-1999 and 2005-65535. -.El -.Pp -The operating system of the source host can be specified in the case of TCP -rules with the -.Ar OS -modifier. -See the -.Sx OPERATING SYSTEM FINGERPRINTING -section for more information. -.Pp -The host, port and OS specifications are optional, as in the following examples: -.Bd -literal -offset indent -pass in all -pass in from any to any -pass in proto tcp from any port \*(Le 1024 to any -pass in proto tcp from any to any port 25 -pass in proto tcp from 10.0.0.0/8 port \*(Gt 1024 \e - to ! 10.1.2.3 port != ssh -pass in proto tcp from any os "OpenBSD" -.Ed -.It Ar all -This is equivalent to "from any to any". -.It Ar group Aq Ar group -Similar to -.Ar user , -this rule only applies to packets of sockets owned by the specified group. -.It Ar user Aq Ar user -This rule only applies to packets of sockets owned by the specified user. -For outgoing connections initiated from the firewall, this is the user -that opened the connection. -For incoming connections to the firewall itself, this is the user that -listens on the destination port. -For forwarded connections, where the firewall is not a connection endpoint, -the user and group are -.Em unknown . -.Pp -All packets, both outgoing and incoming, of one connection are associated -with the same user and group. -Only TCP and UDP packets can be associated with users; for other protocols -these parameters are ignored. -.Pp -User and group refer to the effective (as opposed to the real) IDs, in -case the socket is created by a setuid/setgid process. -User and group IDs are stored when a socket is created; -when a process creates a listening socket as root (for instance, by -binding to a privileged port) and subsequently changes to another -user ID (to drop privileges), the credentials will remain root. -.Pp -User and group IDs can be specified as either numbers or names. -The syntax is similar to the one for ports. -The value -.Em unknown -matches packets of forwarded connections. -.Em unknown -can only be used with the operators -.Cm = -and -.Cm != . -Other constructs like -.Cm user \*(Ge unknown -are invalid. -Forwarded packets with unknown user and group ID match only rules -that explicitly compare against -.Em unknown -with the operators -.Cm = -or -.Cm != . -For instance -.Cm user \*(Ge 0 -does not match forwarded packets. -The following example allows only selected users to open outgoing -connections: -.Bd -literal -offset indent -block out proto { tcp, udp } all -pass out proto { tcp, udp } all user { \*(Lt 1000, dhartmei } -.Ed -.It Xo Ar flags Aq Ar a -.Pf / Ns Aq Ar b -.No \*(Ba / Ns Aq Ar b -.No \*(Ba any -.Xc -This rule only applies to TCP packets that have the flags -.Aq Ar a -set out of set -.Aq Ar b . -Flags not specified in -.Aq Ar b -are ignored. -For stateful connections, the default is -.Ar flags S/SA . -To indicate that flags should not be checked at all, specify -.Ar flags any . -The flags are: (F)IN, (S)YN, (R)ST, (P)USH, (A)CK, (U)RG, (E)CE, and C(W)R. -.Bl -tag -width Fl -.It Ar flags S/S -Flag SYN is set. -The other flags are ignored. -.It Ar flags S/SA -This is the default setting for stateful connections. -Out of SYN and ACK, exactly SYN may be set. -SYN, SYN+PSH and SYN+RST match, but SYN+ACK, ACK and ACK+RST do not. -This is more restrictive than the previous example. -.It Ar flags /SFRA -If the first set is not specified, it defaults to none. -All of SYN, FIN, RST and ACK must be unset. -.El -.Pp -Because -.Ar flags S/SA -is applied by default (unless -.Ar no state -is specified), only the initial SYN packet of a TCP handshake will create -a state for a TCP connection. -It is possible to be less restrictive, and allow state creation from -intermediate -.Pq non-SYN -packets, by specifying -.Ar flags any . -This will cause -.Xr pf 4 -to synchronize to existing connections, for instance -if one flushes the state table. -However, states created from such intermediate packets may be missing -connection details such as the TCP window scaling factor. -States which modify the packet flow, such as those affected by -.Ar nat , binat No or Ar rdr -rules, -.Ar modulate No or Ar synproxy state -options, or scrubbed with -.Ar reassemble tcp -will also not be recoverable from intermediate packets. -Such connections will stall and time out. -.It Xo Ar icmp-type Aq Ar type -.Ar code Aq Ar code -.Xc -.It Xo Ar icmp6-type Aq Ar type -.Ar code Aq Ar code -.Xc -This rule only applies to ICMP or ICMPv6 packets with the specified type -and code. -Text names for ICMP types and codes are listed in -.Xr icmp 4 -and -.Xr icmp6 4 . -This parameter is only valid for rules that cover protocols ICMP or -ICMP6. -The protocol and the ICMP type indicator -.Po -.Ar icmp-type -or -.Ar icmp6-type -.Pc -must match. -.It Xo Ar tos Aq Ar string -.No \*(Ba Aq Ar number -.Xc -This rule applies to packets with the specified -.Em TOS -bits set. -.Em TOS -may be -given as one of -.Ar lowdelay , -.Ar throughput , -.Ar reliability , -or as either hex or decimal. -.Pp -For example, the following rules are identical: -.Bd -literal -offset indent -pass all tos lowdelay -pass all tos 0x10 -pass all tos 16 -.Ed -.It Ar allow-opts -By default, IPv4 packets with IP options or IPv6 packets with routing -extension headers are blocked. -When -.Ar allow-opts -is specified for a -.Ar pass -rule, packets that pass the filter based on that rule (last matching) -do so even if they contain IP options or routing extension headers. -For packets that match state, the rule that initially created the -state is used. -The implicit -.Ar pass -rule that is used when a packet does not match any rules does not -allow IP options. -.It Ar label Aq Ar string -Adds a label (name) to the rule, which can be used to identify the rule. -For instance, -pfctl -s labels -shows per-rule statistics for rules that have labels. -.Pp -The following macros can be used in labels: -.Pp -.Bl -tag -width $srcaddr -compact -offset indent -.It Ar $if -The interface. -.It Ar $srcaddr -The source IP address. -.It Ar $dstaddr -The destination IP address. -.It Ar $srcport -The source port specification. -.It Ar $dstport -The destination port specification. -.It Ar $proto -The protocol name. -.It Ar $nr -The rule number. -.El -.Pp -For example: -.Bd -literal -offset indent -ips = \&"{ 1.2.3.4, 1.2.3.5 }\&" -pass in proto tcp from any to $ips \e - port \*(Gt 1023 label \&"$dstaddr:$dstport\&" -.Ed -.Pp -expands to -.Bd -literal -offset indent -pass in inet proto tcp from any to 1.2.3.4 \e - port \*(Gt 1023 label \&"1.2.3.4:\*(Gt1023\&" -pass in inet proto tcp from any to 1.2.3.5 \e - port \*(Gt 1023 label \&"1.2.3.5:\*(Gt1023\&" -.Ed -.Pp -The macro expansion for the -.Ar label -directive occurs only at configuration file parse time, not during runtime. -.It Xo Ar queue Aq Ar queue -.No \*(Ba ( Aq Ar queue , -.Aq Ar queue ) -.Xc -Packets matching this rule will be assigned to the specified queue. -If two queues are given, packets which have a -.Em TOS -of -.Em lowdelay -and TCP ACKs with no data payload will be assigned to the second one. -See -.Sx QUEUEING -for setup details. -.Pp -For example: -.Bd -literal -offset indent -pass in proto tcp to port 25 queue mail -pass in proto tcp to port 22 queue(ssh_bulk, ssh_prio) -.Ed -.It Ar tag Aq Ar string -Packets matching this rule will be tagged with the -specified string. -The tag acts as an internal marker that can be used to -identify these packets later on. -This can be used, for example, to provide trust between -interfaces and to determine if packets have been -processed by translation rules. -Tags are -.Qq sticky , -meaning that the packet will be tagged even if the rule -is not the last matching rule. -Further matching rules can replace the tag with a -new one but will not remove a previously applied tag. -A packet is only ever assigned one tag at a time. -Packet tagging can be done during -.Ar nat , -.Ar rdr , -or -.Ar binat -rules in addition to filter rules. -Tags take the same macros as labels (see above). -.It Ar tagged Aq Ar string -Used with filter, translation or scrub rules -to specify that packets must already -be tagged with the given tag in order to match the rule. -Inverse tag matching can also be done -by specifying the -.Cm !\& -operator before the -.Ar tagged -keyword. -.It Ar rtable Aq Ar number -Used to select an alternate routing table for the routing lookup. -Only effective before the route lookup happened, i.e. when filtering inbound. -.It Xo Ar divert-to Aq Ar host -.Ar port Aq Ar port -.Xc -Used to redirect packets to a local socket bound to -.Ar host -and -.Ar port . -The packets will not be modified, so -.Xr getsockname 2 -on the socket will return the original destination address of the packet. -.It Ar divert-reply -Used to receive replies for sockets that are bound to addresses -which are not local to the machine. -See -.Xr setsockopt 2 -for information on how to bind these sockets. -.It Ar probability Aq Ar number -A probability attribute can be attached to a rule, with a value set between -0 and 1, bounds not included. -In that case, the rule will be honoured using the given probability value -only. -For example, the following rule will drop 20% of incoming ICMP packets: -.Bd -literal -offset indent -block in proto icmp probability 20% -.Ed -.El -.Sh ROUTING -If a packet matches a rule with a route option set, the packet filter will -route the packet according to the type of route option. -When such a rule creates state, the route option is also applied to all -packets matching the same connection. -.Bl -tag -width xxxx -.It Ar fastroute -The -.Ar fastroute -option does a normal route lookup to find the next hop for the packet. -.It Ar route-to -The -.Ar route-to -option routes the packet to the specified interface with an optional address -for the next hop. -When a -.Ar route-to -rule creates state, only packets that pass in the same direction as the -filter rule specifies will be routed in this way. -Packets passing in the opposite direction (replies) are not affected -and are routed normally. -.It Ar reply-to -The -.Ar reply-to -option is similar to -.Ar route-to , -but routes packets that pass in the opposite direction (replies) to the -specified interface. -Opposite direction is only defined in the context of a state entry, and -.Ar reply-to -is useful only in rules that create state. -It can be used on systems with multiple external connections to -route all outgoing packets of a connection through the interface -the incoming connection arrived through (symmetric routing enforcement). -.It Ar dup-to -The -.Ar dup-to -option creates a duplicate of the packet and routes it like -.Ar route-to . -The original packet gets routed as it normally would. -.El -.Sh POOL OPTIONS -For -.Ar nat -and -.Ar rdr -rules, (as well as for the -.Ar route-to , -.Ar reply-to -and -.Ar dup-to -rule options) for which there is a single redirection address which has a -subnet mask smaller than 32 for IPv4 or 128 for IPv6 (more than one IP -address), a variety of different methods for assigning this address can be -used: -.Bl -tag -width xxxx -.It Ar bitmask -The -.Ar bitmask -option applies the network portion of the redirection address to the address -to be modified (source with -.Ar nat , -destination with -.Ar rdr ) . -.It Ar random -The -.Ar random -option selects an address at random within the defined block of addresses. -.It Ar source-hash -The -.Ar source-hash -option uses a hash of the source address to determine the redirection address, -ensuring that the redirection address is always the same for a given source. -An optional key can be specified after this keyword either in hex or as a -string; by default -.Xr pfctl 8 -randomly generates a key for source-hash every time the -ruleset is reloaded. -.It Ar round-robin -The -.Ar round-robin -option loops through the redirection address(es). -.Pp -When more than one redirection address is specified, -.Ar round-robin -is the only permitted pool type. -.It Ar static-port -With -.Ar nat -rules, the -.Ar static-port -option prevents -.Xr pf 4 -from modifying the source port on TCP and UDP packets. -.El -.Pp -Additionally, the -.Ar sticky-address -option can be specified to help ensure that multiple connections from the -same source are mapped to the same redirection address. -This option can be used with the -.Ar random -and -.Ar round-robin -pool options. -Note that by default these associations are destroyed as soon as there are -no longer states which refer to them; in order to make the mappings last -beyond the lifetime of the states, increase the global options with -.Ar set timeout src.track . -See -.Sx STATEFUL TRACKING OPTIONS -for more ways to control the source tracking. -.Sh STATE MODULATION -Much of the security derived from TCP is attributable to how well the -initial sequence numbers (ISNs) are chosen. -Some popular stack implementations choose -.Em very -poor ISNs and thus are normally susceptible to ISN prediction exploits. -By applying a -.Ar modulate state -rule to a TCP connection, -.Xr pf 4 -will create a high quality random sequence number for each connection -endpoint. -.Pp -The -.Ar modulate state -directive implicitly keeps state on the rule and is -only applicable to TCP connections. -.Pp -For instance: -.Bd -literal -offset indent -block all -pass out proto tcp from any to any modulate state -pass in proto tcp from any to any port 25 flags S/SFRA modulate state -.Ed -.Pp -Note that modulated connections will not recover when the state table -is lost (firewall reboot, flushing the state table, etc...). -.Xr pf 4 -will not be able to infer a connection again after the state table flushes -the connection's modulator. -When the state is lost, the connection may be left dangling until the -respective endpoints time out the connection. -It is possible on a fast local network for the endpoints to start an ACK -storm while trying to resynchronize after the loss of the modulator. -The default -.Ar flags -settings (or a more strict equivalent) should be used on -.Ar modulate state -rules to prevent ACK storms. -.Pp -Note that alternative methods are available -to prevent loss of the state table -and allow for firewall failover. -See -.Xr carp 4 -and -.Xr pfsync 4 -for further information. -.Sh SYN PROXY -By default, -.Xr pf 4 -passes packets that are part of a -.Xr tcp 4 -handshake between the endpoints. -The -.Ar synproxy state -option can be used to cause -.Xr pf 4 -itself to complete the handshake with the active endpoint, perform a handshake -with the passive endpoint, and then forward packets between the endpoints. -.Pp -No packets are sent to the passive endpoint before the active endpoint has -completed the handshake, hence so-called SYN floods with spoofed source -addresses will not reach the passive endpoint, as the sender can't complete the -handshake. -.Pp -The proxy is transparent to both endpoints, they each see a single -connection from/to the other endpoint. -.Xr pf 4 -chooses random initial sequence numbers for both handshakes. -Once the handshakes are completed, the sequence number modulators -(see previous section) are used to translate further packets of the -connection. -.Ar synproxy state -includes -.Ar modulate state . -.Pp -Rules with -.Ar synproxy -will not work if -.Xr pf 4 -operates on a -.Xr bridge 4 . -.Pp -Example: -.Bd -literal -offset indent -pass in proto tcp from any to any port www synproxy state -.Ed -.Sh STATEFUL TRACKING OPTIONS -A number of options related to stateful tracking can be applied on a -per-rule basis. -.Ar keep state , -.Ar modulate state -and -.Ar synproxy state -support these options, and -.Ar keep state -must be specified explicitly to apply options to a rule. -.Pp -.Bl -tag -width xxxx -compact -.It Ar max Aq Ar number -Limits the number of concurrent states the rule may create. -When this limit is reached, further packets that would create -state will not match this rule until existing states time out. -.It Ar no-sync -Prevent state changes for states created by this rule from appearing on the -.Xr pfsync 4 -interface. -.It Xo Aq Ar timeout -.Aq Ar seconds -.Xc -Changes the timeout values used for states created by this rule. -For a list of all valid timeout names, see -.Sx OPTIONS -above. -.It Ar sloppy -Uses a sloppy TCP connection tracker that does not check sequence -numbers at all, which makes insertion and ICMP teardown attacks way -easier. -This is intended to be used in situations where one does not see all -packets of a connection, e.g. in asymmetric routing situations. -Cannot be used with modulate or synproxy state. -.It Ar pflow -States created by this rule are exported on the -.Xr pflow 4 -interface. -.El -.Pp -Multiple options can be specified, separated by commas: -.Bd -literal -offset indent -pass in proto tcp from any to any \e - port www keep state \e - (max 100, source-track rule, max-src-nodes 75, \e - max-src-states 3, tcp.established 60, tcp.closing 5) -.Ed -.Pp -When the -.Ar source-track -keyword is specified, the number of states per source IP is tracked. -.Pp -.Bl -tag -width xxxx -compact -.It Ar source-track rule -The maximum number of states created by this rule is limited by the rule's -.Ar max-src-nodes -and -.Ar max-src-states -options. -Only state entries created by this particular rule count toward the rule's -limits. -.It Ar source-track global -The number of states created by all rules that use this option is limited. -Each rule can specify different -.Ar max-src-nodes -and -.Ar max-src-states -options, however state entries created by any participating rule count towards -each individual rule's limits. -.El -.Pp -The following limits can be set: -.Pp -.Bl -tag -width xxxx -compact -.It Ar max-src-nodes Aq Ar number -Limits the maximum number of source addresses which can simultaneously -have state table entries. -.It Ar max-src-states Aq Ar number -Limits the maximum number of simultaneous state entries that a single -source address can create with this rule. -.El -.Pp -For stateful TCP connections, limits on established connections (connections -which have completed the TCP 3-way handshake) can also be enforced -per source IP. -.Pp -.Bl -tag -width xxxx -compact -.It Ar max-src-conn Aq Ar number -Limits the maximum number of simultaneous TCP connections which have -completed the 3-way handshake that a single host can make. -.It Xo Ar max-src-conn-rate Aq Ar number -.No / Aq Ar seconds -.Xc -Limit the rate of new connections over a time interval. -The connection rate is an approximation calculated as a moving average. -.El -.Pp -Because the 3-way handshake ensures that the source address is not being -spoofed, more aggressive action can be taken based on these limits. -With the -.Ar overload Aq Ar table -state option, source IP addresses which hit either of the limits on -established connections will be added to the named table. -This table can be used in the ruleset to block further activity from -the offending host, redirect it to a tarpit process, or restrict its -bandwidth. -.Pp -The optional -.Ar flush -keyword kills all states created by the matching rule which originate -from the host which exceeds these limits. -The -.Ar global -modifier to the flush command kills all states originating from the -offending host, regardless of which rule created the state. -.Pp -For example, the following rules will protect the webserver against -hosts making more than 100 connections in 10 seconds. -Any host which connects faster than this rate will have its address added -to the -.Aq bad_hosts -table and have all states originating from it flushed. -Any new packets arriving from this host will be dropped unconditionally -by the block rule. -.Bd -literal -offset indent -block quick from \*(Ltbad_hosts\*(Gt -pass in on $ext_if proto tcp to $webserver port www keep state \e - (max-src-conn-rate 100/10, overload \*(Ltbad_hosts\*(Gt flush global) -.Ed -.Sh OPERATING SYSTEM FINGERPRINTING -Passive OS Fingerprinting is a mechanism to inspect nuances of a TCP -connection's initial SYN packet and guess at the host's operating system. -Unfortunately these nuances are easily spoofed by an attacker so the -fingerprint is not useful in making security decisions. -But the fingerprint is typically accurate enough to make policy decisions -upon. -.Pp -The fingerprints may be specified by operating system class, by -version, or by subtype/patchlevel. -The class of an operating system is typically the vendor or genre -and would be -.Ox -for the -.Xr pf 4 -firewall itself. -The version of the oldest available -.Ox -release on the main FTP site -would be 2.6 and the fingerprint would be written -.Pp -.Dl \&"OpenBSD 2.6\&" -.Pp -The subtype of an operating system is typically used to describe the -patchlevel if that patch led to changes in the TCP stack behavior. -In the case of -.Ox , -the only subtype is for a fingerprint that was -normalized by the -.Ar no-df -scrub option and would be specified as -.Pp -.Dl \&"OpenBSD 3.3 no-df\&" -.Pp -Fingerprints for most popular operating systems are provided by -.Xr pf.os 5 . -Once -.Xr pf 4 -is running, a complete list of known operating system fingerprints may -be listed by running: -.Pp -.Dl # pfctl -so -.Pp -Filter rules can enforce policy at any level of operating system specification -assuming a fingerprint is present. -Policy could limit traffic to approved operating systems or even ban traffic -from hosts that aren't at the latest service pack. -.Pp -The -.Ar unknown -class can also be used as the fingerprint which will match packets for -which no operating system fingerprint is known. -.Pp -Examples: -.Bd -literal -offset indent -pass out proto tcp from any os OpenBSD -block out proto tcp from any os Doors -block out proto tcp from any os "Doors PT" -block out proto tcp from any os "Doors PT SP3" -block out from any os "unknown" -pass on lo0 proto tcp from any os "OpenBSD 3.3 lo0" -.Ed -.Pp -Operating system fingerprinting is limited only to the TCP SYN packet. -This means that it will not work on other protocols and will not match -a currently established connection. -.Pp -Caveat: operating system fingerprints are occasionally wrong. -There are three problems: an attacker can trivially craft his packets to -appear as any operating system he chooses; -an operating system patch could change the stack behavior and no fingerprints -will match it until the database is updated; -and multiple operating systems may have the same fingerprint. -.Sh BLOCKING SPOOFED TRAFFIC -"Spoofing" is the faking of IP addresses, typically for malicious -purposes. -The -.Ar antispoof -directive expands to a set of filter rules which will block all -traffic with a source IP from the network(s) directly connected -to the specified interface(s) from entering the system through -any other interface. -.Pp -For example, the line -.Bd -literal -offset indent -antispoof for lo0 -.Ed -.Pp -expands to -.Bd -literal -offset indent -block drop in on ! lo0 inet from 127.0.0.1/8 to any -block drop in on ! lo0 inet6 from ::1 to any -.Ed -.Pp -For non-loopback interfaces, there are additional rules to block incoming -packets with a source IP address identical to the interface's IP(s). -For example, assuming the interface wi0 had an IP address of 10.0.0.1 and a -netmask of 255.255.255.0, -the line -.Bd -literal -offset indent -antispoof for wi0 inet -.Ed -.Pp -expands to -.Bd -literal -offset indent -block drop in on ! wi0 inet from 10.0.0.0/24 to any -block drop in inet from 10.0.0.1 to any -.Ed -.Pp -Caveat: Rules created by the -.Ar antispoof -directive interfere with packets sent over loopback interfaces -to local addresses. -One should pass these explicitly. -.Sh FRAGMENT HANDLING -The size of IP datagrams (packets) can be significantly larger than the -maximum transmission unit (MTU) of the network. -In cases when it is necessary or more efficient to send such large packets, -the large packet will be fragmented into many smaller packets that will each -fit onto the wire. -Unfortunately for a firewalling device, only the first logical fragment will -contain the necessary header information for the subprotocol that allows -.Xr pf 4 -to filter on things such as TCP ports or to perform NAT. -.Pp -Besides the use of -.Ar scrub -rules as described in -.Sx TRAFFIC NORMALIZATION -above, there are three options for handling fragments in the packet filter. -.Pp -One alternative is to filter individual fragments with filter rules. -If no -.Ar scrub -rule applies to a fragment, it is passed to the filter. -Filter rules with matching IP header parameters decide whether the -fragment is passed or blocked, in the same way as complete packets -are filtered. -Without reassembly, fragments can only be filtered based on IP header -fields (source/destination address, protocol), since subprotocol header -fields are not available (TCP/UDP port numbers, ICMP code/type). -The -.Ar fragment -option can be used to restrict filter rules to apply only to -fragments, but not complete packets. -Filter rules without the -.Ar fragment -option still apply to fragments, if they only specify IP header fields. -For instance, the rule -.Bd -literal -offset indent -pass in proto tcp from any to any port 80 -.Ed -.Pp -never applies to a fragment, even if the fragment is part of a TCP -packet with destination port 80, because without reassembly this information -is not available for each fragment. -This also means that fragments cannot create new or match existing -state table entries, which makes stateful filtering and address -translation (NAT, redirection) for fragments impossible. -.Pp -It's also possible to reassemble only certain fragments by specifying -source or destination addresses or protocols as parameters in -.Ar scrub -rules. -.Pp -In most cases, the benefits of reassembly outweigh the additional -memory cost, and it's recommended to use -.Ar scrub -rules to reassemble -all fragments via the -.Ar fragment reassemble -modifier. -.Pp -The memory allocated for fragment caching can be limited using -.Xr pfctl 8 . -Once this limit is reached, fragments that would have to be cached -are dropped until other entries time out. -The timeout value can also be adjusted. -.Pp -Currently, only IPv4 fragments are supported and IPv6 fragments -are blocked unconditionally. -.Sh ANCHORS -Besides the main ruleset, -.Xr pfctl 8 -can load rulesets into -.Ar anchor -attachment points. -An -.Ar anchor -is a container that can hold rules, address tables, and other anchors. -.Pp -An -.Ar anchor -has a name which specifies the path where -.Xr pfctl 8 -can be used to access the anchor to perform operations on it, such as -attaching child anchors to it or loading rules into it. -Anchors may be nested, with components separated by -.Sq / -characters, similar to how file system hierarchies are laid out. -The main ruleset is actually the default anchor, so filter and -translation rules, for example, may also be contained in any anchor. -.Pp -An anchor can reference another -.Ar anchor -attachment point -using the following kinds -of rules: -.Bl -tag -width xxxx -.It Ar nat-anchor Aq Ar name -Evaluates the -.Ar nat -rules in the specified -.Ar anchor . -.It Ar rdr-anchor Aq Ar name -Evaluates the -.Ar rdr -rules in the specified -.Ar anchor . -.It Ar binat-anchor Aq Ar name -Evaluates the -.Ar binat -rules in the specified -.Ar anchor . -.It Ar anchor Aq Ar name -Evaluates the filter rules in the specified -.Ar anchor . -.It Xo Ar load anchor -.Aq Ar name -.Ar from Aq Ar file -.Xc -Loads the rules from the specified file into the -anchor -.Ar name . -.El -.Pp -When evaluation of the main ruleset reaches an -.Ar anchor -rule, -.Xr pf 4 -will proceed to evaluate all rules specified in that anchor. -.Pp -Matching filter and translation rules marked with the -.Ar quick -option are final and abort the evaluation of the rules in other -anchors and the main ruleset. -If the -.Ar anchor -itself is marked with the -.Ar quick -option, -ruleset evaluation will terminate when the anchor is exited if the packet is -matched by any rule within the anchor. -.Pp -.Ar anchor -rules are evaluated relative to the anchor in which they are contained. -For example, all -.Ar anchor -rules specified in the main ruleset will reference anchor -attachment points underneath the main ruleset, and -.Ar anchor -rules specified in a file loaded from a -.Ar load anchor -rule will be attached under that anchor point. -.Pp -Rules may be contained in -.Ar anchor -attachment points which do not contain any rules when the main ruleset -is loaded, and later such anchors can be manipulated through -.Xr pfctl 8 -without reloading the main ruleset or other anchors. -For example, -.Bd -literal -offset indent -ext_if = \&"kue0\&" -block on $ext_if all -anchor spam -pass out on $ext_if all -pass in on $ext_if proto tcp from any \e - to $ext_if port smtp -.Ed -.Pp -blocks all packets on the external interface by default, then evaluates -all rules in the -.Ar anchor -named "spam", and finally passes all outgoing connections and -incoming connections to port 25. -.Bd -literal -offset indent -# echo \&"block in quick from 1.2.3.4 to any\&" \&| \e - pfctl -a spam -f - -.Ed -.Pp -This loads a single rule into the -.Ar anchor , -which blocks all packets from a specific address. -.Pp -The anchor can also be populated by adding a -.Ar load anchor -rule after the -.Ar anchor -rule: -.Bd -literal -offset indent -anchor spam -load anchor spam from "/etc/pf-spam.conf" -.Ed -.Pp -When -.Xr pfctl 8 -loads -.Nm pf.conf , -it will also load all the rules from the file -.Pa /etc/pf-spam.conf -into the anchor. -.Pp -Optionally, -.Ar anchor -rules can specify packet filtering parameters using the same syntax as -filter rules. -When parameters are used, the -.Ar anchor -rule is only evaluated for matching packets. -This allows conditional evaluation of anchors, like: -.Bd -literal -offset indent -block on $ext_if all -anchor spam proto tcp from any to any port smtp -pass out on $ext_if all -pass in on $ext_if proto tcp from any to $ext_if port smtp -.Ed -.Pp -The rules inside -.Ar anchor -spam are only evaluated for -.Ar tcp -packets with destination port 25. -Hence, -.Bd -literal -offset indent -# echo \&"block in quick from 1.2.3.4 to any" \&| \e - pfctl -a spam -f - -.Ed -.Pp -will only block connections from 1.2.3.4 to port 25. -.Pp -Anchors may end with the asterisk -.Pq Sq * -character, which signifies that all anchors attached at that point -should be evaluated in the alphabetical ordering of their anchor name. -For example, -.Bd -literal -offset indent -anchor "spam/*" -.Ed -.Pp -will evaluate each rule in each anchor attached to the -.Li spam -anchor. -Note that it will only evaluate anchors that are directly attached to the -.Li spam -anchor, and will not descend to evaluate anchors recursively. -.Pp -Since anchors are evaluated relative to the anchor in which they are -contained, there is a mechanism for accessing the parent and ancestor -anchors of a given anchor. -Similar to file system path name resolution, if the sequence -.Dq .. -appears as an anchor path component, the parent anchor of the current -anchor in the path evaluation at that point will become the new current -anchor. -As an example, consider the following: -.Bd -literal -offset indent -# echo ' anchor "spam/allowed" ' | pfctl -f - -# echo -e ' anchor "../banned" \en pass' | \e - pfctl -a spam/allowed -f - -.Ed -.Pp -Evaluation of the main ruleset will lead into the -.Li spam/allowed -anchor, which will evaluate the rules in the -.Li spam/banned -anchor, if any, before finally evaluating the -.Ar pass -rule. -.Pp -Filter rule -.Ar anchors -can also be loaded inline in the ruleset within a brace ('{' '}') delimited -block. -Brace delimited blocks may contain rules or other brace-delimited blocks. -When anchors are loaded this way the anchor name becomes optional. -.Bd -literal -offset indent -anchor "external" on egress { - block - anchor out { - pass proto tcp from any to port { 25, 80, 443 } - } - pass in proto tcp to any port 22 -} -.Ed -.Pp -Since the parser specification for anchor names is a string, any -reference to an anchor name containing -.Sq / -characters will require double quote -.Pq Sq \&" -characters around the anchor name. -.Sh TRANSLATION EXAMPLES -This example maps incoming requests on port 80 to port 8080, on -which a daemon is running (because, for example, it is not run as root, -and therefore lacks permission to bind to port 80). -.Bd -literal -# use a macro for the interface name, so it can be changed easily -ext_if = \&"ne3\&" - -# map daemon on 8080 to appear to be on 80 -rdr on $ext_if proto tcp from any to any port 80 -\*(Gt 127.0.0.1 port 8080 -.Ed -.Pp -If the -.Ar pass -modifier is given, packets matching the translation rule are passed without -inspecting the filter rules: -.Bd -literal -rdr pass on $ext_if proto tcp from any to any port 80 -\*(Gt 127.0.0.1 \e - port 8080 -.Ed -.Pp -In the example below, vlan12 is configured as 192.168.168.1; -the machine translates all packets coming from 192.168.168.0/24 to 204.92.77.111 -when they are going out any interface except vlan12. -This has the net effect of making traffic from the 192.168.168.0/24 -network appear as though it is the Internet routable address -204.92.77.111 to nodes behind any interface on the router except -for the nodes on vlan12. -(Thus, 192.168.168.1 can talk to the 192.168.168.0/24 nodes.) -.Bd -literal -nat on ! vlan12 from 192.168.168.0/24 to any -\*(Gt 204.92.77.111 -.Ed -.Pp -In the example below, the machine sits between a fake internal 144.19.74.* -network, and a routable external IP of 204.92.77.100. -The -.Ar no nat -rule excludes protocol AH from being translated. -.Bd -literal -# NO NAT -no nat on $ext_if proto ah from 144.19.74.0/24 to any -nat on $ext_if from 144.19.74.0/24 to any -\*(Gt 204.92.77.100 -.Ed -.Pp -In the example below, packets bound for one specific server, as well as those -generated by the sysadmins are not proxied; all other connections are. -.Bd -literal -# NO RDR -no rdr on $int_if proto { tcp, udp } from any to $server port 80 -no rdr on $int_if proto { tcp, udp } from $sysadmins to any port 80 -rdr on $int_if proto { tcp, udp } from any to any port 80 -\*(Gt 127.0.0.1 \e - port 80 -.Ed -.Pp -This longer example uses both a NAT and a redirection. -The external interface has the address 157.161.48.183. -On localhost, we are running -.Xr ftp-proxy 8 , -waiting for FTP sessions to be redirected to it. -The three mandatory anchors for -.Xr ftp-proxy 8 -are omitted from this example; see the -.Xr ftp-proxy 8 -manpage. -.Bd -literal -# NAT -# Translate outgoing packets' source addresses (any protocol). -# In this case, any address but the gateway's external address is mapped. -nat on $ext_if inet from ! ($ext_if) to any -\*(Gt ($ext_if) - -# NAT PROXYING -# Map outgoing packets' source port to an assigned proxy port instead of -# an arbitrary port. -# In this case, proxy outgoing isakmp with port 500 on the gateway. -nat on $ext_if inet proto udp from any port = isakmp to any -\*(Gt ($ext_if) \e - port 500 - -# BINAT -# Translate outgoing packets' source address (any protocol). -# Translate incoming packets' destination address to an internal machine -# (bidirectional). -binat on $ext_if from 10.1.2.150 to any -\*(Gt $ext_if - -# RDR -# Translate incoming packets' destination addresses. -# As an example, redirect a TCP and UDP port to an internal machine. -rdr on $ext_if inet proto tcp from any to ($ext_if) port 8080 \e - -\*(Gt 10.1.2.151 port 22 -rdr on $ext_if inet proto udp from any to ($ext_if) port 8080 \e - -\*(Gt 10.1.2.151 port 53 - -# RDR -# Translate outgoing ftp control connections to send them to localhost -# for proxying with ftp-proxy(8) running on port 8021. -rdr on $int_if proto tcp from any to any port 21 -\*(Gt 127.0.0.1 port 8021 -.Ed -.Pp -In this example, a NAT gateway is set up to translate internal addresses -using a pool of public addresses (192.0.2.16/28) and to redirect -incoming web server connections to a group of web servers on the internal -network. -.Bd -literal -# NAT LOAD BALANCE -# Translate outgoing packets' source addresses using an address pool. -# A given source address is always translated to the same pool address by -# using the source-hash keyword. -nat on $ext_if inet from any to any -\*(Gt 192.0.2.16/28 source-hash - -# RDR ROUND ROBIN -# Translate incoming web server connections to a group of web servers on -# the internal network. -rdr on $ext_if proto tcp from any to any port 80 \e - -\*(Gt { 10.1.2.155, 10.1.2.160, 10.1.2.161 } round-robin -.Ed -.Sh FILTER EXAMPLES -.Bd -literal -# The external interface is kue0 -# (157.161.48.183, the only routable address) -# and the private network is 10.0.0.0/8, for which we are doing NAT. - -# use a macro for the interface name, so it can be changed easily -ext_if = \&"kue0\&" - -# normalize all incoming traffic -scrub in on $ext_if all fragment reassemble - -# block and log everything by default -block return log on $ext_if all - -# block anything coming from source we have no back routes for -block in from no-route to any - -# block packets whose ingress interface does not match the one in -# the route back to their source address -block in from urpf-failed to any - -# block and log outgoing packets that do not have our address as source, -# they are either spoofed or something is misconfigured (NAT disabled, -# for instance), we want to be nice and do not send out garbage. -block out log quick on $ext_if from ! 157.161.48.183 to any - -# silently drop broadcasts (cable modem noise) -block in quick on $ext_if from any to 255.255.255.255 - -# block and log incoming packets from reserved address space and invalid -# addresses, they are either spoofed or misconfigured, we cannot reply to -# them anyway (hence, no return-rst). -block in log quick on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, \e - 192.168.0.0/16, 255.255.255.255/32 } to any - -# ICMP - -# pass out/in certain ICMP queries and keep state (ping) -# state matching is done on host addresses and ICMP id (not type/code), -# so replies (like 0/0 for 8/0) will match queries -# ICMP error messages (which always refer to a TCP/UDP packet) are -# handled by the TCP/UDP states -pass on $ext_if inet proto icmp all icmp-type 8 code 0 - -# UDP - -# pass out all UDP connections and keep state -pass out on $ext_if proto udp all - -# pass in certain UDP connections and keep state (DNS) -pass in on $ext_if proto udp from any to any port domain - -# TCP - -# pass out all TCP connections and modulate state -pass out on $ext_if proto tcp all modulate state - -# pass in certain TCP connections and keep state (SSH, SMTP, DNS, IDENT) -pass in on $ext_if proto tcp from any to any port { ssh, smtp, domain, \e - auth } - -# Do not allow Windows 9x SMTP connections since they are typically -# a viral worm. Alternately we could limit these OSes to 1 connection each. -block in on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} \e - to any port smtp - -# IPv6 -# pass in/out all IPv6 traffic: note that we have to enable this in two -# different ways, on both our physical interface and our tunnel -pass quick on gif0 inet6 -pass quick on $ext_if proto ipv6 - -# Packet Tagging - -# three interfaces: $int_if, $ext_if, and $wifi_if (wireless). NAT is -# being done on $ext_if for all outgoing packets. tag packets in on -# $int_if and pass those tagged packets out on $ext_if. all other -# outgoing packets (i.e., packets from the wireless network) are only -# permitted to access port 80. - -pass in on $int_if from any to any tag INTNET -pass in on $wifi_if from any to any - -block out on $ext_if from any to any -pass out quick on $ext_if tagged INTNET -pass out on $ext_if proto tcp from any to any port 80 - -# tag incoming packets as they are redirected to spamd(8). use the tag -# to pass those packets through the packet filter. - -rdr on $ext_if inet proto tcp from \*(Ltspammers\*(Gt to port smtp \e - tag SPAMD -\*(Gt 127.0.0.1 port spamd - -block in on $ext_if -pass in on $ext_if inet proto tcp tagged SPAMD -.Ed -.Sh GRAMMAR -Syntax for -.Nm -in BNF: -.Bd -literal -line = ( option | pf-rule | nat-rule | binat-rule | rdr-rule | - antispoof-rule | altq-rule | queue-rule | trans-anchors | - anchor-rule | anchor-close | load-anchor | table-rule | - include ) - -option = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] | - [ "ruleset-optimization" [ "none" | "basic" | "profile" ]] | - [ "optimization" [ "default" | "normal" | - "high-latency" | "satellite" | - "aggressive" | "conservative" ] ] - [ "limit" ( limit-item | "{" limit-list "}" ) ] | - [ "loginterface" ( interface-name | "none" ) ] | - [ "block-policy" ( "drop" | "return" ) ] | - [ "state-policy" ( "if-bound" | "floating" ) ] - [ "state-defaults" state-opts ] - [ "require-order" ( "yes" | "no" ) ] - [ "fingerprints" filename ] | - [ "skip on" ifspec ] | - [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ] ) - -pf-rule = action [ ( "in" | "out" ) ] - [ "log" [ "(" logopts ")"] ] [ "quick" ] - [ "on" ifspec ] [ "fastroute" | route ] [ af ] [ protospec ] - hosts [ filteropt-list ] - -logopts = logopt [ "," logopts ] -logopt = "all" | "user" | "to" interface-name - -filteropt-list = filteropt-list filteropt | filteropt -filteropt = user | group | flags | icmp-type | icmp6-type | "tos" tos | - ( "no" | "keep" | "modulate" | "synproxy" ) "state" - [ "(" state-opts ")" ] | - "fragment" | "no-df" | "min-ttl" number | "set-tos" tos | - "max-mss" number | "random-id" | "reassemble tcp" | - fragmentation | "allow-opts" | - "label" string | "tag" string | [ ! ] "tagged" string | - "queue" ( string | "(" string [ [ "," ] string ] ")" ) | - "rtable" number | "probability" number"%" - -nat-rule = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] - [ "on" ifspec ] [ af ] - [ protospec ] hosts [ "tag" string ] [ "tagged" string ] - [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) - [ portspec ] [ pooltype ] [ "static-port" ] ] - -binat-rule = [ "no" ] "binat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] - [ "on" interface-name ] [ af ] - [ "proto" ( proto-name | proto-number ) ] - "from" address [ "/" mask-bits ] "to" ipspec - [ "tag" string ] [ "tagged" string ] - [ "-\*(Gt" address [ "/" mask-bits ] ] - -rdr-rule = [ "no" ] "rdr" [ "pass" [ "log" [ "(" logopts ")" ] ] ] - [ "on" ifspec ] [ af ] - [ protospec ] hosts [ "tag" string ] [ "tagged" string ] - [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) - [ portspec ] [ pooltype ] ] - -antispoof-rule = "antispoof" [ "log" ] [ "quick" ] - "for" ifspec [ af ] [ "label" string ] - -table-rule = "table" "\*(Lt" string "\*(Gt" [ tableopts-list ] -tableopts-list = tableopts-list tableopts | tableopts -tableopts = "persist" | "const" | "counters" | "file" string | - "{" [ tableaddr-list ] "}" -tableaddr-list = tableaddr-list [ "," ] tableaddr-spec | tableaddr-spec -tableaddr-spec = [ "!" ] tableaddr [ "/" mask-bits ] -tableaddr = hostname | ifspec | "self" | - ipv4-dotted-quad | ipv6-coloned-hex - -altq-rule = "altq on" interface-name queueopts-list - "queue" subqueue -queue-rule = "queue" string [ "on" interface-name ] queueopts-list - subqueue - -anchor-rule = "anchor" [ string ] [ ( "in" | "out" ) ] [ "on" ifspec ] - [ af ] [ protospec ] [ hosts ] [ filteropt-list ] [ "{" ] - -anchor-close = "}" - -trans-anchors = ( "nat-anchor" | "rdr-anchor" | "binat-anchor" ) string - [ "on" ifspec ] [ af ] [ "proto" ] [ protospec ] [ hosts ] - -load-anchor = "load anchor" string "from" filename - -queueopts-list = queueopts-list queueopts | queueopts -queueopts = [ "bandwidth" bandwidth-spec ] | - [ "qlimit" number ] | [ "tbrsize" number ] | - [ "priority" number ] | [ schedulers ] -schedulers = ( cbq-def | priq-def | hfsc-def ) -bandwidth-spec = "number" ( "b" | "Kb" | "Mb" | "Gb" | "%" ) - -action = "pass" | "block" [ return ] | [ "no" ] "scrub" -return = "drop" | "return" | "return-rst" [ "( ttl" number ")" ] | - "return-icmp" [ "(" icmpcode [ [ "," ] icmp6code ] ")" ] | - "return-icmp6" [ "(" icmp6code ")" ] -icmpcode = ( icmp-code-name | icmp-code-number ) -icmp6code = ( icmp6-code-name | icmp6-code-number ) - -ifspec = ( [ "!" ] ( interface-name | interface-group ) ) | - "{" interface-list "}" -interface-list = [ "!" ] ( interface-name | interface-group ) - [ [ "," ] interface-list ] -route = ( "route-to" | "reply-to" | "dup-to" ) - ( routehost | "{" routehost-list "}" ) - [ pooltype ] -af = "inet" | "inet6" - -protospec = "proto" ( proto-name | proto-number | - "{" proto-list "}" ) -proto-list = ( proto-name | proto-number ) [ [ "," ] proto-list ] - -hosts = "all" | - "from" ( "any" | "no-route" | "urpf-failed" | "self" | host | - "{" host-list "}" ) [ port ] [ os ] - "to" ( "any" | "no-route" | "self" | host | - "{" host-list "}" ) [ port ] - -ipspec = "any" | host | "{" host-list "}" -host = [ "!" ] ( address [ "/" mask-bits ] | "\*(Lt" string "\*(Gt" ) -redirhost = address [ "/" mask-bits ] -routehost = "(" interface-name [ address [ "/" mask-bits ] ] ")" -address = ( interface-name | interface-group | - "(" ( interface-name | interface-group ) ")" | - hostname | ipv4-dotted-quad | ipv6-coloned-hex ) -host-list = host [ [ "," ] host-list ] -redirhost-list = redirhost [ [ "," ] redirhost-list ] -routehost-list = routehost [ [ "," ] routehost-list ] - -port = "port" ( unary-op | binary-op | "{" op-list "}" ) -portspec = "port" ( number | name ) [ ":" ( "*" | number | name ) ] -os = "os" ( os-name | "{" os-list "}" ) -user = "user" ( unary-op | binary-op | "{" op-list "}" ) -group = "group" ( unary-op | binary-op | "{" op-list "}" ) - -unary-op = [ "=" | "!=" | "\*(Lt" | "\*(Le" | "\*(Gt" | "\*(Ge" ] - ( name | number ) -binary-op = number ( "\*(Lt\*(Gt" | "\*(Gt\*(Lt" | ":" ) number -op-list = ( unary-op | binary-op ) [ [ "," ] op-list ] - -os-name = operating-system-name -os-list = os-name [ [ "," ] os-list ] - -flags = "flags" ( [ flag-set ] "/" flag-set | "any" ) -flag-set = [ "F" ] [ "S" ] [ "R" ] [ "P" ] [ "A" ] [ "U" ] [ "E" ] - [ "W" ] - -icmp-type = "icmp-type" ( icmp-type-code | "{" icmp-list "}" ) -icmp6-type = "icmp6-type" ( icmp-type-code | "{" icmp-list "}" ) -icmp-type-code = ( icmp-type-name | icmp-type-number ) - [ "code" ( icmp-code-name | icmp-code-number ) ] -icmp-list = icmp-type-code [ [ "," ] icmp-list ] - -tos = ( "lowdelay" | "throughput" | "reliability" | - [ "0x" ] number ) - -state-opts = state-opt [ [ "," ] state-opts ] -state-opt = ( "max" number | "no-sync" | timeout | "sloppy" | "pflow" | - "source-track" [ ( "rule" | "global" ) ] | - "max-src-nodes" number | "max-src-states" number | - "max-src-conn" number | - "max-src-conn-rate" number "/" number | - "overload" "\*(Lt" string "\*(Gt" [ "flush" ] | - "if-bound" | "floating" ) - -fragmentation = [ "fragment reassemble" | "fragment crop" | - "fragment drop-ovl" ] - -timeout-list = timeout [ [ "," ] timeout-list ] -timeout = ( "tcp.first" | "tcp.opening" | "tcp.established" | - "tcp.closing" | "tcp.finwait" | "tcp.closed" | - "udp.first" | "udp.single" | "udp.multiple" | - "icmp.first" | "icmp.error" | - "other.first" | "other.single" | "other.multiple" | - "frag" | "interval" | "src.track" | - "adaptive.start" | "adaptive.end" ) number - -limit-list = limit-item [ [ "," ] limit-list ] -limit-item = ( "states" | "frags" | "src-nodes" ) number - -pooltype = ( "bitmask" | "random" | - "source-hash" [ ( hex-key | string-key ) ] | - "round-robin" ) [ sticky-address ] - -subqueue = string | "{" queue-list "}" -queue-list = string [ [ "," ] string ] -cbq-def = "cbq" [ "(" cbq-opt [ [ "," ] cbq-opt ] ")" ] -priq-def = "priq" [ "(" priq-opt [ [ "," ] priq-opt ] ")" ] -hfsc-def = "hfsc" [ "(" hfsc-opt [ [ "," ] hfsc-opt ] ")" ] -cbq-opt = ( "default" | "borrow" | "red" | "ecn" | "rio" ) -priq-opt = ( "default" | "red" | "ecn" | "rio" ) -hfsc-opt = ( "default" | "red" | "ecn" | "rio" | - linkshare-sc | realtime-sc | upperlimit-sc ) -linkshare-sc = "linkshare" sc-spec -realtime-sc = "realtime" sc-spec -upperlimit-sc = "upperlimit" sc-spec -sc-spec = ( bandwidth-spec | - "(" bandwidth-spec number bandwidth-spec ")" ) -include = "include" filename -.Ed -.Sh FILES -.Bl -tag -width "/etc/protocols" -compact -.It Pa /etc/hosts -Host name database. -.It Pa /etc/pf.conf -Default location of the ruleset file. -.It Pa /etc/pf.os -Default location of OS fingerprints. -.It Pa /etc/protocols -Protocol name database. -.It Pa /etc/services -Service name database. -.El -.Sh SEE ALSO -.Xr altq 4 , -.Xr carp 4 , -.Xr icmp 4 , -.Xr icmp6 4 , -.Xr ip 4 , -.Xr ip6 4 , -.Xr pf 4 , -.Xr pflow 4 , -.Xr pfsync 4 , -.Xr tcp 4 , -.Xr udp 4 , -.Xr hosts 5 , -.Xr pf.os 5 , -.Xr protocols 5 , -.Xr services 5 , -.Xr ftp-proxy 8 , -.Xr pfctl 8 , -.Xr pflogd 8 , -.Sh HISTORY -The -.Nm -file format first appeared in -.Ox 3.0 . diff --git a/contrib/pf/man/pf.os.5 b/contrib/pf/man/pf.os.5 deleted file mode 100644 index 593052572951b..0000000000000 --- a/contrib/pf/man/pf.os.5 +++ /dev/null @@ -1,223 +0,0 @@ -.\" $OpenBSD: pf.os.5,v 1.8 2007/05/31 19:19:58 jmc Exp $ -.\" -.\" Copyright (c) 2003 Mike Frantzen -.\" -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd May 31 2007 -.Dt PF.OS 5 -.Os -.Sh NAME -.Nm pf.os -.Nd format of the operating system fingerprints file -.Sh DESCRIPTION -The -.Xr pf 4 -firewall and the -.Xr tcpdump 1 -program can both fingerprint the operating system of hosts that -originate an IPv4 TCP connection. -The file consists of newline-separated records, one per fingerprint, -containing nine colon -.Pq Ql \&: -separated fields. -These fields are as follows: -.Pp -.Bl -tag -width Description -offset indent -compact -.It window -The TCP window size. -.It TTL -The IP time to live. -.It df -The presence of the IPv4 don't fragment bit. -.It packet size -The size of the initial TCP packet. -.It TCP options -An ordered list of the TCP options. -.It class -The class of operating system. -.It version -The version of the operating system. -.It subtype -The subtype of patchlevel of the operating system. -.It description -The overall textual description of the operating system, version and subtype. -.El -.Pp -The -.Ar window -field corresponds to the th->th_win field in the TCP header and is the -source host's advertised TCP window size. -It may be between zero and 65,535 inclusive. -The window size may be given as a multiple of a constant by prepending -the size with a percent sign -.Sq % -and the value will be used as a modulus. -Three special values may be used for the window size: -.Pp -.Bl -tag -width xxx -offset indent -compact -.It * -An asterisk will wildcard the value so any window size will match. -.It S -Allow any window size which is a multiple of the maximum segment size (MSS). -.It T -Allow any window size which is a multiple of the maximum transmission unit -(MTU). -.El -.Pp -The -.Ar ttl -value is the initial time to live in the IP header. -The fingerprint code will account for the volatility of the packet's TTL -as it traverses a network. -.Pp -The -.Ar df -bit corresponds to the Don't Fragment bit in an IPv4 header. -It tells intermediate routers not to fragment the packet and is used for -path MTU discovery. -It may be either a zero or a one. -.Pp -The -.Ar packet size -is the literal size of the full IP packet and is a function of all of -the IP and TCP options. -.Pp -The -.Ar TCP options -field is an ordered list of the individual TCP options that appear in the -SYN packet. -Each option is described by a single character separated by a comma and -certain ones may include a value. -The options are: -.Pp -.Bl -tag -width Description -offset indent -compact -.It Mnnn -maximum segment size (MSS) option. -The value is the maximum packet size of the network link which may -include the -.Sq % -modulus or match all MSSes with the -.Sq * -value. -.It N -the NOP option (NO Operation). -.It T[0] -the timestamp option. -Certain operating systems always start with a zero timestamp in which -case a zero value is added to the option; otherwise no value is appended. -.It S -the Selective ACKnowledgement OK (SACKOK) option. -.It Wnnn -window scaling option. -The value is the size of the window scaling which may include the -.Sq % -modulus or match all window scalings with the -.Sq * -value. -.El -.Pp -No TCP options in the fingerprint may be given with a single dot -.Sq \&. . -.Pp -An example of OpenBSD's TCP options are: -.Pp -.Dl M*,N,N,S,N,W0,N,N,T -.Pp -The first option -.Ar M* -is the MSS option and will match all values. -The second and third options -.Ar N -will match two NOPs. -The fourth option -.Ar S -will match the SACKOK option. -The fifth -.Ar N -will match another NOP. -The sixth -.Ar W0 -will match a window scaling option with a zero scaling size. -The seventh and eighth -.Ar N -options will match two NOPs. -And the ninth and final option -.Ar T -will match the timestamp option with any time value. -.Pp -The TCP options in a fingerprint will only match packets with the -exact same TCP options in the same order. -.Pp -The -.Ar class -field is the class, genre or vendor of the operating system. -.Pp -The -.Ar version -is the version of the operating system. -It is used to distinguish between different fingerprints of operating -systems of the same class but different versions. -.Pp -The -.Ar subtype -is the subtype or patch level of the operating system version. -It is used to distinguish between different fingerprints of operating -systems of the same class and same version but slightly different -patches or tweaking. -.Pp -The -.Ar description -is a general description of the operating system, its version, -patchlevel and any further useful details. -.Sh EXAMPLES -The fingerprint of a plain -.Ox 3.3 -host is: -.Bd -literal - 16384:64:1:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3::OpenBSD 3.3 -.Ed -.Pp -The fingerprint of an -.Ox 3.3 -host behind a PF scrubbing firewall with a no-df rule would be: -.Bd -literal - 16384:64:0:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3:!df:OpenBSD 3.3 scrub no-df -.Ed -.Pp -An absolutely braindead embedded operating system fingerprint could be: -.Bd -literal - 65535:255:0:40:.:DUMMY:1.1:p3:Dummy embedded OS v1.1p3 -.Ed -.Pp -The -.Xr tcpdump 1 -output of -.Bd -literal - # tcpdump -s128 -c1 -nv 'tcp[13] == 2' - 03:13:48.118526 10.0.0.1.3377 > 10.0.0.2.80: S [tcp sum ok] \e - 534596083:534596083(0) win 57344 (DF) [tos 0x10] \e - (ttl 64, id 11315, len 44) -.Ed -.Pp -almost translates into the following fingerprint -.Bd -literal - 57344:64:1:44:M1460: exampleOS:1.0::exampleOS 1.0 -.Ed -.Sh SEE ALSO -.Xr pf 4 , -.Xr pf.conf 5 , -.Xr pfctl 8 , -.Xr tcpdump 1 diff --git a/contrib/pf/man/pflog.4 b/contrib/pf/man/pflog.4 deleted file mode 100644 index c1039a3ed1428..0000000000000 --- a/contrib/pf/man/pflog.4 +++ /dev/null @@ -1,107 +0,0 @@ -.\" $OpenBSD: pflog.4,v 1.10 2007/05/31 19:19:51 jmc Exp $ -.\" -.\" Copyright (c) 2001 Tobias Weingartner -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd May 31 2007 -.Dt PFLOG 4 -.Os -.Sh NAME -.Nm pflog -.Nd packet filter logging interface -.Sh SYNOPSIS -.Cd "device pflog" -.Sh DESCRIPTION -The -.Nm pflog -interface is a device which makes visible all packets logged by -the packet filter, -.Xr pf 4 . -Logged packets can easily be monitored in real -time by invoking -.Xr tcpdump 1 -on the -.Nm -interface, or stored to disk using -.Xr pflogd 8 . -.Pp -The pflog0 interface is created automatically at boot if both -.Xr pf 4 -and -.Xr pflogd 8 -are enabled; -further instances can be created using -.Xr ifconfig 8 . -.Pp -Each packet retrieved on this interface has a header associated -with it of length -.Dv PFLOG_HDRLEN . -This header documents the address family, interface name, rule -number, reason, action, and direction of the packet that was logged. -This structure, defined in -.Aq Pa net/if_pflog.h -looks like -.Bd -literal -offset indent -struct pfloghdr { - u_int8_t length; - sa_family_t af; - u_int8_t action; - u_int8_t reason; - char ifname[IFNAMSIZ]; - char ruleset[PF_RULESET_NAME_SIZE]; - u_int32_t rulenr; - u_int32_t subrulenr; - uid_t uid; - pid_t pid; - uid_t rule_uid; - pid_t rule_pid; - u_int8_t dir; - u_int8_t pad[3]; -}; -.Ed -.Sh EXAMPLES -Create a -.Nm -interface -and monitor all packets logged on it: -.Bd -literal -offset indent -# ifconfig pflog1 up -# tcpdump -n -e -ttt -i pflog1 -.Ed -.Sh SEE ALSO -.Xr inet 4 , -.Xr inet6 4 , -.Xr netintro 4 , -.Xr pf 4 , -.Xr ifconfig 8 , -.Xr pflogd 8 , -.Xr tcpdump 1 -.Sh HISTORY -The -.Nm -device first appeared in -.Ox 3.0 . -.\" .Sh BUGS -.\" Anything here? diff --git a/contrib/pf/man/pfsync.4 b/contrib/pf/man/pfsync.4 deleted file mode 100644 index b00bf9d0fc789..0000000000000 --- a/contrib/pf/man/pfsync.4 +++ /dev/null @@ -1,229 +0,0 @@ -.\" $OpenBSD: pfsync.4,v 1.28 2009/02/17 10:05:18 dlg Exp $ -.\" -.\" Copyright (c) 2002 Michael Shalayeff -.\" Copyright (c) 2003-2004 Ryan McBride -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND, -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd December 20 2011 -.Dt PFSYNC 4 -.Os -.Sh NAME -.Nm pfsync -.Nd packet filter state table sychronisation interface -.Sh SYNOPSIS -.Cd "device pfsync" -.Sh DESCRIPTION -The -.Nm -interface is a pseudo-device which exposes certain changes to the state -table used by -.Xr pf 4 . -State changes can be viewed by invoking -.Xr tcpdump 1 -on the -.Nm -interface. -If configured with a physical synchronisation interface, -.Nm -will also send state changes out on that interface, -and insert state changes received on that interface from other systems -into the state table. -.Pp -By default, all local changes to the state table are exposed via -.Nm . -State changes from packets received by -.Nm -over the network are not rebroadcast. -Updates to states created by a rule marked with the -.Ar no-sync -keyword are ignored by the -.Nm -interface (see -.Xr pf.conf 5 -for details). -.Pp -The -.Nm -interface will attempt to collapse multiple state updates into a single -packet where possible. -The maximum number of times a single state can be updated before a -.Nm -packet will be sent out is controlled by the -.Ar maxupd -parameter to ifconfig -(see -.Xr ifconfig 8 -and the example below for more details). -The sending out of a -.Nm -packet will be delayed by a maximum of one second. -.Sh NETWORK SYNCHRONISATION -States can be synchronised between two or more firewalls using this -interface, by specifying a synchronisation interface using -.Xr ifconfig 8 . -For example, the following command sets fxp0 as the synchronisation -interface: -.Bd -literal -offset indent -# ifconfig pfsync0 syncdev fxp0 -.Ed -.Pp -By default, state change messages are sent out on the synchronisation -interface using IP multicast packets to the 244.0.0.240 group address. -An alternative destination address for -.Nm -packets can be specified using the -.Ic syncpeer -keyword. -This can be used in combination with -.Xr ipsec 4 -to protect the synchronisation traffic. -In such a configuration, the syncdev should be set to the -.Xr enc 4 -interface, as this is where the traffic arrives when it is decapsulated, -e.g.: -.Bd -literal -offset indent -# ifconfig pfsync0 syncpeer 10.0.0.2 syncdev enc0 -.Ed -.Pp -It is important that the pfsync traffic be well secured -as there is no authentication on the protocol and it would -be trivial to spoof packets which create states, bypassing the pf ruleset. -Either run the pfsync protocol on a trusted network \- ideally a network -dedicated to pfsync messages such as a crossover cable between two firewalls, -or specify a peer address and protect the traffic with -.Xr ipsec 4 . -.Pp -.Nm -has the following -.Xr sysctl 8 -tunables: -.Bl -tag -width ".Va net.pfsync" -.It Va net.pfsync.carp_demotion_factor -Value added to -.Va net.inet.carp.demotion -while -.Nm -tries to perform its bulk update. -See -.Xr carp 4 -for more information. -Default value is 240. -.El -.Sh EXAMPLES -.Nm -and -.Xr carp 4 -can be used together to provide automatic failover of a pair of firewalls -configured in parallel. -One firewall will handle all traffic until it dies, is shut down, or is -manually demoted, at which point the second firewall will take over -automatically. -.Pp -Both firewalls in this example have three -.Xr sis 4 -interfaces. -sis0 is the external interface, on the 10.0.0.0/24 subnet; sis1 is the -internal interface, on the 192.168.0.0/24 subnet; and sis2 is the -.Nm -interface, using the 192.168.254.0/24 subnet. -A crossover cable connects the two firewalls via their sis2 interfaces. -On all three interfaces, firewall A uses the .254 address, while firewall B -uses .253. -The interfaces are configured as follows (firewall A unless otherwise -indicated): -.Pp -Interfaces configuration in -.Pa /etc/rc.conf : -.Bd -literal -offset indent -network_interfaces="lo0 sis0 sis1 sis2" -ifconfig_sis0="10.0.0.254/24" -ifconfig_sis0_alias0="inet 10.0.0.1/24 vhid 1 pass foo" -ifconfig_sis1="192.168.0.254/24" -ifconfig_sis1_alias0="inet 192.168.0.1/24 vhid 2 pass bar" -ifconfig_sis2="192.168.254.254/24" -pfsync_enable="YES" -pfsync_syncdev="sis2" -.Ed -.Pp -.Xr pf 4 -must also be configured to allow -.Nm -and -.Xr carp 4 -traffic through. -The following should be added to the top of -.Pa /etc/pf.conf : -.Bd -literal -offset indent -pass quick on { sis2 } proto pfsync keep state (no-sync) -pass on { sis0 sis1 } proto carp keep state (no-sync) -.Ed -.Pp -It is preferable that one firewall handle the forwarding of all the traffic, -therefore the -.Ar advskew -on the backup firewall's -.Xr carp 4 -vhids should be set to something higher than -the primary's. -For example, if firewall B is the backup, its -carp1 configuration would look like this: -would look like this: -.Bd -literal -offset indent -ifconfig_sis1_alias0="inet 192.168.0.1/24 vhid 2 pass bar advskew 100" -.Ed -.Pp -The following must also be added to -.Pa /etc/sysctl.conf : -.Bd -literal -offset indent -net.inet.carp.preempt=1 -.Ed -.Sh SEE ALSO -.Xr bpf 4 , -.Xr carp 4 , -.Xr enc 4 , -.Xr inet 4 , -.Xr inet6 4 , -.Xr ipsec 4 , -.Xr netintro 4 , -.Xr pf 4 , -.Xr pf.conf 5 , -.Xr protocols 5 , -.Xr rc.conf 5 , -.Xr ifconfig 8 , -.Xr tcpdump 1 -.Sh HISTORY -The -.Nm -device first appeared in -.Ox 3.3 . -It was first imported to -.Fx 5.3 . -.Pp -The -.Nm -protocol and kernel implementation were significantly modified in -.Fx 9.0 . -The newer protocol is not compatible with older one and will not interoperate -with it. diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y deleted file mode 100644 index 99c26c0a33dfc..0000000000000 --- a/contrib/pf/pfctl/parse.y +++ /dev/null @@ -1,6038 +0,0 @@ -/* $OpenBSD: parse.y,v 1.554 2008/10/17 12:59:53 henning Exp $ */ - -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. - * Copyright (c) 2001 Theo de Raadt. All rights reserved. - * Copyright (c) 2002,2003 Henning Brauer. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -%{ -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#ifdef __FreeBSD__ -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -static struct pfctl *pf = NULL; -static int debug = 0; -static int rulestate = 0; -static u_int16_t returnicmpdefault = - (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT; -static u_int16_t returnicmp6default = - (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT; -static int blockpolicy = PFRULE_DROP; -static int require_order = 1; -static int default_statelock; - -TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); -static struct file { - TAILQ_ENTRY(file) entry; - FILE *stream; - char *name; - int lineno; - int errors; -} *file; -struct file *pushfile(const char *, int); -int popfile(void); -int check_file_secrecy(int, const char *); -int yyparse(void); -int yylex(void); -int yyerror(const char *, ...); -int kw_cmp(const void *, const void *); -int lookup(char *); -int lgetc(int); -int lungetc(int); -int findeol(void); - -TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); -struct sym { - TAILQ_ENTRY(sym) entry; - int used; - int persist; - char *nam; - char *val; -}; -int symset(const char *, const char *, int); -char *symget(const char *); - -int atoul(char *, u_long *); - -enum { - PFCTL_STATE_NONE, - PFCTL_STATE_OPTION, - PFCTL_STATE_SCRUB, - PFCTL_STATE_QUEUE, - PFCTL_STATE_NAT, - PFCTL_STATE_FILTER -}; - -struct node_proto { - u_int8_t proto; - struct node_proto *next; - struct node_proto *tail; -}; - -struct node_port { - u_int16_t port[2]; - u_int8_t op; - struct node_port *next; - struct node_port *tail; -}; - -struct node_uid { - uid_t uid[2]; - u_int8_t op; - struct node_uid *next; - struct node_uid *tail; -}; - -struct node_gid { - gid_t gid[2]; - u_int8_t op; - struct node_gid *next; - struct node_gid *tail; -}; - -struct node_icmp { - u_int8_t code; - u_int8_t type; - u_int8_t proto; - struct node_icmp *next; - struct node_icmp *tail; -}; - -enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, - PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, - PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, - PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, - PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; - -enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; - -struct node_state_opt { - int type; - union { - u_int32_t max_states; - u_int32_t max_src_states; - u_int32_t max_src_conn; - struct { - u_int32_t limit; - u_int32_t seconds; - } max_src_conn_rate; - struct { - u_int8_t flush; - char tblname[PF_TABLE_NAME_SIZE]; - } overload; - u_int32_t max_src_nodes; - u_int8_t src_track; - u_int32_t statelock; - struct { - int number; - u_int32_t seconds; - } timeout; - } data; - struct node_state_opt *next; - struct node_state_opt *tail; -}; - -struct peer { - struct node_host *host; - struct node_port *port; -}; - -struct node_queue { - char queue[PF_QNAME_SIZE]; - char parent[PF_QNAME_SIZE]; - char ifname[IFNAMSIZ]; - int scheduler; - struct node_queue *next; - struct node_queue *tail; -} *queues = NULL; - -struct node_qassign { - char *qname; - char *pqname; -}; - -struct filter_opts { - int marker; -#define FOM_FLAGS 0x01 -#define FOM_ICMP 0x02 -#define FOM_TOS 0x04 -#define FOM_KEEP 0x08 -#define FOM_SRCTRACK 0x10 - struct node_uid *uid; - struct node_gid *gid; - struct { - u_int8_t b1; - u_int8_t b2; - u_int16_t w; - u_int16_t w2; - } flags; - struct node_icmp *icmpspec; - u_int32_t tos; - u_int32_t prob; - struct { - int action; - struct node_state_opt *options; - } keep; - int fragment; - int allowopts; - char *label; - struct node_qassign queues; - char *tag; - char *match_tag; - u_int8_t match_tag_not; - u_int rtableid; - struct { - struct node_host *addr; - u_int16_t port; - } divert; -} filter_opts; - -struct antispoof_opts { - char *label; - u_int rtableid; -} antispoof_opts; - -struct scrub_opts { - int marker; -#define SOM_MINTTL 0x01 -#define SOM_MAXMSS 0x02 -#define SOM_FRAGCACHE 0x04 -#define SOM_SETTOS 0x08 - int nodf; - int minttl; - int maxmss; - int settos; - int fragcache; - int randomid; - int reassemble_tcp; - char *match_tag; - u_int8_t match_tag_not; - u_int rtableid; -} scrub_opts; - -struct queue_opts { - int marker; -#define QOM_BWSPEC 0x01 -#define QOM_SCHEDULER 0x02 -#define QOM_PRIORITY 0x04 -#define QOM_TBRSIZE 0x08 -#define QOM_QLIMIT 0x10 - struct node_queue_bw queue_bwspec; - struct node_queue_opt scheduler; - int priority; - int tbrsize; - int qlimit; -} queue_opts; - -struct table_opts { - int flags; - int init_addr; - struct node_tinithead init_nodes; -} table_opts; - -struct pool_opts { - int marker; -#define POM_TYPE 0x01 -#define POM_STICKYADDRESS 0x02 - u_int8_t opts; - int type; - int staticport; - struct pf_poolhashkey *key; - -} pool_opts; - - -struct node_hfsc_opts hfsc_opts; -struct node_state_opt *keep_state_defaults = NULL; - -int disallow_table(struct node_host *, const char *); -int disallow_urpf_failed(struct node_host *, const char *); -int disallow_alias(struct node_host *, const char *); -int rule_consistent(struct pf_rule *, int); -int filter_consistent(struct pf_rule *, int); -int nat_consistent(struct pf_rule *); -int rdr_consistent(struct pf_rule *); -int process_tabledef(char *, struct table_opts *); -void expand_label_str(char *, size_t, const char *, const char *); -void expand_label_if(const char *, char *, size_t, const char *); -void expand_label_addr(const char *, char *, size_t, u_int8_t, - struct node_host *); -void expand_label_port(const char *, char *, size_t, - struct node_port *); -void expand_label_proto(const char *, char *, size_t, u_int8_t); -void expand_label_nr(const char *, char *, size_t); -void expand_label(char *, size_t, const char *, u_int8_t, - struct node_host *, struct node_port *, struct node_host *, - struct node_port *, u_int8_t); -void expand_rule(struct pf_rule *, struct node_if *, - struct node_host *, struct node_proto *, struct node_os *, - struct node_host *, struct node_port *, struct node_host *, - struct node_port *, struct node_uid *, struct node_gid *, - struct node_icmp *, const char *); -int expand_altq(struct pf_altq *, struct node_if *, - struct node_queue *, struct node_queue_bw bwspec, - struct node_queue_opt *); -int expand_queue(struct pf_altq *, struct node_if *, - struct node_queue *, struct node_queue_bw, - struct node_queue_opt *); -int expand_skip_interface(struct node_if *); - -int check_rulestate(int); -int getservice(char *); -int rule_label(struct pf_rule *, char *); -int rt_tableid_max(void); - -void mv_rules(struct pf_ruleset *, struct pf_ruleset *); -void decide_address_family(struct node_host *, sa_family_t *); -void remove_invalid_hosts(struct node_host **, sa_family_t *); -int invalid_redirect(struct node_host *, sa_family_t); -u_int16_t parseicmpspec(char *, sa_family_t); - -TAILQ_HEAD(loadanchorshead, loadanchors) - loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead); - -struct loadanchors { - TAILQ_ENTRY(loadanchors) entries; - char *anchorname; - char *filename; -}; - -typedef struct { - union { - int64_t number; - double probability; - int i; - char *string; - u_int rtableid; - struct { - u_int8_t b1; - u_int8_t b2; - u_int16_t w; - u_int16_t w2; - } b; - struct range { - int a; - int b; - int t; - } range; - struct node_if *interface; - struct node_proto *proto; - struct node_icmp *icmp; - struct node_host *host; - struct node_os *os; - struct node_port *port; - struct node_uid *uid; - struct node_gid *gid; - struct node_state_opt *state_opt; - struct peer peer; - struct { - struct peer src, dst; - struct node_os *src_os; - } fromto; - struct { - struct node_host *host; - u_int8_t rt; - u_int8_t pool_opts; - sa_family_t af; - struct pf_poolhashkey *key; - } route; - struct redirection { - struct node_host *host; - struct range rport; - } *redirection; - struct { - int action; - struct node_state_opt *options; - } keep_state; - struct { - u_int8_t log; - u_int8_t logif; - u_int8_t quick; - } logquick; - struct { - int neg; - char *name; - } tagged; - struct pf_poolhashkey *hashkey; - struct node_queue *queue; - struct node_queue_opt queue_options; - struct node_queue_bw queue_bwspec; - struct node_qassign qassign; - struct filter_opts filter_opts; - struct antispoof_opts antispoof_opts; - struct queue_opts queue_opts; - struct scrub_opts scrub_opts; - struct table_opts table_opts; - struct pool_opts pool_opts; - struct node_hfsc_opts hfsc_opts; - } v; - int lineno; -} YYSTYPE; - -#define PPORT_RANGE 1 -#define PPORT_STAR 2 -int parseport(char *, struct range *r, int); - -#define DYNIF_MULTIADDR(addr) ((addr).type == PF_ADDR_DYNIFTL && \ - (!((addr).iflags & PFI_AFLAG_NOALIAS) || \ - !isdigit((addr).v.ifname[strlen((addr).v.ifname)-1]))) - -%} - -%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS -%token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE -%token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF -%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL -%token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE -%token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR -%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID -%token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID -%token ANTISPOOF FOR INCLUDE -%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY -%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE PRIORITY QLIMIT RTABLE -%token LOAD RULESET_OPTIMIZATION -%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE -%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY -%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS -%token DIVERTTO DIVERTREPLY -%token STRING -%token NUMBER -%token PORTBINARY -%type interface if_list if_item_not if_item -%type number icmptype icmp6type uid gid -%type tos not yesno -%type probability -%type no dir af fragcache optimizer -%type sourcetrack flush unaryop statelock -%type action nataction natpasslog scrubaction -%type flags flag blockspec -%type portplain portstar portrange -%type hashkey -%type proto proto_list proto_item -%type protoval -%type icmpspec -%type icmp_list icmp_item -%type icmp6_list icmp6_item -%type reticmpspec reticmp6spec -%type fromto -%type ipportspec from to -%type ipspec toipspec xhost host dynaddr host_list -%type redir_host_list redirspec -%type route_host route_host_list routespec -%type os xos os_list -%type portspec port_list port_item -%type uids uid_list uid_item -%type gids gid_list gid_item -%type route -%type redirection redirpool -%type label stringall tag anchorname -%type string varstring numberstring -%type keep -%type state_opt_spec state_opt_list state_opt_item -%type logquick quick log logopts logopt -%type antispoof_ifspc antispoof_iflst antispoof_if -%type qname -%type qassign qassign_list qassign_item -%type scheduler -%type cbqflags_list cbqflags_item -%type priqflags_list priqflags_item -%type hfscopts_list hfscopts_item hfsc_opts -%type bandwidth -%type filter_opts filter_opt filter_opts_l -%type antispoof_opts antispoof_opt antispoof_opts_l -%type queue_opts queue_opt queue_opts_l -%type scrub_opts scrub_opt scrub_opts_l -%type table_opts table_opt table_opts_l -%type pool_opts pool_opt pool_opts_l -%type tagged -%type rtable -%% - -ruleset : /* empty */ - | ruleset include '\n' - | ruleset '\n' - | ruleset option '\n' - | ruleset scrubrule '\n' - | ruleset natrule '\n' - | ruleset binatrule '\n' - | ruleset pfrule '\n' - | ruleset anchorrule '\n' - | ruleset loadrule '\n' - | ruleset altqif '\n' - | ruleset queuespec '\n' - | ruleset varset '\n' - | ruleset antispoof '\n' - | ruleset tabledef '\n' - | '{' fakeanchor '}' '\n'; - | ruleset error '\n' { file->errors++; } - ; - -include : INCLUDE STRING { - struct file *nfile; - - if ((nfile = pushfile($2, 0)) == NULL) { - yyerror("failed to include file %s", $2); - free($2); - YYERROR; - } - free($2); - - file = nfile; - lungetc('\n'); - } - ; - -/* - * apply to previouslys specified rule: must be careful to note - * what that is: pf or nat or binat or rdr - */ -fakeanchor : fakeanchor '\n' - | fakeanchor anchorrule '\n' - | fakeanchor binatrule '\n' - | fakeanchor natrule '\n' - | fakeanchor pfrule '\n' - | fakeanchor error '\n' - ; - -optimizer : string { - if (!strcmp($1, "none")) - $$ = 0; - else if (!strcmp($1, "basic")) - $$ = PF_OPTIMIZE_BASIC; - else if (!strcmp($1, "profile")) - $$ = PF_OPTIMIZE_BASIC | PF_OPTIMIZE_PROFILE; - else { - yyerror("unknown ruleset-optimization %s", $1); - YYERROR; - } - } - ; - -option : SET OPTIMIZATION STRING { - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($3); - YYERROR; - } - if (pfctl_set_optimization(pf, $3) != 0) { - yyerror("unknown optimization %s", $3); - free($3); - YYERROR; - } - free($3); - } - | SET RULESET_OPTIMIZATION optimizer { - if (!(pf->opts & PF_OPT_OPTIMIZE)) { - pf->opts |= PF_OPT_OPTIMIZE; - pf->optimize = $3; - } - } - | SET TIMEOUT timeout_spec - | SET TIMEOUT '{' optnl timeout_list '}' - | SET LIMIT limit_spec - | SET LIMIT '{' optnl limit_list '}' - | SET LOGINTERFACE stringall { - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($3); - YYERROR; - } - if (pfctl_set_logif(pf, $3) != 0) { - yyerror("error setting loginterface %s", $3); - free($3); - YYERROR; - } - free($3); - } - | SET HOSTID number { - if ($3 == 0 || $3 > UINT_MAX) { - yyerror("hostid must be non-zero"); - YYERROR; - } - if (pfctl_set_hostid(pf, $3) != 0) { - yyerror("error setting hostid %08x", $3); - YYERROR; - } - } - | SET BLOCKPOLICY DROP { - if (pf->opts & PF_OPT_VERBOSE) - printf("set block-policy drop\n"); - if (check_rulestate(PFCTL_STATE_OPTION)) - YYERROR; - blockpolicy = PFRULE_DROP; - } - | SET BLOCKPOLICY RETURN { - if (pf->opts & PF_OPT_VERBOSE) - printf("set block-policy return\n"); - if (check_rulestate(PFCTL_STATE_OPTION)) - YYERROR; - blockpolicy = PFRULE_RETURN; - } - | SET REQUIREORDER yesno { - if (pf->opts & PF_OPT_VERBOSE) - printf("set require-order %s\n", - $3 == 1 ? "yes" : "no"); - require_order = $3; - } - | SET FINGERPRINTS STRING { - if (pf->opts & PF_OPT_VERBOSE) - printf("set fingerprints \"%s\"\n", $3); - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($3); - YYERROR; - } - if (!pf->anchor->name[0]) { - if (pfctl_file_fingerprints(pf->dev, - pf->opts, $3)) { - yyerror("error loading " - "fingerprints %s", $3); - free($3); - YYERROR; - } - } - free($3); - } - | SET STATEPOLICY statelock { - if (pf->opts & PF_OPT_VERBOSE) - switch ($3) { - case 0: - printf("set state-policy floating\n"); - break; - case PFRULE_IFBOUND: - printf("set state-policy if-bound\n"); - break; - } - default_statelock = $3; - } - | SET DEBUG STRING { - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($3); - YYERROR; - } - if (pfctl_set_debug(pf, $3) != 0) { - yyerror("error setting debuglevel %s", $3); - free($3); - YYERROR; - } - free($3); - } - | SET SKIP interface { - if (expand_skip_interface($3) != 0) { - yyerror("error setting skip interface(s)"); - YYERROR; - } - } - | SET STATEDEFAULTS state_opt_list { - if (keep_state_defaults != NULL) { - yyerror("cannot redefine state-defaults"); - YYERROR; - } - keep_state_defaults = $3; - } - ; - -stringall : STRING { $$ = $1; } - | ALL { - if (($$ = strdup("all")) == NULL) { - err(1, "stringall: strdup"); - } - } - ; - -string : STRING string { - if (asprintf(&$$, "%s %s", $1, $2) == -1) - err(1, "string: asprintf"); - free($1); - free($2); - } - | STRING - ; - -varstring : numberstring varstring { - if (asprintf(&$$, "%s %s", $1, $2) == -1) - err(1, "string: asprintf"); - free($1); - free($2); - } - | numberstring - ; - -numberstring : NUMBER { - char *s; - if (asprintf(&s, "%lld", (long long)$1) == -1) { - yyerror("string: asprintf"); - YYERROR; - } - $$ = s; - } - | STRING - ; - -varset : STRING '=' varstring { - if (pf->opts & PF_OPT_VERBOSE) - printf("%s = \"%s\"\n", $1, $3); - if (symset($1, $3, 0) == -1) - err(1, "cannot store variable %s", $1); - free($1); - free($3); - } - ; - -anchorname : STRING { $$ = $1; } - | /* empty */ { $$ = NULL; } - ; - -pfa_anchorlist : /* empty */ - | pfa_anchorlist '\n' - | pfa_anchorlist pfrule '\n' - | pfa_anchorlist anchorrule '\n' - ; - -pfa_anchor : '{' - { - char ta[PF_ANCHOR_NAME_SIZE]; - struct pf_ruleset *rs; - - /* steping into a brace anchor */ - pf->asd++; - pf->bn++; - pf->brace = 1; - - /* create a holding ruleset in the root */ - snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn); - rs = pf_find_or_create_ruleset(ta); - if (rs == NULL) - err(1, "pfa_anchor: pf_find_or_create_ruleset"); - pf->astack[pf->asd] = rs->anchor; - pf->anchor = rs->anchor; - } '\n' pfa_anchorlist '}' - { - pf->alast = pf->anchor; - pf->asd--; - pf->anchor = pf->astack[pf->asd]; - } - | /* empty */ - ; - -anchorrule : ANCHOR anchorname dir quick interface af proto fromto - filter_opts pfa_anchor - { - struct pf_rule r; - struct node_proto *proto; - - if (check_rulestate(PFCTL_STATE_FILTER)) { - if ($2) - free($2); - YYERROR; - } - - if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { - free($2); - yyerror("anchor names beginning with '_' " - "are reserved for internal use"); - YYERROR; - } - - memset(&r, 0, sizeof(r)); - if (pf->astack[pf->asd + 1]) { - /* move inline rules into relative location */ - pf_anchor_setup(&r, - &pf->astack[pf->asd]->ruleset, - $2 ? $2 : pf->alast->name); - - if (r.anchor == NULL) - err(1, "anchorrule: unable to " - "create ruleset"); - - if (pf->alast != r.anchor) { - if (r.anchor->match) { - yyerror("inline anchor '%s' " - "already exists", - r.anchor->name); - YYERROR; - } - mv_rules(&pf->alast->ruleset, - &r.anchor->ruleset); - } - pf_remove_if_empty_ruleset(&pf->alast->ruleset); - pf->alast = r.anchor; - } else { - if (!$2) { - yyerror("anchors without explicit " - "rules must specify a name"); - YYERROR; - } - } - r.direction = $3; - r.quick = $4.quick; - r.af = $6; - r.prob = $9.prob; - r.rtableid = $9.rtableid; - - if ($9.tag) - if (strlcpy(r.tagname, $9.tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if (rule_label(&r, $9.label)) - YYERROR; - free($9.label); - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; - if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { - yyerror("flags always false"); - YYERROR; - } - if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { - for (proto = $7; proto != NULL && - proto->proto != IPPROTO_TCP; - proto = proto->next) - ; /* nothing */ - if (proto == NULL && $7 != NULL) { - if ($9.flags.b1 || $9.flags.b2) - yyerror( - "flags only apply to tcp"); - if ($8.src_os) - yyerror( - "OS fingerprinting only " - "applies to tcp"); - YYERROR; - } - } - - r.tos = $9.tos; - - if ($9.keep.action) { - yyerror("cannot specify state handling " - "on anchors"); - YYERROR; - } - - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - - decide_address_family($8.src.host, &r.af); - decide_address_family($8.dst.host, &r.af); - - expand_rule(&r, $5, NULL, $7, $8.src_os, - $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, - pf->astack[pf->asd + 1] ? pf->alast->name : $2); - free($2); - pf->astack[pf->asd + 1] = NULL; - } - | NATANCHOR string interface af proto fromto rtable { - struct pf_rule r; - - if (check_rulestate(PFCTL_STATE_NAT)) { - free($2); - YYERROR; - } - - memset(&r, 0, sizeof(r)); - r.action = PF_NAT; - r.af = $4; - r.rtableid = $7; - - decide_address_family($6.src.host, &r.af); - decide_address_family($6.dst.host, &r.af); - - expand_rule(&r, $3, NULL, $5, $6.src_os, - $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0, $2); - free($2); - } - | RDRANCHOR string interface af proto fromto rtable { - struct pf_rule r; - - if (check_rulestate(PFCTL_STATE_NAT)) { - free($2); - YYERROR; - } - - memset(&r, 0, sizeof(r)); - r.action = PF_RDR; - r.af = $4; - r.rtableid = $7; - - decide_address_family($6.src.host, &r.af); - decide_address_family($6.dst.host, &r.af); - - if ($6.src.port != NULL) { - yyerror("source port parameter not supported" - " in rdr-anchor"); - YYERROR; - } - if ($6.dst.port != NULL) { - if ($6.dst.port->next != NULL) { - yyerror("destination port list " - "expansion not supported in " - "rdr-anchor"); - YYERROR; - } else if ($6.dst.port->op != PF_OP_EQ) { - yyerror("destination port operators" - " not supported in rdr-anchor"); - YYERROR; - } - r.dst.port[0] = $6.dst.port->port[0]; - r.dst.port[1] = $6.dst.port->port[1]; - r.dst.port_op = $6.dst.port->op; - } - - expand_rule(&r, $3, NULL, $5, $6.src_os, - $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0, $2); - free($2); - } - | BINATANCHOR string interface af proto fromto rtable { - struct pf_rule r; - - if (check_rulestate(PFCTL_STATE_NAT)) { - free($2); - YYERROR; - } - - memset(&r, 0, sizeof(r)); - r.action = PF_BINAT; - r.af = $4; - r.rtableid = $7; - if ($5 != NULL) { - if ($5->next != NULL) { - yyerror("proto list expansion" - " not supported in binat-anchor"); - YYERROR; - } - r.proto = $5->proto; - free($5); - } - - if ($6.src.host != NULL || $6.src.port != NULL || - $6.dst.host != NULL || $6.dst.port != NULL) { - yyerror("fromto parameter not supported" - " in binat-anchor"); - YYERROR; - } - - decide_address_family($6.src.host, &r.af); - decide_address_family($6.dst.host, &r.af); - - pfctl_add_rule(pf, &r, $2); - free($2); - } - ; - -loadrule : LOAD ANCHOR string FROM string { - struct loadanchors *loadanchor; - - if (strlen(pf->anchor->name) + 1 + - strlen($3) >= MAXPATHLEN) { - yyerror("anchorname %s too long, max %u\n", - $3, MAXPATHLEN - 1); - free($3); - YYERROR; - } - loadanchor = calloc(1, sizeof(struct loadanchors)); - if (loadanchor == NULL) - err(1, "loadrule: calloc"); - if ((loadanchor->anchorname = malloc(MAXPATHLEN)) == - NULL) - err(1, "loadrule: malloc"); - if (pf->anchor->name[0]) - snprintf(loadanchor->anchorname, MAXPATHLEN, - "%s/%s", pf->anchor->name, $3); - else - strlcpy(loadanchor->anchorname, $3, MAXPATHLEN); - if ((loadanchor->filename = strdup($5)) == NULL) - err(1, "loadrule: strdup"); - - TAILQ_INSERT_TAIL(&loadanchorshead, loadanchor, - entries); - - free($3); - free($5); - }; - -scrubaction : no SCRUB { - $$.b2 = $$.w = 0; - if ($1) - $$.b1 = PF_NOSCRUB; - else - $$.b1 = PF_SCRUB; - } - ; - -scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts - { - struct pf_rule r; - - if (check_rulestate(PFCTL_STATE_SCRUB)) - YYERROR; - - memset(&r, 0, sizeof(r)); - - r.action = $1.b1; - r.direction = $2; - - r.log = $3.log; - r.logif = $3.logif; - if ($3.quick) { - yyerror("scrub rules do not support 'quick'"); - YYERROR; - } - - r.af = $5; - if ($8.nodf) - r.rule_flag |= PFRULE_NODF; - if ($8.randomid) - r.rule_flag |= PFRULE_RANDOMID; - if ($8.reassemble_tcp) { - if (r.direction != PF_INOUT) { - yyerror("reassemble tcp rules can not " - "specify direction"); - YYERROR; - } - r.rule_flag |= PFRULE_REASSEMBLE_TCP; - } - if ($8.minttl) - r.min_ttl = $8.minttl; - if ($8.maxmss) - r.max_mss = $8.maxmss; - if ($8.marker & SOM_SETTOS) { - r.rule_flag |= PFRULE_SET_TOS; - r.set_tos = $8.settos; - } - if ($8.fragcache) - r.rule_flag |= $8.fragcache; - if ($8.match_tag) - if (strlcpy(r.match_tagname, $8.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $8.match_tag_not; - r.rtableid = $8.rtableid; - - expand_rule(&r, $4, NULL, $6, $7.src_os, - $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - NULL, NULL, NULL, ""); - } - ; - -scrub_opts : { - bzero(&scrub_opts, sizeof scrub_opts); - scrub_opts.rtableid = -1; - } - scrub_opts_l - { $$ = scrub_opts; } - | /* empty */ { - bzero(&scrub_opts, sizeof scrub_opts); - scrub_opts.rtableid = -1; - $$ = scrub_opts; - } - ; - -scrub_opts_l : scrub_opts_l scrub_opt - | scrub_opt - ; - -scrub_opt : NODF { - if (scrub_opts.nodf) { - yyerror("no-df cannot be respecified"); - YYERROR; - } - scrub_opts.nodf = 1; - } - | MINTTL NUMBER { - if (scrub_opts.marker & SOM_MINTTL) { - yyerror("min-ttl cannot be respecified"); - YYERROR; - } - if ($2 < 0 || $2 > 255) { - yyerror("illegal min-ttl value %d", $2); - YYERROR; - } - scrub_opts.marker |= SOM_MINTTL; - scrub_opts.minttl = $2; - } - | MAXMSS NUMBER { - if (scrub_opts.marker & SOM_MAXMSS) { - yyerror("max-mss cannot be respecified"); - YYERROR; - } - if ($2 < 0 || $2 > 65535) { - yyerror("illegal max-mss value %d", $2); - YYERROR; - } - scrub_opts.marker |= SOM_MAXMSS; - scrub_opts.maxmss = $2; - } - | SETTOS tos { - if (scrub_opts.marker & SOM_SETTOS) { - yyerror("set-tos cannot be respecified"); - YYERROR; - } - scrub_opts.marker |= SOM_SETTOS; - scrub_opts.settos = $2; - } - | fragcache { - if (scrub_opts.marker & SOM_FRAGCACHE) { - yyerror("fragcache cannot be respecified"); - YYERROR; - } - scrub_opts.marker |= SOM_FRAGCACHE; - scrub_opts.fragcache = $1; - } - | REASSEMBLE STRING { - if (strcasecmp($2, "tcp") != 0) { - yyerror("scrub reassemble supports only tcp, " - "not '%s'", $2); - free($2); - YYERROR; - } - free($2); - if (scrub_opts.reassemble_tcp) { - yyerror("reassemble tcp cannot be respecified"); - YYERROR; - } - scrub_opts.reassemble_tcp = 1; - } - | RANDOMID { - if (scrub_opts.randomid) { - yyerror("random-id cannot be respecified"); - YYERROR; - } - scrub_opts.randomid = 1; - } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - scrub_opts.rtableid = $2; - } - | not TAGGED string { - scrub_opts.match_tag = $3; - scrub_opts.match_tag_not = $1; - } - ; - -fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ } - | FRAGMENT FRAGCROP { $$ = PFRULE_FRAGCROP; } - | FRAGMENT FRAGDROP { $$ = PFRULE_FRAGDROP; } - ; - -antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { - struct pf_rule r; - struct node_host *h = NULL, *hh; - struct node_if *i, *j; - - if (check_rulestate(PFCTL_STATE_FILTER)) - YYERROR; - - for (i = $3; i; i = i->next) { - bzero(&r, sizeof(r)); - - r.action = PF_DROP; - r.direction = PF_IN; - r.log = $2.log; - r.logif = $2.logif; - r.quick = $2.quick; - r.af = $4; - if (rule_label(&r, $5.label)) - YYERROR; - r.rtableid = $5.rtableid; - j = calloc(1, sizeof(struct node_if)); - if (j == NULL) - err(1, "antispoof: calloc"); - if (strlcpy(j->ifname, i->ifname, - sizeof(j->ifname)) >= sizeof(j->ifname)) { - free(j); - yyerror("interface name too long"); - YYERROR; - } - j->not = 1; - if (i->dynamic) { - h = calloc(1, sizeof(*h)); - if (h == NULL) - err(1, "address: calloc"); - h->addr.type = PF_ADDR_DYNIFTL; - set_ipmask(h, 128); - if (strlcpy(h->addr.v.ifname, i->ifname, - sizeof(h->addr.v.ifname)) >= - sizeof(h->addr.v.ifname)) { - free(h); - yyerror( - "interface name too long"); - YYERROR; - } - hh = malloc(sizeof(*hh)); - if (hh == NULL) - err(1, "address: malloc"); - bcopy(h, hh, sizeof(*hh)); - h->addr.iflags = PFI_AFLAG_NETWORK; - } else { - h = ifa_lookup(j->ifname, - PFI_AFLAG_NETWORK); - hh = NULL; - } - - if (h != NULL) - expand_rule(&r, j, NULL, NULL, NULL, h, - NULL, NULL, NULL, NULL, NULL, - NULL, ""); - - if ((i->ifa_flags & IFF_LOOPBACK) == 0) { - bzero(&r, sizeof(r)); - - r.action = PF_DROP; - r.direction = PF_IN; - r.log = $2.log; - r.logif = $2.logif; - r.quick = $2.quick; - r.af = $4; - if (rule_label(&r, $5.label)) - YYERROR; - r.rtableid = $5.rtableid; - if (hh != NULL) - h = hh; - else - h = ifa_lookup(i->ifname, 0); - if (h != NULL) - expand_rule(&r, NULL, NULL, - NULL, NULL, h, NULL, NULL, - NULL, NULL, NULL, NULL, ""); - } else - free(hh); - } - free($5.label); - } - ; - -antispoof_ifspc : FOR antispoof_if { $$ = $2; } - | FOR '{' optnl antispoof_iflst '}' { $$ = $4; } - ; - -antispoof_iflst : antispoof_if optnl { $$ = $1; } - | antispoof_iflst comma antispoof_if optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -antispoof_if : if_item { $$ = $1; } - | '(' if_item ')' { - $2->dynamic = 1; - $$ = $2; - } - ; - -antispoof_opts : { - bzero(&antispoof_opts, sizeof antispoof_opts); - antispoof_opts.rtableid = -1; - } - antispoof_opts_l - { $$ = antispoof_opts; } - | /* empty */ { - bzero(&antispoof_opts, sizeof antispoof_opts); - antispoof_opts.rtableid = -1; - $$ = antispoof_opts; - } - ; - -antispoof_opts_l : antispoof_opts_l antispoof_opt - | antispoof_opt - ; - -antispoof_opt : label { - if (antispoof_opts.label) { - yyerror("label cannot be redefined"); - YYERROR; - } - antispoof_opts.label = $1; - } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - antispoof_opts.rtableid = $2; - } - ; - -not : '!' { $$ = 1; } - | /* empty */ { $$ = 0; } - ; - -tabledef : TABLE '<' STRING '>' table_opts { - struct node_host *h, *nh; - struct node_tinit *ti, *nti; - - if (strlen($3) >= PF_TABLE_NAME_SIZE) { - yyerror("table name too long, max %d chars", - PF_TABLE_NAME_SIZE - 1); - free($3); - YYERROR; - } - if (pf->loadopt & PFCTL_FLAG_TABLE) - if (process_tabledef($3, &$5)) { - free($3); - YYERROR; - } - free($3); - for (ti = SIMPLEQ_FIRST(&$5.init_nodes); - ti != SIMPLEQ_END(&$5.init_nodes); ti = nti) { - if (ti->file) - free(ti->file); - for (h = ti->host; h != NULL; h = nh) { - nh = h->next; - free(h); - } - nti = SIMPLEQ_NEXT(ti, entries); - free(ti); - } - } - ; - -table_opts : { - bzero(&table_opts, sizeof table_opts); - SIMPLEQ_INIT(&table_opts.init_nodes); - } - table_opts_l - { $$ = table_opts; } - | /* empty */ - { - bzero(&table_opts, sizeof table_opts); - SIMPLEQ_INIT(&table_opts.init_nodes); - $$ = table_opts; - } - ; - -table_opts_l : table_opts_l table_opt - | table_opt - ; - -table_opt : STRING { - if (!strcmp($1, "const")) - table_opts.flags |= PFR_TFLAG_CONST; - else if (!strcmp($1, "persist")) - table_opts.flags |= PFR_TFLAG_PERSIST; - else if (!strcmp($1, "counters")) - table_opts.flags |= PFR_TFLAG_COUNTERS; - else { - yyerror("invalid table option '%s'", $1); - free($1); - YYERROR; - } - free($1); - } - | '{' optnl '}' { table_opts.init_addr = 1; } - | '{' optnl host_list '}' { - struct node_host *n; - struct node_tinit *ti; - - for (n = $3; n != NULL; n = n->next) { - switch (n->addr.type) { - case PF_ADDR_ADDRMASK: - continue; /* ok */ - case PF_ADDR_RANGE: - yyerror("address ranges are not " - "permitted inside tables"); - break; - case PF_ADDR_DYNIFTL: - yyerror("dynamic addresses are not " - "permitted inside tables"); - break; - case PF_ADDR_TABLE: - yyerror("tables cannot contain tables"); - break; - case PF_ADDR_NOROUTE: - yyerror("\"no-route\" is not permitted " - "inside tables"); - break; - case PF_ADDR_URPFFAILED: - yyerror("\"urpf-failed\" is not " - "permitted inside tables"); - break; - default: - yyerror("unknown address type %d", - n->addr.type); - } - YYERROR; - } - if (!(ti = calloc(1, sizeof(*ti)))) - err(1, "table_opt: calloc"); - ti->host = $3; - SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti, - entries); - table_opts.init_addr = 1; - } - | FILENAME STRING { - struct node_tinit *ti; - - if (!(ti = calloc(1, sizeof(*ti)))) - err(1, "table_opt: calloc"); - ti->file = $2; - SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti, - entries); - table_opts.init_addr = 1; - } - ; - -altqif : ALTQ interface queue_opts QUEUE qassign { - struct pf_altq a; - - if (check_rulestate(PFCTL_STATE_QUEUE)) - YYERROR; - - memset(&a, 0, sizeof(a)); - if ($3.scheduler.qtype == ALTQT_NONE) { - yyerror("no scheduler specified!"); - YYERROR; - } - a.scheduler = $3.scheduler.qtype; - a.qlimit = $3.qlimit; - a.tbrsize = $3.tbrsize; - if ($5 == NULL) { - yyerror("no child queues specified"); - YYERROR; - } - if (expand_altq(&a, $2, $5, $3.queue_bwspec, - &$3.scheduler)) - YYERROR; - } - ; - -queuespec : QUEUE STRING interface queue_opts qassign { - struct pf_altq a; - - if (check_rulestate(PFCTL_STATE_QUEUE)) { - free($2); - YYERROR; - } - - memset(&a, 0, sizeof(a)); - - if (strlcpy(a.qname, $2, sizeof(a.qname)) >= - sizeof(a.qname)) { - yyerror("queue name too long (max " - "%d chars)", PF_QNAME_SIZE-1); - free($2); - YYERROR; - } - free($2); - if ($4.tbrsize) { - yyerror("cannot specify tbrsize for queue"); - YYERROR; - } - if ($4.priority > 255) { - yyerror("priority out of range: max 255"); - YYERROR; - } - a.priority = $4.priority; - a.qlimit = $4.qlimit; - a.scheduler = $4.scheduler.qtype; - if (expand_queue(&a, $3, $5, $4.queue_bwspec, - &$4.scheduler)) { - yyerror("errors in queue definition"); - YYERROR; - } - } - ; - -queue_opts : { - bzero(&queue_opts, sizeof queue_opts); - queue_opts.priority = DEFAULT_PRIORITY; - queue_opts.qlimit = DEFAULT_QLIMIT; - queue_opts.scheduler.qtype = ALTQT_NONE; - queue_opts.queue_bwspec.bw_percent = 100; - } - queue_opts_l - { $$ = queue_opts; } - | /* empty */ { - bzero(&queue_opts, sizeof queue_opts); - queue_opts.priority = DEFAULT_PRIORITY; - queue_opts.qlimit = DEFAULT_QLIMIT; - queue_opts.scheduler.qtype = ALTQT_NONE; - queue_opts.queue_bwspec.bw_percent = 100; - $$ = queue_opts; - } - ; - -queue_opts_l : queue_opts_l queue_opt - | queue_opt - ; - -queue_opt : BANDWIDTH bandwidth { - if (queue_opts.marker & QOM_BWSPEC) { - yyerror("bandwidth cannot be respecified"); - YYERROR; - } - queue_opts.marker |= QOM_BWSPEC; - queue_opts.queue_bwspec = $2; - } - | PRIORITY NUMBER { - if (queue_opts.marker & QOM_PRIORITY) { - yyerror("priority cannot be respecified"); - YYERROR; - } - if ($2 < 0 || $2 > 255) { - yyerror("priority out of range: max 255"); - YYERROR; - } - queue_opts.marker |= QOM_PRIORITY; - queue_opts.priority = $2; - } - | QLIMIT NUMBER { - if (queue_opts.marker & QOM_QLIMIT) { - yyerror("qlimit cannot be respecified"); - YYERROR; - } - if ($2 < 0 || $2 > 65535) { - yyerror("qlimit out of range: max 65535"); - YYERROR; - } - queue_opts.marker |= QOM_QLIMIT; - queue_opts.qlimit = $2; - } - | scheduler { - if (queue_opts.marker & QOM_SCHEDULER) { - yyerror("scheduler cannot be respecified"); - YYERROR; - } - queue_opts.marker |= QOM_SCHEDULER; - queue_opts.scheduler = $1; - } - | TBRSIZE NUMBER { - if (queue_opts.marker & QOM_TBRSIZE) { - yyerror("tbrsize cannot be respecified"); - YYERROR; - } - if ($2 < 0 || $2 > 65535) { - yyerror("tbrsize too big: max 65535"); - YYERROR; - } - queue_opts.marker |= QOM_TBRSIZE; - queue_opts.tbrsize = $2; - } - ; - -bandwidth : STRING { - double bps; - char *cp; - - $$.bw_percent = 0; - - bps = strtod($1, &cp); - if (cp != NULL) { - if (!strcmp(cp, "b")) - ; /* nothing */ - else if (!strcmp(cp, "Kb")) - bps *= 1000; - else if (!strcmp(cp, "Mb")) - bps *= 1000 * 1000; - else if (!strcmp(cp, "Gb")) - bps *= 1000 * 1000 * 1000; - else if (!strcmp(cp, "%")) { - if (bps < 0 || bps > 100) { - yyerror("bandwidth spec " - "out of range"); - free($1); - YYERROR; - } - $$.bw_percent = bps; - bps = 0; - } else { - yyerror("unknown unit %s", cp); - free($1); - YYERROR; - } - } - free($1); - $$.bw_absolute = (u_int32_t)bps; - } - | NUMBER { - if ($1 < 0 || $1 > UINT_MAX) { - yyerror("bandwidth number too big"); - YYERROR; - } - $$.bw_percent = 0; - $$.bw_absolute = $1; - } - ; - -scheduler : CBQ { - $$.qtype = ALTQT_CBQ; - $$.data.cbq_opts.flags = 0; - } - | CBQ '(' cbqflags_list ')' { - $$.qtype = ALTQT_CBQ; - $$.data.cbq_opts.flags = $3; - } - | PRIQ { - $$.qtype = ALTQT_PRIQ; - $$.data.priq_opts.flags = 0; - } - | PRIQ '(' priqflags_list ')' { - $$.qtype = ALTQT_PRIQ; - $$.data.priq_opts.flags = $3; - } - | HFSC { - $$.qtype = ALTQT_HFSC; - bzero(&$$.data.hfsc_opts, - sizeof(struct node_hfsc_opts)); - } - | HFSC '(' hfsc_opts ')' { - $$.qtype = ALTQT_HFSC; - $$.data.hfsc_opts = $3; - } - ; - -cbqflags_list : cbqflags_item { $$ |= $1; } - | cbqflags_list comma cbqflags_item { $$ |= $3; } - ; - -cbqflags_item : STRING { - if (!strcmp($1, "default")) - $$ = CBQCLF_DEFCLASS; - else if (!strcmp($1, "borrow")) - $$ = CBQCLF_BORROW; - else if (!strcmp($1, "red")) - $$ = CBQCLF_RED; - else if (!strcmp($1, "ecn")) - $$ = CBQCLF_RED|CBQCLF_ECN; - else if (!strcmp($1, "rio")) - $$ = CBQCLF_RIO; - else { - yyerror("unknown cbq flag \"%s\"", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -priqflags_list : priqflags_item { $$ |= $1; } - | priqflags_list comma priqflags_item { $$ |= $3; } - ; - -priqflags_item : STRING { - if (!strcmp($1, "default")) - $$ = PRCF_DEFAULTCLASS; - else if (!strcmp($1, "red")) - $$ = PRCF_RED; - else if (!strcmp($1, "ecn")) - $$ = PRCF_RED|PRCF_ECN; - else if (!strcmp($1, "rio")) - $$ = PRCF_RIO; - else { - yyerror("unknown priq flag \"%s\"", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -hfsc_opts : { - bzero(&hfsc_opts, - sizeof(struct node_hfsc_opts)); - } - hfscopts_list { - $$ = hfsc_opts; - } - ; - -hfscopts_list : hfscopts_item - | hfscopts_list comma hfscopts_item - ; - -hfscopts_item : LINKSHARE bandwidth { - if (hfsc_opts.linkshare.used) { - yyerror("linkshare already specified"); - YYERROR; - } - hfsc_opts.linkshare.m2 = $2; - hfsc_opts.linkshare.used = 1; - } - | LINKSHARE '(' bandwidth comma NUMBER comma bandwidth ')' - { - if ($5 < 0 || $5 > INT_MAX) { - yyerror("timing in curve out of range"); - YYERROR; - } - if (hfsc_opts.linkshare.used) { - yyerror("linkshare already specified"); - YYERROR; - } - hfsc_opts.linkshare.m1 = $3; - hfsc_opts.linkshare.d = $5; - hfsc_opts.linkshare.m2 = $7; - hfsc_opts.linkshare.used = 1; - } - | REALTIME bandwidth { - if (hfsc_opts.realtime.used) { - yyerror("realtime already specified"); - YYERROR; - } - hfsc_opts.realtime.m2 = $2; - hfsc_opts.realtime.used = 1; - } - | REALTIME '(' bandwidth comma NUMBER comma bandwidth ')' - { - if ($5 < 0 || $5 > INT_MAX) { - yyerror("timing in curve out of range"); - YYERROR; - } - if (hfsc_opts.realtime.used) { - yyerror("realtime already specified"); - YYERROR; - } - hfsc_opts.realtime.m1 = $3; - hfsc_opts.realtime.d = $5; - hfsc_opts.realtime.m2 = $7; - hfsc_opts.realtime.used = 1; - } - | UPPERLIMIT bandwidth { - if (hfsc_opts.upperlimit.used) { - yyerror("upperlimit already specified"); - YYERROR; - } - hfsc_opts.upperlimit.m2 = $2; - hfsc_opts.upperlimit.used = 1; - } - | UPPERLIMIT '(' bandwidth comma NUMBER comma bandwidth ')' - { - if ($5 < 0 || $5 > INT_MAX) { - yyerror("timing in curve out of range"); - YYERROR; - } - if (hfsc_opts.upperlimit.used) { - yyerror("upperlimit already specified"); - YYERROR; - } - hfsc_opts.upperlimit.m1 = $3; - hfsc_opts.upperlimit.d = $5; - hfsc_opts.upperlimit.m2 = $7; - hfsc_opts.upperlimit.used = 1; - } - | STRING { - if (!strcmp($1, "default")) - hfsc_opts.flags |= HFCF_DEFAULTCLASS; - else if (!strcmp($1, "red")) - hfsc_opts.flags |= HFCF_RED; - else if (!strcmp($1, "ecn")) - hfsc_opts.flags |= HFCF_RED|HFCF_ECN; - else if (!strcmp($1, "rio")) - hfsc_opts.flags |= HFCF_RIO; - else { - yyerror("unknown hfsc flag \"%s\"", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -qassign : /* empty */ { $$ = NULL; } - | qassign_item { $$ = $1; } - | '{' optnl qassign_list '}' { $$ = $3; } - ; - -qassign_list : qassign_item optnl { $$ = $1; } - | qassign_list comma qassign_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -qassign_item : STRING { - $$ = calloc(1, sizeof(struct node_queue)); - if ($$ == NULL) - err(1, "qassign_item: calloc"); - if (strlcpy($$->queue, $1, sizeof($$->queue)) >= - sizeof($$->queue)) { - yyerror("queue name '%s' too long (max " - "%d chars)", $1, sizeof($$->queue)-1); - free($1); - free($$); - YYERROR; - } - free($1); - $$->next = NULL; - $$->tail = $$; - } - ; - -pfrule : action dir logquick interface route af proto fromto - filter_opts - { - struct pf_rule r; - struct node_state_opt *o; - struct node_proto *proto; - int srctrack = 0; - int statelock = 0; - int adaptive = 0; - int defaults = 0; - - if (check_rulestate(PFCTL_STATE_FILTER)) - YYERROR; - - memset(&r, 0, sizeof(r)); - - r.action = $1.b1; - switch ($1.b2) { - case PFRULE_RETURNRST: - r.rule_flag |= PFRULE_RETURNRST; - r.return_ttl = $1.w; - break; - case PFRULE_RETURNICMP: - r.rule_flag |= PFRULE_RETURNICMP; - r.return_icmp = $1.w; - r.return_icmp6 = $1.w2; - break; - case PFRULE_RETURN: - r.rule_flag |= PFRULE_RETURN; - r.return_icmp = $1.w; - r.return_icmp6 = $1.w2; - break; - } - r.direction = $2; - r.log = $3.log; - r.logif = $3.logif; - r.quick = $3.quick; - r.prob = $9.prob; - r.rtableid = $9.rtableid; - - r.af = $6; - if ($9.tag) - if (strlcpy(r.tagname, $9.tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if (rule_label(&r, $9.label)) - YYERROR; - free($9.label); - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; - if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { - yyerror("flags always false"); - YYERROR; - } - if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { - for (proto = $7; proto != NULL && - proto->proto != IPPROTO_TCP; - proto = proto->next) - ; /* nothing */ - if (proto == NULL && $7 != NULL) { - if ($9.flags.b1 || $9.flags.b2) - yyerror( - "flags only apply to tcp"); - if ($8.src_os) - yyerror( - "OS fingerprinting only " - "apply to tcp"); - YYERROR; - } -#if 0 - if (($9.flags.b1 & parse_flags("S")) == 0 && - $8.src_os) { - yyerror("OS fingerprinting requires " - "the SYN TCP flag (flags S/SA)"); - YYERROR; - } -#endif - } - - r.tos = $9.tos; - r.keep_state = $9.keep.action; - o = $9.keep.options; - - /* 'keep state' by default on pass rules. */ - if (!r.keep_state && !r.action && - !($9.marker & FOM_KEEP)) { - r.keep_state = PF_STATE_NORMAL; - o = keep_state_defaults; - defaults = 1; - } - - while (o) { - struct node_state_opt *p = o; - - switch (o->type) { - case PF_STATE_OPT_MAX: - if (r.max_states) { - yyerror("state option 'max' " - "multiple definitions"); - YYERROR; - } - r.max_states = o->data.max_states; - break; - case PF_STATE_OPT_NOSYNC: - if (r.rule_flag & PFRULE_NOSYNC) { - yyerror("state option 'sync' " - "multiple definitions"); - YYERROR; - } - r.rule_flag |= PFRULE_NOSYNC; - break; - case PF_STATE_OPT_SRCTRACK: - if (srctrack) { - yyerror("state option " - "'source-track' " - "multiple definitions"); - YYERROR; - } - srctrack = o->data.src_track; - r.rule_flag |= PFRULE_SRCTRACK; - break; - case PF_STATE_OPT_MAX_SRC_STATES: - if (r.max_src_states) { - yyerror("state option " - "'max-src-states' " - "multiple definitions"); - YYERROR; - } - if (o->data.max_src_states == 0) { - yyerror("'max-src-states' must " - "be > 0"); - YYERROR; - } - r.max_src_states = - o->data.max_src_states; - r.rule_flag |= PFRULE_SRCTRACK; - break; - case PF_STATE_OPT_OVERLOAD: - if (r.overload_tblname[0]) { - yyerror("multiple 'overload' " - "table definitions"); - YYERROR; - } - if (strlcpy(r.overload_tblname, - o->data.overload.tblname, - PF_TABLE_NAME_SIZE) >= - PF_TABLE_NAME_SIZE) { - yyerror("state option: " - "strlcpy"); - YYERROR; - } - r.flush = o->data.overload.flush; - break; - case PF_STATE_OPT_MAX_SRC_CONN: - if (r.max_src_conn) { - yyerror("state option " - "'max-src-conn' " - "multiple definitions"); - YYERROR; - } - if (o->data.max_src_conn == 0) { - yyerror("'max-src-conn' " - "must be > 0"); - YYERROR; - } - r.max_src_conn = - o->data.max_src_conn; - r.rule_flag |= PFRULE_SRCTRACK | - PFRULE_RULESRCTRACK; - break; - case PF_STATE_OPT_MAX_SRC_CONN_RATE: - if (r.max_src_conn_rate.limit) { - yyerror("state option " - "'max-src-conn-rate' " - "multiple definitions"); - YYERROR; - } - if (!o->data.max_src_conn_rate.limit || - !o->data.max_src_conn_rate.seconds) { - yyerror("'max-src-conn-rate' " - "values must be > 0"); - YYERROR; - } - if (o->data.max_src_conn_rate.limit > - PF_THRESHOLD_MAX) { - yyerror("'max-src-conn-rate' " - "maximum rate must be < %u", - PF_THRESHOLD_MAX); - YYERROR; - } - r.max_src_conn_rate.limit = - o->data.max_src_conn_rate.limit; - r.max_src_conn_rate.seconds = - o->data.max_src_conn_rate.seconds; - r.rule_flag |= PFRULE_SRCTRACK | - PFRULE_RULESRCTRACK; - break; - case PF_STATE_OPT_MAX_SRC_NODES: - if (r.max_src_nodes) { - yyerror("state option " - "'max-src-nodes' " - "multiple definitions"); - YYERROR; - } - if (o->data.max_src_nodes == 0) { - yyerror("'max-src-nodes' must " - "be > 0"); - YYERROR; - } - r.max_src_nodes = - o->data.max_src_nodes; - r.rule_flag |= PFRULE_SRCTRACK | - PFRULE_RULESRCTRACK; - break; - case PF_STATE_OPT_STATELOCK: - if (statelock) { - yyerror("state locking option: " - "multiple definitions"); - YYERROR; - } - statelock = 1; - r.rule_flag |= o->data.statelock; - break; - case PF_STATE_OPT_SLOPPY: - if (r.rule_flag & PFRULE_STATESLOPPY) { - yyerror("state sloppy option: " - "multiple definitions"); - YYERROR; - } - r.rule_flag |= PFRULE_STATESLOPPY; - break; - case PF_STATE_OPT_TIMEOUT: - if (o->data.timeout.number == - PFTM_ADAPTIVE_START || - o->data.timeout.number == - PFTM_ADAPTIVE_END) - adaptive = 1; - if (r.timeout[o->data.timeout.number]) { - yyerror("state timeout %s " - "multiple definitions", - pf_timeouts[o->data. - timeout.number].name); - YYERROR; - } - r.timeout[o->data.timeout.number] = - o->data.timeout.seconds; - } - o = o->next; - if (!defaults) - free(p); - } - - /* 'flags S/SA' by default on stateful rules */ - if (!r.action && !r.flags && !r.flagset && - !$9.fragment && !($9.marker & FOM_FLAGS) && - r.keep_state) { - r.flags = parse_flags("S"); - r.flagset = parse_flags("SA"); - } - if (!adaptive && r.max_states) { - r.timeout[PFTM_ADAPTIVE_START] = - (r.max_states / 10) * 6; - r.timeout[PFTM_ADAPTIVE_END] = - (r.max_states / 10) * 12; - } - if (r.rule_flag & PFRULE_SRCTRACK) { - if (srctrack == PF_SRCTRACK_GLOBAL && - r.max_src_nodes) { - yyerror("'max-src-nodes' is " - "incompatible with " - "'source-track global'"); - YYERROR; - } - if (srctrack == PF_SRCTRACK_GLOBAL && - r.max_src_conn) { - yyerror("'max-src-conn' is " - "incompatible with " - "'source-track global'"); - YYERROR; - } - if (srctrack == PF_SRCTRACK_GLOBAL && - r.max_src_conn_rate.seconds) { - yyerror("'max-src-conn-rate' is " - "incompatible with " - "'source-track global'"); - YYERROR; - } - if (r.timeout[PFTM_SRC_NODE] < - r.max_src_conn_rate.seconds) - r.timeout[PFTM_SRC_NODE] = - r.max_src_conn_rate.seconds; - r.rule_flag |= PFRULE_SRCTRACK; - if (srctrack == PF_SRCTRACK_RULE) - r.rule_flag |= PFRULE_RULESRCTRACK; - } - if (r.keep_state && !statelock) - r.rule_flag |= default_statelock; - - if ($9.fragment) - r.rule_flag |= PFRULE_FRAGMENT; - r.allow_opts = $9.allowopts; - - decide_address_family($8.src.host, &r.af); - decide_address_family($8.dst.host, &r.af); - - if ($5.rt) { - if (!r.direction) { - yyerror("direction must be explicit " - "with rules that specify routing"); - YYERROR; - } - r.rt = $5.rt; - r.rpool.opts = $5.pool_opts; - if ($5.key != NULL) - memcpy(&r.rpool.key, $5.key, - sizeof(struct pf_poolhashkey)); - } - if (r.rt && r.rt != PF_FASTROUTE) { - decide_address_family($5.host, &r.af); - remove_invalid_hosts(&$5.host, &r.af); - if ($5.host == NULL) { - yyerror("no routing address with " - "matching address family found."); - YYERROR; - } - if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($5.host->next != NULL || - $5.host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($5.host->addr))) - r.rpool.opts |= PF_POOL_ROUNDROBIN; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_table($5.host, "tables are only " - "supported in round-robin routing pools")) - YYERROR; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_alias($5.host, "interface (%s) " - "is only supported in round-robin " - "routing pools")) - YYERROR; - if ($5.host->next != NULL) { - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("r.rpool.opts must " - "be PF_POOL_ROUNDROBIN"); - YYERROR; - } - } - } - if ($9.queues.qname != NULL) { - if (strlcpy(r.qname, $9.queues.qname, - sizeof(r.qname)) >= sizeof(r.qname)) { - yyerror("rule qname too long (max " - "%d chars)", sizeof(r.qname)-1); - YYERROR; - } - free($9.queues.qname); - } - if ($9.queues.pqname != NULL) { - if (strlcpy(r.pqname, $9.queues.pqname, - sizeof(r.pqname)) >= sizeof(r.pqname)) { - yyerror("rule pqname too long (max " - "%d chars)", sizeof(r.pqname)-1); - YYERROR; - } - free($9.queues.pqname); - } -#ifdef __FreeBSD__ - r.divert.port = $9.divert.port; -#else - if ((r.divert.port = $9.divert.port)) { - if (r.direction == PF_OUT) { - if ($9.divert.addr) { - yyerror("address specified " - "for outgoing divert"); - YYERROR; - } - bzero(&r.divert.addr, - sizeof(r.divert.addr)); - } else { - if (!$9.divert.addr) { - yyerror("no address specified " - "for incoming divert"); - YYERROR; - } - if ($9.divert.addr->af != r.af) { - yyerror("address family " - "mismatch for divert"); - YYERROR; - } - r.divert.addr = - $9.divert.addr->addr.v.a.addr; - } - } -#endif - - expand_rule(&r, $4, $5.host, $7, $8.src_os, - $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, ""); - } - ; - -filter_opts : { - bzero(&filter_opts, sizeof filter_opts); - filter_opts.rtableid = -1; - } - filter_opts_l - { $$ = filter_opts; } - | /* empty */ { - bzero(&filter_opts, sizeof filter_opts); - filter_opts.rtableid = -1; - $$ = filter_opts; - } - ; - -filter_opts_l : filter_opts_l filter_opt - | filter_opt - ; - -filter_opt : USER uids { - if (filter_opts.uid) - $2->tail->next = filter_opts.uid; - filter_opts.uid = $2; - } - | GROUP gids { - if (filter_opts.gid) - $2->tail->next = filter_opts.gid; - filter_opts.gid = $2; - } - | flags { - if (filter_opts.marker & FOM_FLAGS) { - yyerror("flags cannot be redefined"); - YYERROR; - } - filter_opts.marker |= FOM_FLAGS; - filter_opts.flags.b1 |= $1.b1; - filter_opts.flags.b2 |= $1.b2; - filter_opts.flags.w |= $1.w; - filter_opts.flags.w2 |= $1.w2; - } - | icmpspec { - if (filter_opts.marker & FOM_ICMP) { - yyerror("icmp-type cannot be redefined"); - YYERROR; - } - filter_opts.marker |= FOM_ICMP; - filter_opts.icmpspec = $1; - } - | TOS tos { - if (filter_opts.marker & FOM_TOS) { - yyerror("tos cannot be redefined"); - YYERROR; - } - filter_opts.marker |= FOM_TOS; - filter_opts.tos = $2; - } - | keep { - if (filter_opts.marker & FOM_KEEP) { - yyerror("modulate or keep cannot be redefined"); - YYERROR; - } - filter_opts.marker |= FOM_KEEP; - filter_opts.keep.action = $1.action; - filter_opts.keep.options = $1.options; - } - | FRAGMENT { - filter_opts.fragment = 1; - } - | ALLOWOPTS { - filter_opts.allowopts = 1; - } - | label { - if (filter_opts.label) { - yyerror("label cannot be redefined"); - YYERROR; - } - filter_opts.label = $1; - } - | qname { - if (filter_opts.queues.qname) { - yyerror("queue cannot be redefined"); - YYERROR; - } - filter_opts.queues = $1; - } - | TAG string { - filter_opts.tag = $2; - } - | not TAGGED string { - filter_opts.match_tag = $3; - filter_opts.match_tag_not = $1; - } - | PROBABILITY probability { - double p; - - p = floor($2 * UINT_MAX + 0.5); - if (p < 0.0 || p > UINT_MAX) { - yyerror("invalid probability: %lf", p); - YYERROR; - } - filter_opts.prob = (u_int32_t)p; - if (filter_opts.prob == 0) - filter_opts.prob = 1; - } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - filter_opts.rtableid = $2; - } - | DIVERTTO portplain { -#ifdef __FreeBSD__ - filter_opts.divert.port = $2.a; - if (!filter_opts.divert.port) { - yyerror("invalid divert port: %u", ntohs($2.a)); - YYERROR; - } -#endif - } - | DIVERTTO STRING PORT portplain { -#ifndef __FreeBSD__ - if ((filter_opts.divert.addr = host($2)) == NULL) { - yyerror("could not parse divert address: %s", - $2); - free($2); - YYERROR; - } -#else - if ($2) -#endif - free($2); - filter_opts.divert.port = $4.a; - if (!filter_opts.divert.port) { - yyerror("invalid divert port: %u", ntohs($4.a)); - YYERROR; - } - } - | DIVERTREPLY { -#ifdef __FreeBSD__ - yyerror("divert-reply has no meaning in FreeBSD pf(4)"); - YYERROR; -#else - filter_opts.divert.port = 1; /* some random value */ -#endif - } - ; - -probability : STRING { - char *e; - double p = strtod($1, &e); - - if (*e == '%') { - p *= 0.01; - e++; - } - if (*e) { - yyerror("invalid probability: %s", $1); - free($1); - YYERROR; - } - free($1); - $$ = p; - } - | NUMBER { - $$ = (double)$1; - } - ; - - -action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; } - | BLOCK blockspec { $$ = $2; $$.b1 = PF_DROP; } - ; - -blockspec : /* empty */ { - $$.b2 = blockpolicy; - $$.w = returnicmpdefault; - $$.w2 = returnicmp6default; - } - | DROP { - $$.b2 = PFRULE_DROP; - $$.w = 0; - $$.w2 = 0; - } - | RETURNRST { - $$.b2 = PFRULE_RETURNRST; - $$.w = 0; - $$.w2 = 0; - } - | RETURNRST '(' TTL NUMBER ')' { - if ($4 < 0 || $4 > 255) { - yyerror("illegal ttl value %d", $4); - YYERROR; - } - $$.b2 = PFRULE_RETURNRST; - $$.w = $4; - $$.w2 = 0; - } - | RETURNICMP { - $$.b2 = PFRULE_RETURNICMP; - $$.w = returnicmpdefault; - $$.w2 = returnicmp6default; - } - | RETURNICMP6 { - $$.b2 = PFRULE_RETURNICMP; - $$.w = returnicmpdefault; - $$.w2 = returnicmp6default; - } - | RETURNICMP '(' reticmpspec ')' { - $$.b2 = PFRULE_RETURNICMP; - $$.w = $3; - $$.w2 = returnicmpdefault; - } - | RETURNICMP6 '(' reticmp6spec ')' { - $$.b2 = PFRULE_RETURNICMP; - $$.w = returnicmpdefault; - $$.w2 = $3; - } - | RETURNICMP '(' reticmpspec comma reticmp6spec ')' { - $$.b2 = PFRULE_RETURNICMP; - $$.w = $3; - $$.w2 = $5; - } - | RETURN { - $$.b2 = PFRULE_RETURN; - $$.w = returnicmpdefault; - $$.w2 = returnicmp6default; - } - ; - -reticmpspec : STRING { - if (!($$ = parseicmpspec($1, AF_INET))) { - free($1); - YYERROR; - } - free($1); - } - | NUMBER { - u_int8_t icmptype; - - if ($1 < 0 || $1 > 255) { - yyerror("invalid icmp code %lu", $1); - YYERROR; - } - icmptype = returnicmpdefault >> 8; - $$ = (icmptype << 8 | $1); - } - ; - -reticmp6spec : STRING { - if (!($$ = parseicmpspec($1, AF_INET6))) { - free($1); - YYERROR; - } - free($1); - } - | NUMBER { - u_int8_t icmptype; - - if ($1 < 0 || $1 > 255) { - yyerror("invalid icmp code %lu", $1); - YYERROR; - } - icmptype = returnicmp6default >> 8; - $$ = (icmptype << 8 | $1); - } - ; - -dir : /* empty */ { $$ = PF_INOUT; } - | IN { $$ = PF_IN; } - | OUT { $$ = PF_OUT; } - ; - -quick : /* empty */ { $$.quick = 0; } - | QUICK { $$.quick = 1; } - ; - -logquick : /* empty */ { $$.log = 0; $$.quick = 0; $$.logif = 0; } - | log { $$ = $1; $$.quick = 0; } - | QUICK { $$.quick = 1; $$.log = 0; $$.logif = 0; } - | log QUICK { $$ = $1; $$.quick = 1; } - | QUICK log { $$ = $2; $$.quick = 1; } - ; - -log : LOG { $$.log = PF_LOG; $$.logif = 0; } - | LOG '(' logopts ')' { - $$.log = PF_LOG | $3.log; - $$.logif = $3.logif; - } - ; - -logopts : logopt { $$ = $1; } - | logopts comma logopt { - $$.log = $1.log | $3.log; - $$.logif = $3.logif; - if ($$.logif == 0) - $$.logif = $1.logif; - } - ; - -logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; } - | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } - | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } - | TO string { - const char *errstr; - u_int i; - - $$.log = 0; - if (strncmp($2, "pflog", 5)) { - yyerror("%s: should be a pflog interface", $2); - free($2); - YYERROR; - } - i = strtonum($2 + 5, 0, 255, &errstr); - if (errstr) { - yyerror("%s: %s", $2, errstr); - free($2); - YYERROR; - } - free($2); - $$.logif = i; - } - ; - -interface : /* empty */ { $$ = NULL; } - | ON if_item_not { $$ = $2; } - | ON '{' optnl if_list '}' { $$ = $4; } - ; - -if_list : if_item_not optnl { $$ = $1; } - | if_list comma if_item_not optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -if_item_not : not if_item { $$ = $2; $$->not = $1; } - ; - -if_item : STRING { - struct node_host *n; - - $$ = calloc(1, sizeof(struct node_if)); - if ($$ == NULL) - err(1, "if_item: calloc"); - if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >= - sizeof($$->ifname)) { - free($1); - free($$); - yyerror("interface name too long"); - YYERROR; - } - - if ((n = ifa_exists($1)) != NULL) - $$->ifa_flags = n->ifa_flags; - - free($1); - $$->not = 0; - $$->next = NULL; - $$->tail = $$; - } - ; - -af : /* empty */ { $$ = 0; } - | INET { $$ = AF_INET; } - | INET6 { $$ = AF_INET6; } - ; - -proto : /* empty */ { $$ = NULL; } - | PROTO proto_item { $$ = $2; } - | PROTO '{' optnl proto_list '}' { $$ = $4; } - ; - -proto_list : proto_item optnl { $$ = $1; } - | proto_list comma proto_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -proto_item : protoval { - u_int8_t pr; - - pr = (u_int8_t)$1; - if (pr == 0) { - yyerror("proto 0 cannot be used"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_proto)); - if ($$ == NULL) - err(1, "proto_item: calloc"); - $$->proto = pr; - $$->next = NULL; - $$->tail = $$; - } - ; - -protoval : STRING { - struct protoent *p; - - p = getprotobyname($1); - if (p == NULL) { - yyerror("unknown protocol %s", $1); - free($1); - YYERROR; - } - $$ = p->p_proto; - free($1); - } - | NUMBER { - if ($1 < 0 || $1 > 255) { - yyerror("protocol outside range"); - YYERROR; - } - } - ; - -fromto : ALL { - $$.src.host = NULL; - $$.src.port = NULL; - $$.dst.host = NULL; - $$.dst.port = NULL; - $$.src_os = NULL; - } - | from os to { - $$.src = $1; - $$.src_os = $2; - $$.dst = $3; - } - ; - -os : /* empty */ { $$ = NULL; } - | OS xos { $$ = $2; } - | OS '{' optnl os_list '}' { $$ = $4; } - ; - -xos : STRING { - $$ = calloc(1, sizeof(struct node_os)); - if ($$ == NULL) - err(1, "os: calloc"); - $$->os = $1; - $$->tail = $$; - } - ; - -os_list : xos optnl { $$ = $1; } - | os_list comma xos optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -from : /* empty */ { - $$.host = NULL; - $$.port = NULL; - } - | FROM ipportspec { - $$ = $2; - } - ; - -to : /* empty */ { - $$.host = NULL; - $$.port = NULL; - } - | TO ipportspec { - if (disallow_urpf_failed($2.host, "\"urpf-failed\" is " - "not permitted in a destination address")) - YYERROR; - $$ = $2; - } - ; - -ipportspec : ipspec { - $$.host = $1; - $$.port = NULL; - } - | ipspec PORT portspec { - $$.host = $1; - $$.port = $3; - } - | PORT portspec { - $$.host = NULL; - $$.port = $2; - } - ; - -optnl : '\n' optnl - | - ; - -ipspec : ANY { $$ = NULL; } - | xhost { $$ = $1; } - | '{' optnl host_list '}' { $$ = $3; } - ; - -toipspec : TO ipspec { $$ = $2; } - | /* empty */ { $$ = NULL; } - ; - -host_list : ipspec optnl { $$ = $1; } - | host_list comma ipspec optnl { - if ($3 == NULL) - $$ = $1; - else if ($1 == NULL) - $$ = $3; - else { - $1->tail->next = $3; - $1->tail = $3->tail; - $$ = $1; - } - } - ; - -xhost : not host { - struct node_host *n; - - for (n = $2; n != NULL; n = n->next) - n->not = $1; - $$ = $2; - } - | not NOROUTE { - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "xhost: calloc"); - $$->addr.type = PF_ADDR_NOROUTE; - $$->next = NULL; - $$->not = $1; - $$->tail = $$; - } - | not URPFFAILED { - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "xhost: calloc"); - $$->addr.type = PF_ADDR_URPFFAILED; - $$->next = NULL; - $$->not = $1; - $$->tail = $$; - } - ; - -host : STRING { - if (($$ = host($1)) == NULL) { - /* error. "any" is handled elsewhere */ - free($1); - yyerror("could not parse host specification"); - YYERROR; - } - free($1); - - } - | STRING '-' STRING { - struct node_host *b, *e; - - if ((b = host($1)) == NULL || (e = host($3)) == NULL) { - free($1); - free($3); - yyerror("could not parse host specification"); - YYERROR; - } - if (b->af != e->af || - b->addr.type != PF_ADDR_ADDRMASK || - e->addr.type != PF_ADDR_ADDRMASK || - unmask(&b->addr.v.a.mask, b->af) != - (b->af == AF_INET ? 32 : 128) || - unmask(&e->addr.v.a.mask, e->af) != - (e->af == AF_INET ? 32 : 128) || - b->next != NULL || b->not || - e->next != NULL || e->not) { - free(b); - free(e); - free($1); - free($3); - yyerror("invalid address range"); - YYERROR; - } - memcpy(&b->addr.v.a.mask, &e->addr.v.a.addr, - sizeof(b->addr.v.a.mask)); - b->addr.type = PF_ADDR_RANGE; - $$ = b; - free(e); - free($1); - free($3); - } - | STRING '/' NUMBER { - char *buf; - - if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1) - err(1, "host: asprintf"); - free($1); - if (($$ = host(buf)) == NULL) { - /* error. "any" is handled elsewhere */ - free(buf); - yyerror("could not parse host specification"); - YYERROR; - } - free(buf); - } - | NUMBER '/' NUMBER { - char *buf; - - /* ie. for 10/8 parsing */ -#ifdef __FreeBSD__ - if (asprintf(&buf, "%lld/%lld", (long long)$1, (long long)$3) == -1) -#else - if (asprintf(&buf, "%lld/%lld", $1, $3) == -1) -#endif - err(1, "host: asprintf"); - if (($$ = host(buf)) == NULL) { - /* error. "any" is handled elsewhere */ - free(buf); - yyerror("could not parse host specification"); - YYERROR; - } - free(buf); - } - | dynaddr - | dynaddr '/' NUMBER { - struct node_host *n; - - if ($3 < 0 || $3 > 128) { - yyerror("bit number too big"); - YYERROR; - } - $$ = $1; - for (n = $1; n != NULL; n = n->next) - set_ipmask(n, $3); - } - | '<' STRING '>' { - if (strlen($2) >= PF_TABLE_NAME_SIZE) { - yyerror("table name '%s' too long", $2); - free($2); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "host: calloc"); - $$->addr.type = PF_ADDR_TABLE; - if (strlcpy($$->addr.v.tblname, $2, - sizeof($$->addr.v.tblname)) >= - sizeof($$->addr.v.tblname)) - errx(1, "host: strlcpy"); - free($2); - $$->next = NULL; - $$->tail = $$; - } - ; - -number : NUMBER - | STRING { - u_long ulval; - - if (atoul($1, &ulval) == -1) { - yyerror("%s is not a number", $1); - free($1); - YYERROR; - } else - $$ = ulval; - free($1); - } - ; - -dynaddr : '(' STRING ')' { - int flags = 0; - char *p, *op; - - op = $2; - if (!isalpha(op[0])) { - yyerror("invalid interface name '%s'", op); - free(op); - YYERROR; - } - while ((p = strrchr($2, ':')) != NULL) { - if (!strcmp(p+1, "network")) - flags |= PFI_AFLAG_NETWORK; - else if (!strcmp(p+1, "broadcast")) - flags |= PFI_AFLAG_BROADCAST; - else if (!strcmp(p+1, "peer")) - flags |= PFI_AFLAG_PEER; - else if (!strcmp(p+1, "0")) - flags |= PFI_AFLAG_NOALIAS; - else { - yyerror("interface %s has bad modifier", - $2); - free(op); - YYERROR; - } - *p = '\0'; - } - if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { - free(op); - yyerror("illegal combination of " - "interface modifiers"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "address: calloc"); - $$->af = 0; - set_ipmask($$, 128); - $$->addr.type = PF_ADDR_DYNIFTL; - $$->addr.iflags = flags; - if (strlcpy($$->addr.v.ifname, $2, - sizeof($$->addr.v.ifname)) >= - sizeof($$->addr.v.ifname)) { - free(op); - free($$); - yyerror("interface name too long"); - YYERROR; - } - free(op); - $$->next = NULL; - $$->tail = $$; - } - ; - -portspec : port_item { $$ = $1; } - | '{' optnl port_list '}' { $$ = $3; } - ; - -port_list : port_item optnl { $$ = $1; } - | port_list comma port_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -port_item : portrange { - $$ = calloc(1, sizeof(struct node_port)); - if ($$ == NULL) - err(1, "port_item: calloc"); - $$->port[0] = $1.a; - $$->port[1] = $1.b; - if ($1.t) - $$->op = PF_OP_RRG; - else - $$->op = PF_OP_EQ; - $$->next = NULL; - $$->tail = $$; - } - | unaryop portrange { - if ($2.t) { - yyerror("':' cannot be used with an other " - "port operator"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_port)); - if ($$ == NULL) - err(1, "port_item: calloc"); - $$->port[0] = $2.a; - $$->port[1] = $2.b; - $$->op = $1; - $$->next = NULL; - $$->tail = $$; - } - | portrange PORTBINARY portrange { - if ($1.t || $3.t) { - yyerror("':' cannot be used with an other " - "port operator"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_port)); - if ($$ == NULL) - err(1, "port_item: calloc"); - $$->port[0] = $1.a; - $$->port[1] = $3.a; - $$->op = $2; - $$->next = NULL; - $$->tail = $$; - } - ; - -portplain : numberstring { - if (parseport($1, &$$, 0) == -1) { - free($1); - YYERROR; - } - free($1); - } - ; - -portrange : numberstring { - if (parseport($1, &$$, PPORT_RANGE) == -1) { - free($1); - YYERROR; - } - free($1); - } - ; - -uids : uid_item { $$ = $1; } - | '{' optnl uid_list '}' { $$ = $3; } - ; - -uid_list : uid_item optnl { $$ = $1; } - | uid_list comma uid_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -uid_item : uid { - $$ = calloc(1, sizeof(struct node_uid)); - if ($$ == NULL) - err(1, "uid_item: calloc"); - $$->uid[0] = $1; - $$->uid[1] = $1; - $$->op = PF_OP_EQ; - $$->next = NULL; - $$->tail = $$; - } - | unaryop uid { - if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { - yyerror("user unknown requires operator = or " - "!="); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_uid)); - if ($$ == NULL) - err(1, "uid_item: calloc"); - $$->uid[0] = $2; - $$->uid[1] = $2; - $$->op = $1; - $$->next = NULL; - $$->tail = $$; - } - | uid PORTBINARY uid { - if ($1 == UID_MAX || $3 == UID_MAX) { - yyerror("user unknown requires operator = or " - "!="); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_uid)); - if ($$ == NULL) - err(1, "uid_item: calloc"); - $$->uid[0] = $1; - $$->uid[1] = $3; - $$->op = $2; - $$->next = NULL; - $$->tail = $$; - } - ; - -uid : STRING { - if (!strcmp($1, "unknown")) - $$ = UID_MAX; - else { - struct passwd *pw; - - if ((pw = getpwnam($1)) == NULL) { - yyerror("unknown user %s", $1); - free($1); - YYERROR; - } - $$ = pw->pw_uid; - } - free($1); - } - | NUMBER { - if ($1 < 0 || $1 >= UID_MAX) { - yyerror("illegal uid value %lu", $1); - YYERROR; - } - $$ = $1; - } - ; - -gids : gid_item { $$ = $1; } - | '{' optnl gid_list '}' { $$ = $3; } - ; - -gid_list : gid_item optnl { $$ = $1; } - | gid_list comma gid_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -gid_item : gid { - $$ = calloc(1, sizeof(struct node_gid)); - if ($$ == NULL) - err(1, "gid_item: calloc"); - $$->gid[0] = $1; - $$->gid[1] = $1; - $$->op = PF_OP_EQ; - $$->next = NULL; - $$->tail = $$; - } - | unaryop gid { - if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { - yyerror("group unknown requires operator = or " - "!="); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_gid)); - if ($$ == NULL) - err(1, "gid_item: calloc"); - $$->gid[0] = $2; - $$->gid[1] = $2; - $$->op = $1; - $$->next = NULL; - $$->tail = $$; - } - | gid PORTBINARY gid { - if ($1 == GID_MAX || $3 == GID_MAX) { - yyerror("group unknown requires operator = or " - "!="); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_gid)); - if ($$ == NULL) - err(1, "gid_item: calloc"); - $$->gid[0] = $1; - $$->gid[1] = $3; - $$->op = $2; - $$->next = NULL; - $$->tail = $$; - } - ; - -gid : STRING { - if (!strcmp($1, "unknown")) - $$ = GID_MAX; - else { - struct group *grp; - - if ((grp = getgrnam($1)) == NULL) { - yyerror("unknown group %s", $1); - free($1); - YYERROR; - } - $$ = grp->gr_gid; - } - free($1); - } - | NUMBER { - if ($1 < 0 || $1 >= GID_MAX) { - yyerror("illegal gid value %lu", $1); - YYERROR; - } - $$ = $1; - } - ; - -flag : STRING { - int f; - - if ((f = parse_flags($1)) < 0) { - yyerror("bad flags %s", $1); - free($1); - YYERROR; - } - free($1); - $$.b1 = f; - } - ; - -flags : FLAGS flag '/' flag { $$.b1 = $2.b1; $$.b2 = $4.b1; } - | FLAGS '/' flag { $$.b1 = 0; $$.b2 = $3.b1; } - | FLAGS ANY { $$.b1 = 0; $$.b2 = 0; } - ; - -icmpspec : ICMPTYPE icmp_item { $$ = $2; } - | ICMPTYPE '{' optnl icmp_list '}' { $$ = $4; } - | ICMP6TYPE icmp6_item { $$ = $2; } - | ICMP6TYPE '{' optnl icmp6_list '}' { $$ = $4; } - ; - -icmp_list : icmp_item optnl { $$ = $1; } - | icmp_list comma icmp_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -icmp6_list : icmp6_item optnl { $$ = $1; } - | icmp6_list comma icmp6_item optnl { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -icmp_item : icmptype { - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = 0; - $$->proto = IPPROTO_ICMP; - $$->next = NULL; - $$->tail = $$; - } - | icmptype CODE STRING { - const struct icmpcodeent *p; - - if ((p = geticmpcodebyname($1-1, $3, AF_INET)) == NULL) { - yyerror("unknown icmp-code %s", $3); - free($3); - YYERROR; - } - - free($3); - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = p->code + 1; - $$->proto = IPPROTO_ICMP; - $$->next = NULL; - $$->tail = $$; - } - | icmptype CODE NUMBER { - if ($3 < 0 || $3 > 255) { - yyerror("illegal icmp-code %lu", $3); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = $3 + 1; - $$->proto = IPPROTO_ICMP; - $$->next = NULL; - $$->tail = $$; - } - ; - -icmp6_item : icmp6type { - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = 0; - $$->proto = IPPROTO_ICMPV6; - $$->next = NULL; - $$->tail = $$; - } - | icmp6type CODE STRING { - const struct icmpcodeent *p; - - if ((p = geticmpcodebyname($1-1, $3, AF_INET6)) == NULL) { - yyerror("unknown icmp6-code %s", $3); - free($3); - YYERROR; - } - free($3); - - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = p->code + 1; - $$->proto = IPPROTO_ICMPV6; - $$->next = NULL; - $$->tail = $$; - } - | icmp6type CODE NUMBER { - if ($3 < 0 || $3 > 255) { - yyerror("illegal icmp-code %lu", $3); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_icmp)); - if ($$ == NULL) - err(1, "icmp_item: calloc"); - $$->type = $1; - $$->code = $3 + 1; - $$->proto = IPPROTO_ICMPV6; - $$->next = NULL; - $$->tail = $$; - } - ; - -icmptype : STRING { - const struct icmptypeent *p; - - if ((p = geticmptypebyname($1, AF_INET)) == NULL) { - yyerror("unknown icmp-type %s", $1); - free($1); - YYERROR; - } - $$ = p->type + 1; - free($1); - } - | NUMBER { - if ($1 < 0 || $1 > 255) { - yyerror("illegal icmp-type %lu", $1); - YYERROR; - } - $$ = $1 + 1; - } - ; - -icmp6type : STRING { - const struct icmptypeent *p; - - if ((p = geticmptypebyname($1, AF_INET6)) == - NULL) { - yyerror("unknown icmp6-type %s", $1); - free($1); - YYERROR; - } - $$ = p->type + 1; - free($1); - } - | NUMBER { - if ($1 < 0 || $1 > 255) { - yyerror("illegal icmp6-type %lu", $1); - YYERROR; - } - $$ = $1 + 1; - } - ; - -tos : STRING { - if (!strcmp($1, "lowdelay")) - $$ = IPTOS_LOWDELAY; - else if (!strcmp($1, "throughput")) - $$ = IPTOS_THROUGHPUT; - else if (!strcmp($1, "reliability")) - $$ = IPTOS_RELIABILITY; - else if ($1[0] == '0' && $1[1] == 'x') - $$ = strtoul($1, NULL, 16); - else - $$ = 0; /* flag bad argument */ - if (!$$ || $$ > 255) { - yyerror("illegal tos value %s", $1); - free($1); - YYERROR; - } - free($1); - } - | NUMBER { - $$ = $1; - if (!$$ || $$ > 255) { - yyerror("illegal tos value %s", $1); - YYERROR; - } - } - ; - -sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; } - | SOURCETRACK GLOBAL { $$ = PF_SRCTRACK_GLOBAL; } - | SOURCETRACK RULE { $$ = PF_SRCTRACK_RULE; } - ; - -statelock : IFBOUND { - $$ = PFRULE_IFBOUND; - } - | FLOATING { - $$ = 0; - } - ; - -keep : NO STATE { - $$.action = 0; - $$.options = NULL; - } - | KEEP STATE state_opt_spec { - $$.action = PF_STATE_NORMAL; - $$.options = $3; - } - | MODULATE STATE state_opt_spec { - $$.action = PF_STATE_MODULATE; - $$.options = $3; - } - | SYNPROXY STATE state_opt_spec { - $$.action = PF_STATE_SYNPROXY; - $$.options = $3; - } - ; - -flush : /* empty */ { $$ = 0; } - | FLUSH { $$ = PF_FLUSH; } - | FLUSH GLOBAL { - $$ = PF_FLUSH | PF_FLUSH_GLOBAL; - } - ; - -state_opt_spec : '(' state_opt_list ')' { $$ = $2; } - | /* empty */ { $$ = NULL; } - ; - -state_opt_list : state_opt_item { $$ = $1; } - | state_opt_list comma state_opt_item { - $1->tail->next = $3; - $1->tail = $3; - $$ = $1; - } - ; - -state_opt_item : MAXIMUM NUMBER { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_MAX; - $$->data.max_states = $2; - $$->next = NULL; - $$->tail = $$; - } - | NOSYNC { - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_NOSYNC; - $$->next = NULL; - $$->tail = $$; - } - | MAXSRCSTATES NUMBER { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_MAX_SRC_STATES; - $$->data.max_src_states = $2; - $$->next = NULL; - $$->tail = $$; - } - | MAXSRCCONN NUMBER { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_MAX_SRC_CONN; - $$->data.max_src_conn = $2; - $$->next = NULL; - $$->tail = $$; - } - | MAXSRCCONNRATE NUMBER '/' NUMBER { - if ($2 < 0 || $2 > UINT_MAX || - $4 < 0 || $4 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE; - $$->data.max_src_conn_rate.limit = $2; - $$->data.max_src_conn_rate.seconds = $4; - $$->next = NULL; - $$->tail = $$; - } - | OVERLOAD '<' STRING '>' flush { - if (strlen($3) >= PF_TABLE_NAME_SIZE) { - yyerror("table name '%s' too long", $3); - free($3); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - if (strlcpy($$->data.overload.tblname, $3, - PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE) - errx(1, "state_opt_item: strlcpy"); - free($3); - $$->type = PF_STATE_OPT_OVERLOAD; - $$->data.overload.flush = $5; - $$->next = NULL; - $$->tail = $$; - } - | MAXSRCNODES NUMBER { - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_MAX_SRC_NODES; - $$->data.max_src_nodes = $2; - $$->next = NULL; - $$->tail = $$; - } - | sourcetrack { - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_SRCTRACK; - $$->data.src_track = $1; - $$->next = NULL; - $$->tail = $$; - } - | statelock { - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_STATELOCK; - $$->data.statelock = $1; - $$->next = NULL; - $$->tail = $$; - } - | SLOPPY { - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_SLOPPY; - $$->next = NULL; - $$->tail = $$; - } - | STRING NUMBER { - int i; - - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - for (i = 0; pf_timeouts[i].name && - strcmp(pf_timeouts[i].name, $1); ++i) - ; /* nothing */ - if (!pf_timeouts[i].name) { - yyerror("illegal timeout name %s", $1); - free($1); - YYERROR; - } - if (strchr(pf_timeouts[i].name, '.') == NULL) { - yyerror("illegal state timeout %s", $1); - free($1); - YYERROR; - } - free($1); - $$ = calloc(1, sizeof(struct node_state_opt)); - if ($$ == NULL) - err(1, "state_opt_item: calloc"); - $$->type = PF_STATE_OPT_TIMEOUT; - $$->data.timeout.number = pf_timeouts[i].timeout; - $$->data.timeout.seconds = $2; - $$->next = NULL; - $$->tail = $$; - } - ; - -label : LABEL STRING { - $$ = $2; - } - ; - -qname : QUEUE STRING { - $$.qname = $2; - $$.pqname = NULL; - } - | QUEUE '(' STRING ')' { - $$.qname = $3; - $$.pqname = NULL; - } - | QUEUE '(' STRING comma STRING ')' { - $$.qname = $3; - $$.pqname = $5; - } - ; - -no : /* empty */ { $$ = 0; } - | NO { $$ = 1; } - ; - -portstar : numberstring { - if (parseport($1, &$$, PPORT_RANGE|PPORT_STAR) == -1) { - free($1); - YYERROR; - } - free($1); - } - ; - -redirspec : host { $$ = $1; } - | '{' optnl redir_host_list '}' { $$ = $3; } - ; - -redir_host_list : host optnl { $$ = $1; } - | redir_host_list comma host optnl { - $1->tail->next = $3; - $1->tail = $3->tail; - $$ = $1; - } - ; - -redirpool : /* empty */ { $$ = NULL; } - | ARROW redirspec { - $$ = calloc(1, sizeof(struct redirection)); - if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport.a = $$->rport.b = $$->rport.t = 0; - } - | ARROW redirspec PORT portstar { - $$ = calloc(1, sizeof(struct redirection)); - if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport = $4; - } - ; - -hashkey : /* empty */ - { - $$ = calloc(1, sizeof(struct pf_poolhashkey)); - if ($$ == NULL) - err(1, "hashkey: calloc"); - $$->key32[0] = arc4random(); - $$->key32[1] = arc4random(); - $$->key32[2] = arc4random(); - $$->key32[3] = arc4random(); - } - | string - { - if (!strncmp($1, "0x", 2)) { - if (strlen($1) != 34) { - free($1); - yyerror("hex key must be 128 bits " - "(32 hex digits) long"); - YYERROR; - } - $$ = calloc(1, sizeof(struct pf_poolhashkey)); - if ($$ == NULL) - err(1, "hashkey: calloc"); - - if (sscanf($1, "0x%8x%8x%8x%8x", - &$$->key32[0], &$$->key32[1], - &$$->key32[2], &$$->key32[3]) != 4) { - free($$); - free($1); - yyerror("invalid hex key"); - YYERROR; - } - } else { - MD5_CTX context; - - $$ = calloc(1, sizeof(struct pf_poolhashkey)); - if ($$ == NULL) - err(1, "hashkey: calloc"); - MD5Init(&context); - MD5Update(&context, (unsigned char *)$1, - strlen($1)); - MD5Final((unsigned char *)$$, &context); - HTONL($$->key32[0]); - HTONL($$->key32[1]); - HTONL($$->key32[2]); - HTONL($$->key32[3]); - } - free($1); - } - ; - -pool_opts : { bzero(&pool_opts, sizeof pool_opts); } - pool_opts_l - { $$ = pool_opts; } - | /* empty */ { - bzero(&pool_opts, sizeof pool_opts); - $$ = pool_opts; - } - ; - -pool_opts_l : pool_opts_l pool_opt - | pool_opt - ; - -pool_opt : BITMASK { - if (pool_opts.type) { - yyerror("pool type cannot be redefined"); - YYERROR; - } - pool_opts.type = PF_POOL_BITMASK; - } - | RANDOM { - if (pool_opts.type) { - yyerror("pool type cannot be redefined"); - YYERROR; - } - pool_opts.type = PF_POOL_RANDOM; - } - | SOURCEHASH hashkey { - if (pool_opts.type) { - yyerror("pool type cannot be redefined"); - YYERROR; - } - pool_opts.type = PF_POOL_SRCHASH; - pool_opts.key = $2; - } - | ROUNDROBIN { - if (pool_opts.type) { - yyerror("pool type cannot be redefined"); - YYERROR; - } - pool_opts.type = PF_POOL_ROUNDROBIN; - } - | STATICPORT { - if (pool_opts.staticport) { - yyerror("static-port cannot be redefined"); - YYERROR; - } - pool_opts.staticport = 1; - } - | STICKYADDRESS { - if (filter_opts.marker & POM_STICKYADDRESS) { - yyerror("sticky-address cannot be redefined"); - YYERROR; - } - pool_opts.marker |= POM_STICKYADDRESS; - pool_opts.opts |= PF_POOL_STICKYADDR; - } - ; - -redirection : /* empty */ { $$ = NULL; } - | ARROW host { - $$ = calloc(1, sizeof(struct redirection)); - if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport.a = $$->rport.b = $$->rport.t = 0; - } - | ARROW host PORT portstar { - $$ = calloc(1, sizeof(struct redirection)); - if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport = $4; - } - ; - -natpasslog : /* empty */ { $$.b1 = $$.b2 = 0; $$.w2 = 0; } - | PASS { $$.b1 = 1; $$.b2 = 0; $$.w2 = 0; } - | PASS log { $$.b1 = 1; $$.b2 = $2.log; $$.w2 = $2.logif; } - | log { $$.b1 = 0; $$.b2 = $1.log; $$.w2 = $1.logif; } - ; - -nataction : no NAT natpasslog { - if ($1 && $3.b1) { - yyerror("\"pass\" not valid with \"no\""); - YYERROR; - } - if ($1) - $$.b1 = PF_NONAT; - else - $$.b1 = PF_NAT; - $$.b2 = $3.b1; - $$.w = $3.b2; - $$.w2 = $3.w2; - } - | no RDR natpasslog { - if ($1 && $3.b1) { - yyerror("\"pass\" not valid with \"no\""); - YYERROR; - } - if ($1) - $$.b1 = PF_NORDR; - else - $$.b1 = PF_RDR; - $$.b2 = $3.b1; - $$.w = $3.b2; - $$.w2 = $3.w2; - } - ; - -natrule : nataction interface af proto fromto tag tagged rtable - redirpool pool_opts - { - struct pf_rule r; - - if (check_rulestate(PFCTL_STATE_NAT)) - YYERROR; - - memset(&r, 0, sizeof(r)); - - r.action = $1.b1; - r.natpass = $1.b2; - r.log = $1.w; - r.logif = $1.w2; - r.af = $3; - - if (!r.af) { - if ($5.src.host && $5.src.host->af && - !$5.src.host->ifindex) - r.af = $5.src.host->af; - else if ($5.dst.host && $5.dst.host->af && - !$5.dst.host->ifindex) - r.af = $5.dst.host->af; - } - - if ($6 != NULL) - if (strlcpy(r.tagname, $6, PF_TAG_NAME_SIZE) >= - PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - - if ($7.name) - if (strlcpy(r.match_tagname, $7.name, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $7.neg; - r.rtableid = $8; - - if (r.action == PF_NONAT || r.action == PF_NORDR) { - if ($9 != NULL) { - yyerror("translation rule with 'no' " - "does not need '->'"); - YYERROR; - } - } else { - if ($9 == NULL || $9->host == NULL) { - yyerror("translation rule requires '-> " - "address'"); - YYERROR; - } - if (!r.af && ! $9->host->ifindex) - r.af = $9->host->af; - - remove_invalid_hosts(&$9->host, &r.af); - if (invalid_redirect($9->host, r.af)) - YYERROR; - if (check_netmask($9->host, r.af)) - YYERROR; - - r.rpool.proxy_port[0] = ntohs($9->rport.a); - - switch (r.action) { - case PF_RDR: - if (!$9->rport.b && $9->rport.t && - $5.dst.port != NULL) { - r.rpool.proxy_port[1] = - ntohs($9->rport.a) + - (ntohs( - $5.dst.port->port[1]) - - ntohs( - $5.dst.port->port[0])); - } else - r.rpool.proxy_port[1] = - ntohs($9->rport.b); - break; - case PF_NAT: - r.rpool.proxy_port[1] = - ntohs($9->rport.b); - if (!r.rpool.proxy_port[0] && - !r.rpool.proxy_port[1]) { - r.rpool.proxy_port[0] = - PF_NAT_PROXY_PORT_LOW; - r.rpool.proxy_port[1] = - PF_NAT_PROXY_PORT_HIGH; - } else if (!r.rpool.proxy_port[1]) - r.rpool.proxy_port[1] = - r.rpool.proxy_port[0]; - break; - default: - break; - } - - r.rpool.opts = $10.type; - if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($9->host->next != NULL || - $9->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($9->host->addr))) - r.rpool.opts = PF_POOL_ROUNDROBIN; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_table($9->host, "tables are only " - "supported in round-robin redirection " - "pools")) - YYERROR; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_alias($9->host, "interface (%s) " - "is only supported in round-robin " - "redirection pools")) - YYERROR; - if ($9->host->next != NULL) { - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("only round-robin " - "valid for multiple " - "redirection addresses"); - YYERROR; - } - } - } - - if ($10.key != NULL) - memcpy(&r.rpool.key, $10.key, - sizeof(struct pf_poolhashkey)); - - if ($10.opts) - r.rpool.opts |= $10.opts; - - if ($10.staticport) { - if (r.action != PF_NAT) { - yyerror("the 'static-port' option is " - "only valid with nat rules"); - YYERROR; - } - if (r.rpool.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rpool.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'static-port' option can't" - " be used when specifying a port" - " range"); - YYERROR; - } - r.rpool.proxy_port[0] = 0; - r.rpool.proxy_port[1] = 0; - } - - expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, - $5.src_os, $5.src.host, $5.src.port, $5.dst.host, - $5.dst.port, 0, 0, 0, ""); - free($9); - } - ; - -binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag - tagged rtable redirection - { - struct pf_rule binat; - struct pf_pooladdr *pa; - - if (check_rulestate(PFCTL_STATE_NAT)) - YYERROR; - if (disallow_urpf_failed($9, "\"urpf-failed\" is not " - "permitted as a binat destination")) - YYERROR; - - memset(&binat, 0, sizeof(binat)); - - if ($1 && $3.b1) { - yyerror("\"pass\" not valid with \"no\""); - YYERROR; - } - if ($1) - binat.action = PF_NOBINAT; - else - binat.action = PF_BINAT; - binat.natpass = $3.b1; - binat.log = $3.b2; - binat.logif = $3.w2; - binat.af = $5; - if (!binat.af && $8 != NULL && $8->af) - binat.af = $8->af; - if (!binat.af && $9 != NULL && $9->af) - binat.af = $9->af; - - if (!binat.af && $13 != NULL && $13->host) - binat.af = $13->host->af; - if (!binat.af) { - yyerror("address family (inet/inet6) " - "undefined"); - YYERROR; - } - - if ($4 != NULL) { - memcpy(binat.ifname, $4->ifname, - sizeof(binat.ifname)); - binat.ifnot = $4->not; - free($4); - } - - if ($10 != NULL) - if (strlcpy(binat.tagname, $10, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($11.name) - if (strlcpy(binat.match_tagname, $11.name, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - binat.match_tag_not = $11.neg; - binat.rtableid = $12; - - if ($6 != NULL) { - binat.proto = $6->proto; - free($6); - } - - if ($8 != NULL && disallow_table($8, "invalid use of " - "table <%s> as the source address of a binat rule")) - YYERROR; - if ($8 != NULL && disallow_alias($8, "invalid use of " - "interface (%s) as the source address of a binat " - "rule")) - YYERROR; - if ($13 != NULL && $13->host != NULL && disallow_table( - $13->host, "invalid use of table <%s> as the " - "redirect address of a binat rule")) - YYERROR; - if ($13 != NULL && $13->host != NULL && disallow_alias( - $13->host, "invalid use of interface (%s) as the " - "redirect address of a binat rule")) - YYERROR; - - if ($8 != NULL) { - if ($8->next) { - yyerror("multiple binat ip addresses"); - YYERROR; - } - if ($8->addr.type == PF_ADDR_DYNIFTL) - $8->af = binat.af; - if ($8->af != binat.af) { - yyerror("binat ip versions must match"); - YYERROR; - } - if (check_netmask($8, binat.af)) - YYERROR; - memcpy(&binat.src.addr, &$8->addr, - sizeof(binat.src.addr)); - free($8); - } - if ($9 != NULL) { - if ($9->next) { - yyerror("multiple binat ip addresses"); - YYERROR; - } - if ($9->af != binat.af && $9->af) { - yyerror("binat ip versions must match"); - YYERROR; - } - if (check_netmask($9, binat.af)) - YYERROR; - memcpy(&binat.dst.addr, &$9->addr, - sizeof(binat.dst.addr)); - binat.dst.neg = $9->not; - free($9); - } - - if (binat.action == PF_NOBINAT) { - if ($13 != NULL) { - yyerror("'no binat' rule does not need" - " '->'"); - YYERROR; - } - } else { - if ($13 == NULL || $13->host == NULL) { - yyerror("'binat' rule requires" - " '-> address'"); - YYERROR; - } - - remove_invalid_hosts(&$13->host, &binat.af); - if (invalid_redirect($13->host, binat.af)) - YYERROR; - if ($13->host->next != NULL) { - yyerror("binat rule must redirect to " - "a single address"); - YYERROR; - } - if (check_netmask($13->host, binat.af)) - YYERROR; - - if (!PF_AZERO(&binat.src.addr.v.a.mask, - binat.af) && - !PF_AEQ(&binat.src.addr.v.a.mask, - &$13->host->addr.v.a.mask, binat.af)) { - yyerror("'binat' source mask and " - "redirect mask must be the same"); - YYERROR; - } - - TAILQ_INIT(&binat.rpool.list); - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "binat: calloc"); - pa->addr = $13->host->addr; - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&binat.rpool.list, - pa, entries); - - free($13); - } - - pfctl_add_rule(pf, &binat, ""); - } - ; - -tag : /* empty */ { $$ = NULL; } - | TAG STRING { $$ = $2; } - ; - -tagged : /* empty */ { $$.neg = 0; $$.name = NULL; } - | not TAGGED string { $$.neg = $1; $$.name = $3; } - ; - -rtable : /* empty */ { $$ = -1; } - | RTABLE NUMBER { - if ($2 < 0 || $2 > rt_tableid_max()) { - yyerror("invalid rtable id"); - YYERROR; - } - $$ = $2; - } - ; - -route_host : STRING { - $$ = calloc(1, sizeof(struct node_host)); - if ($$ == NULL) - err(1, "route_host: calloc"); - $$->ifname = $1; - set_ipmask($$, 128); - $$->next = NULL; - $$->tail = $$; - } - | '(' STRING host ')' { - $$ = $3; - $$->ifname = $2; - } - ; - -route_host_list : route_host optnl { $$ = $1; } - | route_host_list comma route_host optnl { - if ($1->af == 0) - $1->af = $3->af; - if ($1->af != $3->af) { - yyerror("all pool addresses must be in the " - "same address family"); - YYERROR; - } - $1->tail->next = $3; - $1->tail = $3->tail; - $$ = $1; - } - ; - -routespec : route_host { $$ = $1; } - | '{' optnl route_host_list '}' { $$ = $3; } - ; - -route : /* empty */ { - $$.host = NULL; - $$.rt = 0; - $$.pool_opts = 0; - } - | FASTROUTE { - $$.host = NULL; - $$.rt = PF_FASTROUTE; - $$.pool_opts = 0; - } - | ROUTETO routespec pool_opts { - $$.host = $2; - $$.rt = PF_ROUTETO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; - } - | REPLYTO routespec pool_opts { - $$.host = $2; - $$.rt = PF_REPLYTO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; - } - | DUPTO routespec pool_opts { - $$.host = $2; - $$.rt = PF_DUPTO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; - } - ; - -timeout_spec : STRING NUMBER - { - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($1); - YYERROR; - } - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - if (pfctl_set_timeout(pf, $1, $2, 0) != 0) { - yyerror("unknown timeout %s", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -timeout_list : timeout_list comma timeout_spec optnl - | timeout_spec optnl - ; - -limit_spec : STRING NUMBER - { - if (check_rulestate(PFCTL_STATE_OPTION)) { - free($1); - YYERROR; - } - if ($2 < 0 || $2 > UINT_MAX) { - yyerror("only positive values permitted"); - YYERROR; - } - if (pfctl_set_limit(pf, $1, $2) != 0) { - yyerror("unable to set limit %s %u", $1, $2); - free($1); - YYERROR; - } - free($1); - } - ; - -limit_list : limit_list comma limit_spec optnl - | limit_spec optnl - ; - -comma : ',' - | /* empty */ - ; - -yesno : NO { $$ = 0; } - | STRING { - if (!strcmp($1, "yes")) - $$ = 1; - else { - yyerror("invalid value '%s', expected 'yes' " - "or 'no'", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -unaryop : '=' { $$ = PF_OP_EQ; } - | '!' '=' { $$ = PF_OP_NE; } - | '<' '=' { $$ = PF_OP_LE; } - | '<' { $$ = PF_OP_LT; } - | '>' '=' { $$ = PF_OP_GE; } - | '>' { $$ = PF_OP_GT; } - ; - -%% - -int -yyerror(const char *fmt, ...) -{ - va_list ap; - - file->errors++; - va_start(ap, fmt); - fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - return (0); -} - -int -disallow_table(struct node_host *h, const char *fmt) -{ - for (; h != NULL; h = h->next) - if (h->addr.type == PF_ADDR_TABLE) { - yyerror(fmt, h->addr.v.tblname); - return (1); - } - return (0); -} - -int -disallow_urpf_failed(struct node_host *h, const char *fmt) -{ - for (; h != NULL; h = h->next) - if (h->addr.type == PF_ADDR_URPFFAILED) { - yyerror(fmt); - return (1); - } - return (0); -} - -int -disallow_alias(struct node_host *h, const char *fmt) -{ - for (; h != NULL; h = h->next) - if (DYNIF_MULTIADDR(h->addr)) { - yyerror(fmt, h->addr.v.tblname); - return (1); - } - return (0); -} - -int -rule_consistent(struct pf_rule *r, int anchor_call) -{ - int problems = 0; - - switch (r->action) { - case PF_PASS: - case PF_DROP: - case PF_SCRUB: - case PF_NOSCRUB: - problems = filter_consistent(r, anchor_call); - break; - case PF_NAT: - case PF_NONAT: - problems = nat_consistent(r); - break; - case PF_RDR: - case PF_NORDR: - problems = rdr_consistent(r); - break; - case PF_BINAT: - case PF_NOBINAT: - default: - break; - } - return (problems); -} - -int -filter_consistent(struct pf_rule *r, int anchor_call) -{ - int problems = 0; - - if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP && - (r->src.port_op || r->dst.port_op)) { - yyerror("port only applies to tcp/udp"); - problems++; - } - if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 && - (r->type || r->code)) { - yyerror("icmp-type/code only applies to icmp"); - problems++; - } - if (!r->af && (r->type || r->code)) { - yyerror("must indicate address family with icmp-type/code"); - problems++; - } - if (r->overload_tblname[0] && - r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) { - yyerror("'overload' requires 'max-src-conn' " - "or 'max-src-conn-rate'"); - problems++; - } - if ((r->proto == IPPROTO_ICMP && r->af == AF_INET6) || - (r->proto == IPPROTO_ICMPV6 && r->af == AF_INET)) { - yyerror("proto %s doesn't match address family %s", - r->proto == IPPROTO_ICMP ? "icmp" : "icmp6", - r->af == AF_INET ? "inet" : "inet6"); - problems++; - } - if (r->allow_opts && r->action != PF_PASS) { - yyerror("allow-opts can only be specified for pass rules"); - problems++; - } - if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op || - r->dst.port_op || r->flagset || r->type || r->code)) { - yyerror("fragments can be filtered only on IP header fields"); - problems++; - } - if (r->rule_flag & PFRULE_RETURNRST && r->proto != IPPROTO_TCP) { - yyerror("return-rst can only be applied to TCP rules"); - problems++; - } - if (r->max_src_nodes && !(r->rule_flag & PFRULE_RULESRCTRACK)) { - yyerror("max-src-nodes requires 'source-track rule'"); - problems++; - } - if (r->action == PF_DROP && r->keep_state) { - yyerror("keep state on block rules doesn't make sense"); - problems++; - } - if (r->rule_flag & PFRULE_STATESLOPPY && - (r->keep_state == PF_STATE_MODULATE || - r->keep_state == PF_STATE_SYNPROXY)) { - yyerror("sloppy state matching cannot be used with " - "synproxy state or modulate state"); - problems++; - } - return (-problems); -} - -int -nat_consistent(struct pf_rule *r) -{ - return (0); /* yeah! */ -} - -int -rdr_consistent(struct pf_rule *r) -{ - int problems = 0; - - if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) { - if (r->src.port_op) { - yyerror("src port only applies to tcp/udp"); - problems++; - } - if (r->dst.port_op) { - yyerror("dst port only applies to tcp/udp"); - problems++; - } - if (r->rpool.proxy_port[0]) { - yyerror("rpool port only applies to tcp/udp"); - problems++; - } - } - if (r->dst.port_op && - r->dst.port_op != PF_OP_EQ && r->dst.port_op != PF_OP_RRG) { - yyerror("invalid port operator for rdr destination port"); - problems++; - } - return (-problems); -} - -int -process_tabledef(char *name, struct table_opts *opts) -{ - struct pfr_buffer ab; - struct node_tinit *ti; - - bzero(&ab, sizeof(ab)); - ab.pfrb_type = PFRB_ADDRS; - SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) { - if (ti->file) - if (pfr_buf_load(&ab, ti->file, 0, append_addr)) { - if (errno) - yyerror("cannot load \"%s\": %s", - ti->file, strerror(errno)); - else - yyerror("file \"%s\" contains bad data", - ti->file); - goto _error; - } - if (ti->host) - if (append_addr_host(&ab, ti->host, 0, 0)) { - yyerror("cannot create address buffer: %s", - strerror(errno)); - goto _error; - } - } - if (pf->opts & PF_OPT_VERBOSE) - print_tabledef(name, opts->flags, opts->init_addr, - &opts->init_nodes); - if (!(pf->opts & PF_OPT_NOACTION) && - pfctl_define_table(name, opts->flags, opts->init_addr, - pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) { - yyerror("cannot define table %s: %s", name, - pfr_strerror(errno)); - goto _error; - } - pf->tdirty = 1; - pfr_buf_clear(&ab); - return (0); -_error: - pfr_buf_clear(&ab); - return (-1); -} - -struct keywords { - const char *k_name; - int k_val; -}; - -/* macro gore, but you should've seen the prior indentation nightmare... */ - -#define FREE_LIST(T,r) \ - do { \ - T *p, *node = r; \ - while (node != NULL) { \ - p = node; \ - node = node->next; \ - free(p); \ - } \ - } while (0) - -#define LOOP_THROUGH(T,n,r,C) \ - do { \ - T *n; \ - if (r == NULL) { \ - r = calloc(1, sizeof(T)); \ - if (r == NULL) \ - err(1, "LOOP: calloc"); \ - r->next = NULL; \ - } \ - n = r; \ - while (n != NULL) { \ - do { \ - C; \ - } while (0); \ - n = n->next; \ - } \ - } while (0) - -void -expand_label_str(char *label, size_t len, const char *srch, const char *repl) -{ - char *tmp; - char *p, *q; - - if ((tmp = calloc(1, len)) == NULL) - err(1, "expand_label_str: calloc"); - p = q = label; - while ((q = strstr(p, srch)) != NULL) { - *q = '\0'; - if ((strlcat(tmp, p, len) >= len) || - (strlcat(tmp, repl, len) >= len)) - errx(1, "expand_label: label too long"); - q += strlen(srch); - p = q; - } - if (strlcat(tmp, p, len) >= len) - errx(1, "expand_label: label too long"); - strlcpy(label, tmp, len); /* always fits */ - free(tmp); -} - -void -expand_label_if(const char *name, char *label, size_t len, const char *ifname) -{ - if (strstr(label, name) != NULL) { - if (!*ifname) - expand_label_str(label, len, name, "any"); - else - expand_label_str(label, len, name, ifname); - } -} - -void -expand_label_addr(const char *name, char *label, size_t len, sa_family_t af, - struct node_host *h) -{ - char tmp[64], tmp_not[66]; - - if (strstr(label, name) != NULL) { - switch (h->addr.type) { - case PF_ADDR_DYNIFTL: - snprintf(tmp, sizeof(tmp), "(%s)", h->addr.v.ifname); - break; - case PF_ADDR_TABLE: - snprintf(tmp, sizeof(tmp), "<%s>", h->addr.v.tblname); - break; - case PF_ADDR_NOROUTE: - snprintf(tmp, sizeof(tmp), "no-route"); - break; - case PF_ADDR_URPFFAILED: - snprintf(tmp, sizeof(tmp), "urpf-failed"); - break; - case PF_ADDR_ADDRMASK: - if (!af || (PF_AZERO(&h->addr.v.a.addr, af) && - PF_AZERO(&h->addr.v.a.mask, af))) - snprintf(tmp, sizeof(tmp), "any"); - else { - char a[48]; - int bits; - - if (inet_ntop(af, &h->addr.v.a.addr, a, - sizeof(a)) == NULL) - snprintf(tmp, sizeof(tmp), "?"); - else { - bits = unmask(&h->addr.v.a.mask, af); - if ((af == AF_INET && bits < 32) || - (af == AF_INET6 && bits < 128)) - snprintf(tmp, sizeof(tmp), - "%s/%d", a, bits); - else - snprintf(tmp, sizeof(tmp), - "%s", a); - } - } - break; - default: - snprintf(tmp, sizeof(tmp), "?"); - break; - } - - if (h->not) { - snprintf(tmp_not, sizeof(tmp_not), "! %s", tmp); - expand_label_str(label, len, name, tmp_not); - } else - expand_label_str(label, len, name, tmp); - } -} - -void -expand_label_port(const char *name, char *label, size_t len, - struct node_port *port) -{ - char a1[6], a2[6], op[13] = ""; - - if (strstr(label, name) != NULL) { - snprintf(a1, sizeof(a1), "%u", ntohs(port->port[0])); - snprintf(a2, sizeof(a2), "%u", ntohs(port->port[1])); - if (!port->op) - ; - else if (port->op == PF_OP_IRG) - snprintf(op, sizeof(op), "%s><%s", a1, a2); - else if (port->op == PF_OP_XRG) - snprintf(op, sizeof(op), "%s<>%s", a1, a2); - else if (port->op == PF_OP_EQ) - snprintf(op, sizeof(op), "%s", a1); - else if (port->op == PF_OP_NE) - snprintf(op, sizeof(op), "!=%s", a1); - else if (port->op == PF_OP_LT) - snprintf(op, sizeof(op), "<%s", a1); - else if (port->op == PF_OP_LE) - snprintf(op, sizeof(op), "<=%s", a1); - else if (port->op == PF_OP_GT) - snprintf(op, sizeof(op), ">%s", a1); - else if (port->op == PF_OP_GE) - snprintf(op, sizeof(op), ">=%s", a1); - expand_label_str(label, len, name, op); - } -} - -void -expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto) -{ - struct protoent *pe; - char n[4]; - - if (strstr(label, name) != NULL) { - pe = getprotobynumber(proto); - if (pe != NULL) - expand_label_str(label, len, name, pe->p_name); - else { - snprintf(n, sizeof(n), "%u", proto); - expand_label_str(label, len, name, n); - } - } -} - -void -expand_label_nr(const char *name, char *label, size_t len) -{ - char n[11]; - - if (strstr(label, name) != NULL) { - snprintf(n, sizeof(n), "%u", pf->anchor->match); - expand_label_str(label, len, name, n); - } -} - -void -expand_label(char *label, size_t len, const char *ifname, sa_family_t af, - struct node_host *src_host, struct node_port *src_port, - struct node_host *dst_host, struct node_port *dst_port, - u_int8_t proto) -{ - expand_label_if("$if", label, len, ifname); - expand_label_addr("$srcaddr", label, len, af, src_host); - expand_label_addr("$dstaddr", label, len, af, dst_host); - expand_label_port("$srcport", label, len, src_port); - expand_label_port("$dstport", label, len, dst_port); - expand_label_proto("$proto", label, len, proto); - expand_label_nr("$nr", label, len); -} - -int -expand_altq(struct pf_altq *a, struct node_if *interfaces, - struct node_queue *nqueues, struct node_queue_bw bwspec, - struct node_queue_opt *opts) -{ - struct pf_altq pa, pb; - char qname[PF_QNAME_SIZE]; - struct node_queue *n; - struct node_queue_bw bw; - int errs = 0; - - if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_queue, nqueues); - return (0); - } - - LOOP_THROUGH(struct node_if, interface, interfaces, - memcpy(&pa, a, sizeof(struct pf_altq)); - if (strlcpy(pa.ifname, interface->ifname, - sizeof(pa.ifname)) >= sizeof(pa.ifname)) - errx(1, "expand_altq: strlcpy"); - - if (interface->not) { - yyerror("altq on ! is not supported"); - errs++; - } else { - if (eval_pfaltq(pf, &pa, &bwspec, opts)) - errs++; - else - if (pfctl_add_altq(pf, &pa)) - errs++; - - if (pf->opts & PF_OPT_VERBOSE) { - print_altq(&pf->paltq->altq, 0, - &bwspec, opts); - if (nqueues && nqueues->tail) { - printf("queue { "); - LOOP_THROUGH(struct node_queue, queue, - nqueues, - printf("%s ", - queue->queue); - ); - printf("}"); - } - printf("\n"); - } - - if (pa.scheduler == ALTQT_CBQ || - pa.scheduler == ALTQT_HFSC) { - /* now create a root queue */ - memset(&pb, 0, sizeof(struct pf_altq)); - if (strlcpy(qname, "root_", sizeof(qname)) >= - sizeof(qname)) - errx(1, "expand_altq: strlcpy"); - if (strlcat(qname, interface->ifname, - sizeof(qname)) >= sizeof(qname)) - errx(1, "expand_altq: strlcat"); - if (strlcpy(pb.qname, qname, - sizeof(pb.qname)) >= sizeof(pb.qname)) - errx(1, "expand_altq: strlcpy"); - if (strlcpy(pb.ifname, interface->ifname, - sizeof(pb.ifname)) >= sizeof(pb.ifname)) - errx(1, "expand_altq: strlcpy"); - pb.qlimit = pa.qlimit; - pb.scheduler = pa.scheduler; - bw.bw_absolute = pa.ifbandwidth; - bw.bw_percent = 0; - if (eval_pfqueue(pf, &pb, &bw, opts)) - errs++; - else - if (pfctl_add_altq(pf, &pb)) - errs++; - } - - LOOP_THROUGH(struct node_queue, queue, nqueues, - n = calloc(1, sizeof(struct node_queue)); - if (n == NULL) - err(1, "expand_altq: calloc"); - if (pa.scheduler == ALTQT_CBQ || - pa.scheduler == ALTQT_HFSC) - if (strlcpy(n->parent, qname, - sizeof(n->parent)) >= - sizeof(n->parent)) - errx(1, "expand_altq: strlcpy"); - if (strlcpy(n->queue, queue->queue, - sizeof(n->queue)) >= sizeof(n->queue)) - errx(1, "expand_altq: strlcpy"); - if (strlcpy(n->ifname, interface->ifname, - sizeof(n->ifname)) >= sizeof(n->ifname)) - errx(1, "expand_altq: strlcpy"); - n->scheduler = pa.scheduler; - n->next = NULL; - n->tail = n; - if (queues == NULL) - queues = n; - else { - queues->tail->next = n; - queues->tail = n; - } - ); - } - ); - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_queue, nqueues); - - return (errs); -} - -int -expand_queue(struct pf_altq *a, struct node_if *interfaces, - struct node_queue *nqueues, struct node_queue_bw bwspec, - struct node_queue_opt *opts) -{ - struct node_queue *n, *nq; - struct pf_altq pa; - u_int8_t found = 0; - u_int8_t errs = 0; - - if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { - FREE_LIST(struct node_queue, nqueues); - return (0); - } - - if (queues == NULL) { - yyerror("queue %s has no parent", a->qname); - FREE_LIST(struct node_queue, nqueues); - return (1); - } - - LOOP_THROUGH(struct node_if, interface, interfaces, - LOOP_THROUGH(struct node_queue, tqueue, queues, - if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) && - (interface->ifname[0] == 0 || - (!interface->not && !strncmp(interface->ifname, - tqueue->ifname, IFNAMSIZ)) || - (interface->not && strncmp(interface->ifname, - tqueue->ifname, IFNAMSIZ)))) { - /* found ourself in queues */ - found++; - - memcpy(&pa, a, sizeof(struct pf_altq)); - - if (pa.scheduler != ALTQT_NONE && - pa.scheduler != tqueue->scheduler) { - yyerror("exactly one scheduler type " - "per interface allowed"); - return (1); - } - pa.scheduler = tqueue->scheduler; - - /* scheduler dependent error checking */ - switch (pa.scheduler) { - case ALTQT_PRIQ: - if (nqueues != NULL) { - yyerror("priq queues cannot " - "have child queues"); - return (1); - } - if (bwspec.bw_absolute > 0 || - bwspec.bw_percent < 100) { - yyerror("priq doesn't take " - "bandwidth"); - return (1); - } - break; - default: - break; - } - - if (strlcpy(pa.ifname, tqueue->ifname, - sizeof(pa.ifname)) >= sizeof(pa.ifname)) - errx(1, "expand_queue: strlcpy"); - if (strlcpy(pa.parent, tqueue->parent, - sizeof(pa.parent)) >= sizeof(pa.parent)) - errx(1, "expand_queue: strlcpy"); - - if (eval_pfqueue(pf, &pa, &bwspec, opts)) - errs++; - else - if (pfctl_add_altq(pf, &pa)) - errs++; - - for (nq = nqueues; nq != NULL; nq = nq->next) { - if (!strcmp(a->qname, nq->queue)) { - yyerror("queue cannot have " - "itself as child"); - errs++; - continue; - } - n = calloc(1, - sizeof(struct node_queue)); - if (n == NULL) - err(1, "expand_queue: calloc"); - if (strlcpy(n->parent, a->qname, - sizeof(n->parent)) >= - sizeof(n->parent)) - errx(1, "expand_queue strlcpy"); - if (strlcpy(n->queue, nq->queue, - sizeof(n->queue)) >= - sizeof(n->queue)) - errx(1, "expand_queue strlcpy"); - if (strlcpy(n->ifname, tqueue->ifname, - sizeof(n->ifname)) >= - sizeof(n->ifname)) - errx(1, "expand_queue strlcpy"); - n->scheduler = tqueue->scheduler; - n->next = NULL; - n->tail = n; - if (queues == NULL) - queues = n; - else { - queues->tail->next = n; - queues->tail = n; - } - } - if ((pf->opts & PF_OPT_VERBOSE) && ( - (found == 1 && interface->ifname[0] == 0) || - (found > 0 && interface->ifname[0] != 0))) { - print_queue(&pf->paltq->altq, 0, - &bwspec, interface->ifname[0] != 0, - opts); - if (nqueues && nqueues->tail) { - printf("{ "); - LOOP_THROUGH(struct node_queue, - queue, nqueues, - printf("%s ", - queue->queue); - ); - printf("}"); - } - printf("\n"); - } - } - ); - ); - - FREE_LIST(struct node_queue, nqueues); - FREE_LIST(struct node_if, interfaces); - - if (!found) { - yyerror("queue %s has no parent", a->qname); - errs++; - } - - if (errs) - return (1); - else - return (0); -} - -void -expand_rule(struct pf_rule *r, - struct node_if *interfaces, struct node_host *rpool_hosts, - struct node_proto *protos, struct node_os *src_oses, - struct node_host *src_hosts, struct node_port *src_ports, - struct node_host *dst_hosts, struct node_port *dst_ports, - struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types, - const char *anchor_call) -{ - sa_family_t af = r->af; - int added = 0, error = 0; - char ifname[IF_NAMESIZE]; - char label[PF_RULE_LABEL_SIZE]; - char tagname[PF_TAG_NAME_SIZE]; - char match_tagname[PF_TAG_NAME_SIZE]; - struct pf_pooladdr *pa; - struct node_host *h; - u_int8_t flags, flagset, keep_state; - - if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label)) - errx(1, "expand_rule: strlcpy"); - if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname)) - errx(1, "expand_rule: strlcpy"); - if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >= - sizeof(match_tagname)) - errx(1, "expand_rule: strlcpy"); - flags = r->flags; - flagset = r->flagset; - keep_state = r->keep_state; - - LOOP_THROUGH(struct node_if, interface, interfaces, - LOOP_THROUGH(struct node_proto, proto, protos, - LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types, - LOOP_THROUGH(struct node_host, src_host, src_hosts, - LOOP_THROUGH(struct node_port, src_port, src_ports, - LOOP_THROUGH(struct node_os, src_os, src_oses, - LOOP_THROUGH(struct node_host, dst_host, dst_hosts, - LOOP_THROUGH(struct node_port, dst_port, dst_ports, - LOOP_THROUGH(struct node_uid, uid, uids, - LOOP_THROUGH(struct node_gid, gid, gids, - - r->af = af; - /* for link-local IPv6 address, interface must match up */ - if ((r->af && src_host->af && r->af != src_host->af) || - (r->af && dst_host->af && r->af != dst_host->af) || - (src_host->af && dst_host->af && - src_host->af != dst_host->af) || - (src_host->ifindex && dst_host->ifindex && - src_host->ifindex != dst_host->ifindex) || - (src_host->ifindex && *interface->ifname && - src_host->ifindex != if_nametoindex(interface->ifname)) || - (dst_host->ifindex && *interface->ifname && - dst_host->ifindex != if_nametoindex(interface->ifname))) - continue; - if (!r->af && src_host->af) - r->af = src_host->af; - else if (!r->af && dst_host->af) - r->af = dst_host->af; - - if (*interface->ifname) - strlcpy(r->ifname, interface->ifname, - sizeof(r->ifname)); - else if (if_indextoname(src_host->ifindex, ifname)) - strlcpy(r->ifname, ifname, sizeof(r->ifname)); - else if (if_indextoname(dst_host->ifindex, ifname)) - strlcpy(r->ifname, ifname, sizeof(r->ifname)); - else - memset(r->ifname, '\0', sizeof(r->ifname)); - - if (strlcpy(r->label, label, sizeof(r->label)) >= - sizeof(r->label)) - errx(1, "expand_rule: strlcpy"); - if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >= - sizeof(r->tagname)) - errx(1, "expand_rule: strlcpy"); - if (strlcpy(r->match_tagname, match_tagname, - sizeof(r->match_tagname)) >= sizeof(r->match_tagname)) - errx(1, "expand_rule: strlcpy"); - expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af, - src_host, src_port, dst_host, dst_port, proto->proto); - expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af, - src_host, src_port, dst_host, dst_port, proto->proto); - expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname, - r->af, src_host, src_port, dst_host, dst_port, - proto->proto); - - error += check_netmask(src_host, r->af); - error += check_netmask(dst_host, r->af); - - r->ifnot = interface->not; - r->proto = proto->proto; - r->src.addr = src_host->addr; - r->src.neg = src_host->not; - r->src.port[0] = src_port->port[0]; - r->src.port[1] = src_port->port[1]; - r->src.port_op = src_port->op; - r->dst.addr = dst_host->addr; - r->dst.neg = dst_host->not; - r->dst.port[0] = dst_port->port[0]; - r->dst.port[1] = dst_port->port[1]; - r->dst.port_op = dst_port->op; - r->uid.op = uid->op; - r->uid.uid[0] = uid->uid[0]; - r->uid.uid[1] = uid->uid[1]; - r->gid.op = gid->op; - r->gid.gid[0] = gid->gid[0]; - r->gid.gid[1] = gid->gid[1]; - r->type = icmp_type->type; - r->code = icmp_type->code; - - if ((keep_state == PF_STATE_MODULATE || - keep_state == PF_STATE_SYNPROXY) && - r->proto && r->proto != IPPROTO_TCP) - r->keep_state = PF_STATE_NORMAL; - else - r->keep_state = keep_state; - - if (r->proto && r->proto != IPPROTO_TCP) { - r->flags = 0; - r->flagset = 0; - } else { - r->flags = flags; - r->flagset = flagset; - } - if (icmp_type->proto && r->proto != icmp_type->proto) { - yyerror("icmp-type mismatch"); - error++; - } - - if (src_os && src_os->os) { - r->os_fingerprint = pfctl_get_fingerprint(src_os->os); - if ((pf->opts & PF_OPT_VERBOSE2) && - r->os_fingerprint == PF_OSFP_NOMATCH) - fprintf(stderr, - "warning: unknown '%s' OS fingerprint\n", - src_os->os); - } else { - r->os_fingerprint = PF_OSFP_ANY; - } - - TAILQ_INIT(&r->rpool.list); - for (h = rpool_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries); - } - - if (rule_consistent(r, anchor_call[0]) < 0 || error) - yyerror("skipping rule due to errors"); - else { - r->nr = pf->astack[pf->asd]->match++; - pfctl_add_rule(pf, r, anchor_call); - added++; - } - - )))))))))); - - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_proto, protos); - FREE_LIST(struct node_host, src_hosts); - FREE_LIST(struct node_port, src_ports); - FREE_LIST(struct node_os, src_oses); - FREE_LIST(struct node_host, dst_hosts); - FREE_LIST(struct node_port, dst_ports); - FREE_LIST(struct node_uid, uids); - FREE_LIST(struct node_gid, gids); - FREE_LIST(struct node_icmp, icmp_types); - FREE_LIST(struct node_host, rpool_hosts); - - if (!added) - yyerror("rule expands to no valid combination"); -} - -int -expand_skip_interface(struct node_if *interfaces) -{ - int errs = 0; - - if (!interfaces || (!interfaces->next && !interfaces->not && - !strcmp(interfaces->ifname, "none"))) { - if (pf->opts & PF_OPT_VERBOSE) - printf("set skip on none\n"); - errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0); - return (errs); - } - - if (pf->opts & PF_OPT_VERBOSE) - printf("set skip on {"); - LOOP_THROUGH(struct node_if, interface, interfaces, - if (pf->opts & PF_OPT_VERBOSE) - printf(" %s", interface->ifname); - if (interface->not) { - yyerror("skip on ! is not supported"); - errs++; - } else - errs += pfctl_set_interface_flags(pf, - interface->ifname, PFI_IFLAG_SKIP, 1); - ); - if (pf->opts & PF_OPT_VERBOSE) - printf(" }\n"); - - FREE_LIST(struct node_if, interfaces); - - if (errs) - return (1); - else - return (0); -} - -#undef FREE_LIST -#undef LOOP_THROUGH - -int -check_rulestate(int desired_state) -{ - if (require_order && (rulestate > desired_state)) { - yyerror("Rules must be in order: options, normalization, " - "queueing, translation, filtering"); - return (1); - } - rulestate = desired_state; - return (0); -} - -int -kw_cmp(const void *k, const void *e) -{ - return (strcmp(k, ((const struct keywords *)e)->k_name)); -} - -int -lookup(char *s) -{ - /* this has to be sorted always */ - static const struct keywords keywords[] = { - { "all", ALL}, - { "allow-opts", ALLOWOPTS}, - { "altq", ALTQ}, - { "anchor", ANCHOR}, - { "antispoof", ANTISPOOF}, - { "any", ANY}, - { "bandwidth", BANDWIDTH}, - { "binat", BINAT}, - { "binat-anchor", BINATANCHOR}, - { "bitmask", BITMASK}, - { "block", BLOCK}, - { "block-policy", BLOCKPOLICY}, - { "cbq", CBQ}, - { "code", CODE}, - { "crop", FRAGCROP}, - { "debug", DEBUG}, - { "divert-reply", DIVERTREPLY}, - { "divert-to", DIVERTTO}, - { "drop", DROP}, - { "drop-ovl", FRAGDROP}, - { "dup-to", DUPTO}, - { "fastroute", FASTROUTE}, - { "file", FILENAME}, - { "fingerprints", FINGERPRINTS}, - { "flags", FLAGS}, - { "floating", FLOATING}, - { "flush", FLUSH}, - { "for", FOR}, - { "fragment", FRAGMENT}, - { "from", FROM}, - { "global", GLOBAL}, - { "group", GROUP}, - { "hfsc", HFSC}, - { "hostid", HOSTID}, - { "icmp-type", ICMPTYPE}, - { "icmp6-type", ICMP6TYPE}, - { "if-bound", IFBOUND}, - { "in", IN}, - { "include", INCLUDE}, - { "inet", INET}, - { "inet6", INET6}, - { "keep", KEEP}, - { "label", LABEL}, - { "limit", LIMIT}, - { "linkshare", LINKSHARE}, - { "load", LOAD}, - { "log", LOG}, - { "loginterface", LOGINTERFACE}, - { "max", MAXIMUM}, - { "max-mss", MAXMSS}, - { "max-src-conn", MAXSRCCONN}, - { "max-src-conn-rate", MAXSRCCONNRATE}, - { "max-src-nodes", MAXSRCNODES}, - { "max-src-states", MAXSRCSTATES}, - { "min-ttl", MINTTL}, - { "modulate", MODULATE}, - { "nat", NAT}, - { "nat-anchor", NATANCHOR}, - { "no", NO}, - { "no-df", NODF}, - { "no-route", NOROUTE}, - { "no-sync", NOSYNC}, - { "on", ON}, - { "optimization", OPTIMIZATION}, - { "os", OS}, - { "out", OUT}, - { "overload", OVERLOAD}, - { "pass", PASS}, - { "port", PORT}, - { "priority", PRIORITY}, - { "priq", PRIQ}, - { "probability", PROBABILITY}, - { "proto", PROTO}, - { "qlimit", QLIMIT}, - { "queue", QUEUE}, - { "quick", QUICK}, - { "random", RANDOM}, - { "random-id", RANDOMID}, - { "rdr", RDR}, - { "rdr-anchor", RDRANCHOR}, - { "realtime", REALTIME}, - { "reassemble", REASSEMBLE}, - { "reply-to", REPLYTO}, - { "require-order", REQUIREORDER}, - { "return", RETURN}, - { "return-icmp", RETURNICMP}, - { "return-icmp6", RETURNICMP6}, - { "return-rst", RETURNRST}, - { "round-robin", ROUNDROBIN}, - { "route", ROUTE}, - { "route-to", ROUTETO}, - { "rtable", RTABLE}, - { "rule", RULE}, - { "ruleset-optimization", RULESET_OPTIMIZATION}, - { "scrub", SCRUB}, - { "set", SET}, - { "set-tos", SETTOS}, - { "skip", SKIP}, - { "sloppy", SLOPPY}, - { "source-hash", SOURCEHASH}, - { "source-track", SOURCETRACK}, - { "state", STATE}, - { "state-defaults", STATEDEFAULTS}, - { "state-policy", STATEPOLICY}, - { "static-port", STATICPORT}, - { "sticky-address", STICKYADDRESS}, - { "synproxy", SYNPROXY}, - { "table", TABLE}, - { "tag", TAG}, - { "tagged", TAGGED}, - { "tbrsize", TBRSIZE}, - { "timeout", TIMEOUT}, - { "to", TO}, - { "tos", TOS}, - { "ttl", TTL}, - { "upperlimit", UPPERLIMIT}, - { "urpf-failed", URPFFAILED}, - { "user", USER}, - }; - const struct keywords *p; - - p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), - sizeof(keywords[0]), kw_cmp); - - if (p) { - if (debug > 1) - fprintf(stderr, "%s: %d\n", s, p->k_val); - return (p->k_val); - } else { - if (debug > 1) - fprintf(stderr, "string: %s\n", s); - return (STRING); - } -} - -#define MAXPUSHBACK 128 - -char *parsebuf; -int parseindex; -char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; - -int -lgetc(int quotec) -{ - int c, next; - - if (parsebuf) { - /* Read character from the parsebuffer instead of input. */ - if (parseindex >= 0) { - c = parsebuf[parseindex++]; - if (c != '\0') - return (c); - parsebuf = NULL; - } else - parseindex++; - } - - if (pushback_index) - return (pushback_buffer[--pushback_index]); - - if (quotec) { - if ((c = getc(file->stream)) == EOF) { - yyerror("reached end of file while parsing quoted string"); - if (popfile() == EOF) - return (EOF); - return (quotec); - } - return (c); - } - - while ((c = getc(file->stream)) == '\\') { - next = getc(file->stream); - if (next != '\n') { - c = next; - break; - } - yylval.lineno = file->lineno; - file->lineno++; - } - - while (c == EOF) { - if (popfile() == EOF) - return (EOF); - c = getc(file->stream); - } - return (c); -} - -int -lungetc(int c) -{ - if (c == EOF) - return (EOF); - if (parsebuf) { - parseindex--; - if (parseindex >= 0) - return (c); - } - if (pushback_index < MAXPUSHBACK-1) - return (pushback_buffer[pushback_index++] = c); - else - return (EOF); -} - -int -findeol(void) -{ - int c; - - parsebuf = NULL; - - /* skip to either EOF or the first real EOL */ - while (1) { - if (pushback_index) - c = pushback_buffer[--pushback_index]; - else - c = lgetc(0); - if (c == '\n') { - file->lineno++; - break; - } - if (c == EOF) - break; - } - return (ERROR); -} - -int -yylex(void) -{ - char buf[8096]; - char *p, *val; - int quotec, next, c; - int token; - -top: - p = buf; - while ((c = lgetc(0)) == ' ' || c == '\t') - ; /* nothing */ - - yylval.lineno = file->lineno; - if (c == '#') - while ((c = lgetc(0)) != '\n' && c != EOF) - ; /* nothing */ - if (c == '$' && parsebuf == NULL) { - while (1) { - if ((c = lgetc(0)) == EOF) - return (0); - - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - if (isalnum(c) || c == '_') { - *p++ = (char)c; - continue; - } - *p = '\0'; - lungetc(c); - break; - } - val = symget(buf); - if (val == NULL) { - yyerror("macro '%s' not defined", buf); - return (findeol()); - } - parsebuf = val; - parseindex = 0; - goto top; - } - - switch (c) { - case '\'': - case '"': - quotec = c; - while (1) { - if ((c = lgetc(quotec)) == EOF) - return (0); - if (c == '\n') { - file->lineno++; - continue; - } else if (c == '\\') { - if ((next = lgetc(quotec)) == EOF) - return (0); - if (next == quotec || c == ' ' || c == '\t') - c = next; - else if (next == '\n') - continue; - else - lungetc(next); - } else if (c == quotec) { - *p = '\0'; - break; - } - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - *p++ = (char)c; - } - yylval.v.string = strdup(buf); - if (yylval.v.string == NULL) - err(1, "yylex: strdup"); - return (STRING); - case '<': - next = lgetc(0); - if (next == '>') { - yylval.v.i = PF_OP_XRG; - return (PORTBINARY); - } - lungetc(next); - break; - case '>': - next = lgetc(0); - if (next == '<') { - yylval.v.i = PF_OP_IRG; - return (PORTBINARY); - } - lungetc(next); - break; - case '-': - next = lgetc(0); - if (next == '>') - return (ARROW); - lungetc(next); - break; - } - -#define allowed_to_end_number(x) \ - (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') - - if (c == '-' || isdigit(c)) { - do { - *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && isdigit(c)); - lungetc(c); - if (p == buf + 1 && buf[0] == '-') - goto nodigits; - if (c == EOF || allowed_to_end_number(c)) { - const char *errstr = NULL; - - *p = '\0'; - yylval.v.number = strtonum(buf, LLONG_MIN, - LLONG_MAX, &errstr); - if (errstr) { - yyerror("\"%s\" invalid number: %s", - buf, errstr); - return (findeol()); - } - return (NUMBER); - } else { -nodigits: - while (p > buf + 1) - lungetc(*--p); - c = *--p; - if (c == '-') - return (c); - } - } - -#define allowed_in_string(x) \ - (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ - x != '{' && x != '}' && x != '<' && x != '>' && \ - x != '!' && x != '=' && x != '/' && x != '#' && \ - x != ',')) - - if (isalnum(c) || c == ':' || c == '_') { - do { - *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); - lungetc(c); - *p = '\0'; - if ((token = lookup(buf)) == STRING) - if ((yylval.v.string = strdup(buf)) == NULL) - err(1, "yylex: strdup"); - return (token); - } - if (c == '\n') { - yylval.lineno = file->lineno; - file->lineno++; - } - if (c == EOF) - return (0); - return (c); -} - -int -check_file_secrecy(int fd, const char *fname) -{ - struct stat st; - - if (fstat(fd, &st)) { - warn("cannot stat %s", fname); - return (-1); - } - if (st.st_uid != 0 && st.st_uid != getuid()) { - warnx("%s: owner not root or current user", fname); - return (-1); - } - if (st.st_mode & (S_IRWXG | S_IRWXO)) { - warnx("%s: group/world readable/writeable", fname); - return (-1); - } - return (0); -} - -struct file * -pushfile(const char *name, int secret) -{ - struct file *nfile; - - if ((nfile = calloc(1, sizeof(struct file))) == NULL || - (nfile->name = strdup(name)) == NULL) { - warn("malloc"); - return (NULL); - } - if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) { - nfile->stream = stdin; - free(nfile->name); - if ((nfile->name = strdup("stdin")) == NULL) { - warn("strdup"); - free(nfile); - return (NULL); - } - } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { - warn("%s", nfile->name); - free(nfile->name); - free(nfile); - return (NULL); - } else if (secret && - check_file_secrecy(fileno(nfile->stream), nfile->name)) { - fclose(nfile->stream); - free(nfile->name); - free(nfile); - return (NULL); - } - nfile->lineno = 1; - TAILQ_INSERT_TAIL(&files, nfile, entry); - return (nfile); -} - -int -popfile(void) -{ - struct file *prev; - - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { - prev->errors += file->errors; - TAILQ_REMOVE(&files, file, entry); - fclose(file->stream); - free(file->name); - free(file); - file = prev; - return (0); - } - return (EOF); -} - -int -parse_config(char *filename, struct pfctl *xpf) -{ - int errors = 0; - struct sym *sym; - - pf = xpf; - errors = 0; - rulestate = PFCTL_STATE_NONE; - returnicmpdefault = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT; - returnicmp6default = - (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT; - blockpolicy = PFRULE_DROP; - require_order = 1; - - if ((file = pushfile(filename, 0)) == NULL) { - warn("cannot open the main config file!"); - return (-1); - } - - yyparse(); - errors = file->errors; - popfile(); - - /* Free macros and check which have not been used. */ - while ((sym = TAILQ_FIRST(&symhead))) { - if ((pf->opts & PF_OPT_VERBOSE2) && !sym->used) - fprintf(stderr, "warning: macro '%s' not " - "used\n", sym->nam); - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entry); - free(sym); - } - - return (errors ? -1 : 0); -} - -int -symset(const char *nam, const char *val, int persist) -{ - struct sym *sym; - - for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); - sym = TAILQ_NEXT(sym, entry)) - ; /* nothing */ - - if (sym != NULL) { - if (sym->persist == 1) - return (0); - else { - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entry); - free(sym); - } - } - if ((sym = calloc(1, sizeof(*sym))) == NULL) - return (-1); - - sym->nam = strdup(nam); - if (sym->nam == NULL) { - free(sym); - return (-1); - } - sym->val = strdup(val); - if (sym->val == NULL) { - free(sym->nam); - free(sym); - return (-1); - } - sym->used = 0; - sym->persist = persist; - TAILQ_INSERT_TAIL(&symhead, sym, entry); - return (0); -} - -int -pfctl_cmdline_symset(char *s) -{ - char *sym, *val; - int ret; - - if ((val = strrchr(s, '=')) == NULL) - return (-1); - - if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL) - err(1, "pfctl_cmdline_symset: malloc"); - - strlcpy(sym, s, strlen(s) - strlen(val) + 1); - - ret = symset(sym, val + 1, 1); - free(sym); - - return (ret); -} - -char * -symget(const char *nam) -{ - struct sym *sym; - - TAILQ_FOREACH(sym, &symhead, entry) - if (strcmp(nam, sym->nam) == 0) { - sym->used = 1; - return (sym->val); - } - return (NULL); -} - -void -mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst) -{ - int i; - struct pf_rule *r; - - for (i = 0; i < PF_RULESET_MAX; ++i) { - while ((r = TAILQ_FIRST(src->rules[i].active.ptr)) - != NULL) { - TAILQ_REMOVE(src->rules[i].active.ptr, r, entries); - TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries); - dst->anchor->match++; - } - src->anchor->match = 0; - while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr)) - != NULL) { - TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries); - TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr, - r, entries); - } - } -} - -void -decide_address_family(struct node_host *n, sa_family_t *af) -{ - if (*af != 0 || n == NULL) - return; - *af = n->af; - while ((n = n->next) != NULL) { - if (n->af != *af) { - *af = 0; - return; - } - } -} - -void -remove_invalid_hosts(struct node_host **nh, sa_family_t *af) -{ - struct node_host *n = *nh, *prev = NULL; - - while (n != NULL) { - if (*af && n->af && n->af != *af) { - /* unlink and free n */ - struct node_host *next = n->next; - - /* adjust tail pointer */ - if (n == (*nh)->tail) - (*nh)->tail = prev; - /* adjust previous node's next pointer */ - if (prev == NULL) - *nh = next; - else - prev->next = next; - /* free node */ - if (n->ifname != NULL) - free(n->ifname); - free(n); - n = next; - } else { - if (n->af && !*af) - *af = n->af; - prev = n; - n = n->next; - } - } -} - -int -invalid_redirect(struct node_host *nh, sa_family_t af) -{ - if (!af) { - struct node_host *n; - - /* tables and dyniftl are ok without an address family */ - for (n = nh; n != NULL; n = n->next) { - if (n->addr.type != PF_ADDR_TABLE && - n->addr.type != PF_ADDR_DYNIFTL) { - yyerror("address family not given and " - "translation address expands to multiple " - "address families"); - return (1); - } - } - } - if (nh == NULL) { - yyerror("no translation address with matching address family " - "found."); - return (1); - } - return (0); -} - -int -atoul(char *s, u_long *ulvalp) -{ - u_long ulval; - char *ep; - - errno = 0; - ulval = strtoul(s, &ep, 0); - if (s[0] == '\0' || *ep != '\0') - return (-1); - if (errno == ERANGE && ulval == ULONG_MAX) - return (-1); - *ulvalp = ulval; - return (0); -} - -int -getservice(char *n) -{ - struct servent *s; - u_long ulval; - - if (atoul(n, &ulval) == 0) { - if (ulval > 65535) { - yyerror("illegal port value %lu", ulval); - return (-1); - } - return (htons(ulval)); - } else { - s = getservbyname(n, "tcp"); - if (s == NULL) - s = getservbyname(n, "udp"); - if (s == NULL) { - yyerror("unknown port %s", n); - return (-1); - } - return (s->s_port); - } -} - -int -rule_label(struct pf_rule *r, char *s) -{ - if (s) { - if (strlcpy(r->label, s, sizeof(r->label)) >= - sizeof(r->label)) { - yyerror("rule label too long (max %d chars)", - sizeof(r->label)-1); - return (-1); - } - } - return (0); -} - -u_int16_t -parseicmpspec(char *w, sa_family_t af) -{ - const struct icmpcodeent *p; - u_long ulval; - u_int8_t icmptype; - - if (af == AF_INET) - icmptype = returnicmpdefault >> 8; - else - icmptype = returnicmp6default >> 8; - - if (atoul(w, &ulval) == -1) { - if ((p = geticmpcodebyname(icmptype, w, af)) == NULL) { - yyerror("unknown icmp code %s", w); - return (0); - } - ulval = p->code; - } - if (ulval > 255) { - yyerror("invalid icmp code %lu", ulval); - return (0); - } - return (icmptype << 8 | ulval); -} - -int -parseport(char *port, struct range *r, int extensions) -{ - char *p = strchr(port, ':'); - - if (p == NULL) { - if ((r->a = getservice(port)) == -1) - return (-1); - r->b = 0; - r->t = PF_OP_NONE; - return (0); - } - if ((extensions & PPORT_STAR) && !strcmp(p+1, "*")) { - *p = 0; - if ((r->a = getservice(port)) == -1) - return (-1); - r->b = 0; - r->t = PF_OP_IRG; - return (0); - } - if ((extensions & PPORT_RANGE)) { - *p++ = 0; - if ((r->a = getservice(port)) == -1 || - (r->b = getservice(p)) == -1) - return (-1); - if (r->a == r->b) { - r->b = 0; - r->t = PF_OP_NONE; - } else - r->t = PF_OP_RRG; - return (0); - } - return (-1); -} - -int -pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans) -{ - struct loadanchors *la; - - TAILQ_FOREACH(la, &loadanchorshead, entries) { - if (pf->opts & PF_OPT_VERBOSE) - fprintf(stderr, "\nLoading anchor %s from %s\n", - la->anchorname, la->filename); - if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize, - la->anchorname, trans) == -1) - return (-1); - } - - return (0); -} - -int -rt_tableid_max(void) -{ -#ifdef __FreeBSD__ - int fibs; - size_t l = sizeof(fibs); - - if (sysctlbyname("net.fibs", &fibs, &l, NULL, 0) == -1) - fibs = 16; /* XXX RT_MAXFIBS, at least limit it some. */ - /* - * As the OpenBSD code only compares > and not >= we need to adjust - * here given we only accept values of 0..n and want to avoid #ifdefs - * in the grammer. - */ - return (fibs - 1); -#else - return (RT_TABLEID_MAX); -#endif -} diff --git a/contrib/pf/pfctl/pf_print_state.c b/contrib/pf/pfctl/pf_print_state.c deleted file mode 100644 index d6637b445c350..0000000000000 --- a/contrib/pf/pfctl/pf_print_state.c +++ /dev/null @@ -1,375 +0,0 @@ -/* $OpenBSD: pf_print_state.c,v 1.52 2008/08/12 16:40:18 david Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#ifdef __FreeBSD__ -#include -#define betoh64 be64toh -#endif -#include -#define TCPSTATES -#include -#include -#include -#include - -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -void print_name(struct pf_addr *, sa_family_t); - -void -print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) -{ - switch (addr->type) { - case PF_ADDR_DYNIFTL: - printf("(%s", addr->v.ifname); - if (addr->iflags & PFI_AFLAG_NETWORK) - printf(":network"); - if (addr->iflags & PFI_AFLAG_BROADCAST) - printf(":broadcast"); - if (addr->iflags & PFI_AFLAG_PEER) - printf(":peer"); - if (addr->iflags & PFI_AFLAG_NOALIAS) - printf(":0"); - if (verbose) { - if (addr->p.dyncnt <= 0) - printf(":*"); - else - printf(":%d", addr->p.dyncnt); - } - printf(")"); - break; - case PF_ADDR_TABLE: - if (verbose) - if (addr->p.tblcnt == -1) - printf("<%s:*>", addr->v.tblname); - else - printf("<%s:%d>", addr->v.tblname, - addr->p.tblcnt); - else - printf("<%s>", addr->v.tblname); - return; - case PF_ADDR_RANGE: { - char buf[48]; - - if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL) - printf(" - ?"); - else - printf(" - %s", buf); - break; - } - case PF_ADDR_ADDRMASK: - if (PF_AZERO(&addr->v.a.addr, AF_INET6) && - PF_AZERO(&addr->v.a.mask, AF_INET6)) - printf("any"); - else { - char buf[48]; - - if (inet_ntop(af, &addr->v.a.addr, buf, - sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - } - break; - case PF_ADDR_NOROUTE: - printf("no-route"); - return; - case PF_ADDR_URPFFAILED: - printf("urpf-failed"); - return; - default: - printf("?"); - return; - } - - /* mask if not _both_ address and mask are zero */ - if (addr->type != PF_ADDR_RANGE && - !(PF_AZERO(&addr->v.a.addr, AF_INET6) && - PF_AZERO(&addr->v.a.mask, AF_INET6))) { - int bits = unmask(&addr->v.a.mask, af); - - if (bits != (af == AF_INET ? 32 : 128)) - printf("/%d", bits); - } -} - -void -print_name(struct pf_addr *addr, sa_family_t af) -{ - char host[NI_MAXHOST]; - - strlcpy(host, "?", sizeof(host)); - switch (af) { - case AF_INET: { - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr = addr->v4; - getnameinfo((struct sockaddr *)&sin, sin.sin_len, - host, sizeof(host), NULL, 0, NI_NOFQDN); - break; - } - case AF_INET6: { - struct sockaddr_in6 sin6; - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = addr->v6; - getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, - host, sizeof(host), NULL, 0, NI_NOFQDN); - break; - } - } - printf("%s", host); -} - -void -print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts) -{ - if (opts & PF_OPT_USEDNS) - print_name(addr, af); - else { - struct pf_addr_wrap aw; - - memset(&aw, 0, sizeof(aw)); - aw.v.a.addr = *addr; - if (af == AF_INET) - aw.v.a.mask.addr32[0] = 0xffffffff; - else { - memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); - af = AF_INET6; - } - print_addr(&aw, af, opts & PF_OPT_VERBOSE2); - } - - if (port) { - if (af == AF_INET) - printf(":%u", ntohs(port)); - else - printf("[%u]", ntohs(port)); - } -} - -void -print_seq(struct pfsync_state_peer *p) -{ - if (p->seqdiff) - printf("[%u + %u](+%u)", ntohl(p->seqlo), - ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff)); - else - printf("[%u + %u]", ntohl(p->seqlo), - ntohl(p->seqhi) - ntohl(p->seqlo)); -} - -void -print_state(struct pfsync_state *s, int opts) -{ - struct pfsync_state_peer *src, *dst; - struct pfsync_state_key *sk, *nk; - struct protoent *p; - int min, sec; - - if (s->direction == PF_OUT) { - src = &s->src; - dst = &s->dst; - sk = &s->key[PF_SK_STACK]; - nk = &s->key[PF_SK_WIRE]; - if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) - sk->port[0] = nk->port[0]; - } else { - src = &s->dst; - dst = &s->src; - sk = &s->key[PF_SK_WIRE]; - nk = &s->key[PF_SK_STACK]; - if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) - sk->port[1] = nk->port[1]; - } - printf("%s ", s->ifname); - if ((p = getprotobynumber(s->proto)) != NULL) - printf("%s ", p->p_name); - else - printf("%u ", s->proto); - - print_host(&nk->addr[1], nk->port[1], s->af, opts); - if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) || - nk->port[1] != sk->port[1]) { - printf(" ("); - print_host(&sk->addr[1], sk->port[1], s->af, opts); - printf(")"); - } - if (s->direction == PF_OUT) - printf(" -> "); - else - printf(" <- "); - print_host(&nk->addr[0], nk->port[0], s->af, opts); - if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) || - nk->port[0] != sk->port[0]) { - printf(" ("); - print_host(&sk->addr[0], sk->port[0], s->af, opts); - printf(")"); - } - - printf(" "); - if (s->proto == IPPROTO_TCP) { - if (src->state <= TCPS_TIME_WAIT && - dst->state <= TCPS_TIME_WAIT) - printf(" %s:%s\n", tcpstates[src->state], - tcpstates[dst->state]); - else if (src->state == PF_TCPS_PROXY_SRC || - dst->state == PF_TCPS_PROXY_SRC) - printf(" PROXY:SRC\n"); - else if (src->state == PF_TCPS_PROXY_DST || - dst->state == PF_TCPS_PROXY_DST) - printf(" PROXY:DST\n"); - else - printf(" \n", - src->state, dst->state); - if (opts & PF_OPT_VERBOSE) { - printf(" "); - print_seq(src); - if (src->wscale && dst->wscale) - printf(" wscale %u", - src->wscale & PF_WSCALE_MASK); - printf(" "); - print_seq(dst); - if (src->wscale && dst->wscale) - printf(" wscale %u", - dst->wscale & PF_WSCALE_MASK); - printf("\n"); - } - } else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES && - dst->state < PFUDPS_NSTATES) { - const char *states[] = PFUDPS_NAMES; - - printf(" %s:%s\n", states[src->state], states[dst->state]); - } else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES && - dst->state < PFOTHERS_NSTATES) { - /* XXX ICMP doesn't really have state levels */ - const char *states[] = PFOTHERS_NAMES; - - printf(" %s:%s\n", states[src->state], states[dst->state]); - } else { - printf(" %u:%u\n", src->state, dst->state); - } - - if (opts & PF_OPT_VERBOSE) { - u_int64_t packets[2]; - u_int64_t bytes[2]; - u_int32_t creation = ntohl(s->creation); - u_int32_t expire = ntohl(s->expire); - - sec = creation % 60; - creation /= 60; - min = creation % 60; - creation /= 60; - printf(" age %.2u:%.2u:%.2u", creation, min, sec); - sec = expire % 60; - expire /= 60; - min = expire % 60; - expire /= 60; - printf(", expires in %.2u:%.2u:%.2u", expire, min, sec); - - bcopy(s->packets[0], &packets[0], sizeof(u_int64_t)); - bcopy(s->packets[1], &packets[1], sizeof(u_int64_t)); - bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t)); - bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t)); - printf(", %llu:%llu pkts, %llu:%llu bytes", -#ifdef __FreeBSD__ - (unsigned long long)betoh64(packets[0]), - (unsigned long long)betoh64(packets[1]), - (unsigned long long)betoh64(bytes[0]), - (unsigned long long)betoh64(bytes[1])); -#else - betoh64(packets[0]), - betoh64(packets[1]), - betoh64(bytes[0]), - betoh64(bytes[1])); -#endif - if (ntohl(s->anchor) != -1) - printf(", anchor %u", ntohl(s->anchor)); - if (ntohl(s->rule) != -1) - printf(", rule %u", ntohl(s->rule)); - if (s->state_flags & PFSTATE_SLOPPY) - printf(", sloppy"); - if (s->sync_flags & PFSYNC_FLAG_SRCNODE) - printf(", source-track"); - if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE) - printf(", sticky-address"); - printf("\n"); - } - if (opts & PF_OPT_VERBOSE2) { - u_int64_t id; - - bcopy(&s->id, &id, sizeof(u_int64_t)); - printf(" id: %016llx creatorid: %08x", -#ifdef __FreeBSD__ - (unsigned long long)betoh64(id), ntohl(s->creatorid)); -#else - betoh64(id), ntohl(s->creatorid)); -#endif - printf("\n"); - } -} - -int -unmask(struct pf_addr *m, sa_family_t af) -{ - int i = 31, j = 0, b = 0; - u_int32_t tmp; - - while (j < 4 && m->addr32[j] == 0xffffffff) { - b += 32; - j++; - } - if (j < 4) { - tmp = ntohl(m->addr32[j]); - for (i = 31; tmp & (1 << i); --i) - b++; - } - return (b); -} diff --git a/contrib/pf/pfctl/pfctl.8 b/contrib/pf/pfctl/pfctl.8 deleted file mode 100644 index b178a07e8434f..0000000000000 --- a/contrib/pf/pfctl/pfctl.8 +++ /dev/null @@ -1,687 +0,0 @@ -.\" $OpenBSD: pfctl.8,v 1.138 2008/06/10 20:55:02 mcbride Exp $ -.\" -.\" Copyright (c) 2001 Kjell Wooding. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. The name of the author may not be used to endorse or promote products -.\" derived from this software without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd June 21, 2011 -.Dt PFCTL 8 -.Os -.Sh NAME -.Nm pfctl -.Nd control the packet filter (PF) device -.Sh SYNOPSIS -.Nm pfctl -.Bk -words -.Op Fl AdeghmNnOPqRrvz -.Op Fl a Ar anchor -.Oo Fl D Ar macro Ns = -.Ar value Oc -.Op Fl F Ar modifier -.Op Fl f Ar file -.Op Fl i Ar interface -.Op Fl K Ar host | network -.Xo -.Oo Fl k -.Ar host | network | label | id -.Oc Xc -.Op Fl o Ar level -.Op Fl p Ar device -.Op Fl s Ar modifier -.Xo -.Oo Fl t Ar table -.Fl T Ar command -.Op Ar address ... Oc -.Xc -.Op Fl x Ar level -.Ek -.Sh DESCRIPTION -The -.Nm -utility communicates with the packet filter device using the -ioctl interface described in -.Xr pf 4 . -It allows ruleset and parameter configuration and retrieval of status -information from the packet filter. -.Pp -Packet filtering restricts the types of packets that pass through -network interfaces entering or leaving the host based on filter -rules as described in -.Xr pf.conf 5 . -The packet filter can also replace addresses and ports of packets. -Replacing source addresses and ports of outgoing packets is called -NAT (Network Address Translation) and is used to connect an internal -network (usually reserved address space) to an external one (the -Internet) by making all connections to external hosts appear to -come from the gateway. -Replacing destination addresses and ports of incoming packets -is used to redirect connections to different hosts and/or ports. -A combination of both translations, bidirectional NAT, is also -supported. -Translation rules are described in -.Xr pf.conf 5 . -.Pp -When the variable -.Va pf -is set to -.Dv YES -in -.Xr rc.conf 5 , -the rule file specified with the variable -.Va pf_rules -is loaded automatically by the -.Xr rc 8 -scripts and the packet filter is enabled. -.Pp -The packet filter does not itself forward packets between interfaces. -Forwarding can be enabled by setting the -.Xr sysctl 8 -variables -.Em net.inet.ip.forwarding -and/or -.Em net.inet6.ip6.forwarding -to 1. -Set them permanently in -.Xr sysctl.conf 5 . -.Pp -The -.Nm -utility provides several commands. -The options are as follows: -.Bl -tag -width Ds -.It Fl A -Load only the queue rules present in the rule file. -Other rules and options are ignored. -.It Fl a Ar anchor -Apply flags -.Fl f , -.Fl F , -and -.Fl s -only to the rules in the specified -.Ar anchor . -In addition to the main ruleset, -.Nm -can load and manipulate additional rulesets by name, -called anchors. -The main ruleset is the default anchor. -.Pp -Anchors are referenced by name and may be nested, -with the various components of the anchor path separated by -.Sq / -characters, similar to how file system hierarchies are laid out. -The last component of the anchor path is where ruleset operations are -performed. -.Pp -Evaluation of -.Ar anchor -rules from the main ruleset is described in -.Xr pf.conf 5 . -.Pp -For example, the following will show all filter rules (see the -.Fl s -flag below) inside the anchor -.Dq authpf/smith(1234) , -which would have been created for user -.Dq smith -by -.Xr authpf 8 , -PID 1234: -.Bd -literal -offset indent -# pfctl -a "authpf/smith(1234)" -s rules -.Ed -.Pp -Private tables can also be put inside anchors, either by having table -statements in the -.Xr pf.conf 5 -file that is loaded in the anchor, or by using regular table commands, as in: -.Bd -literal -offset indent -# pfctl -a foo/bar -t mytable -T add 1.2.3.4 5.6.7.8 -.Ed -.Pp -When a rule referring to a table is loaded in an anchor, the rule will use the -private table if one is defined, and then fall back to the table defined in the -main ruleset, if there is one. -This is similar to C rules for variable scope. -It is possible to create distinct tables with the same name in the global -ruleset and in an anchor, but this is often bad design and a warning will be -issued in that case. -.Pp -By default, recursive inline printing of anchors applies only to unnamed -anchors specified inline in the ruleset. -If the anchor name is terminated with a -.Sq * -character, the -.Fl s -flag will recursively print all anchors in a brace delimited block. -For example the following will print the -.Dq authpf -ruleset recursively: -.Bd -literal -offset indent -# pfctl -a 'authpf/*' -sr -.Ed -.Pp -To print the main ruleset recursively, specify only -.Sq * -as the anchor name: -.Bd -literal -offset indent -# pfctl -a '*' -sr -.Ed -.It Fl D Ar macro Ns = Ns Ar value -Define -.Ar macro -to be set to -.Ar value -on the command line. -Overrides the definition of -.Ar macro -in the ruleset. -.It Fl d -Disable the packet filter. -.It Fl e -Enable the packet filter. -.It Fl F Ar modifier -Flush the filter parameters specified by -.Ar modifier -(may be abbreviated): -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl F Cm nat -Flush the NAT rules. -.It Fl F Cm queue -Flush the queue rules. -.It Fl F Cm rules -Flush the filter rules. -.It Fl F Cm states -Flush the state table (NAT and filter). -.It Fl F Cm Sources -Flush the source tracking table. -.It Fl F Cm info -Flush the filter information (statistics that are not bound to rules). -.It Fl F Cm Tables -Flush the tables. -.It Fl F Cm osfp -Flush the passive operating system fingerprints. -.It Fl F Cm all -Flush all of the above. -.El -.It Fl f Ar file -Load the rules contained in -.Ar file . -This -.Ar file -may contain macros, tables, options, and normalization, queueing, -translation, and filtering rules. -With the exception of macros and tables, the statements must appear in that -order. -.It Fl g -Include output helpful for debugging. -.It Fl h -Help. -.It Fl i Ar interface -Restrict the operation to the given -.Ar interface . -.It Fl K Ar host | network -Kill all of the source tracking entries originating from the specified -.Ar host -or -.Ar network . -A second -.Fl K Ar host -or -.Fl K Ar network -option may be specified, which will kill all the source tracking -entries from the first host/network to the second. -.It Xo -.Fl k -.Ar host | network | label | id -.Xc -Kill all of the state entries matching the specified -.Ar host , -.Ar network , -.Ar label , -or -.Ar id . -.Pp -For example, to kill all of the state entries originating from -.Dq host : -.Pp -.Dl # pfctl -k host -.Pp -A second -.Fl k Ar host -or -.Fl k Ar network -option may be specified, which will kill all the state entries -from the first host/network to the second. -To kill all of the state entries from -.Dq host1 -to -.Dq host2 : -.Pp -.Dl # pfctl -k host1 -k host2 -.Pp -To kill all states originating from 192.168.1.0/24 to 172.16.0.0/16: -.Pp -.Dl # pfctl -k 192.168.1.0/24 -k 172.16.0.0/16 -.Pp -A network prefix length of 0 can be used as a wildcard. -To kill all states with the target -.Dq host2 : -.Pp -.Dl # pfctl -k 0.0.0.0/0 -k host2 -.Pp -It is also possible to kill states by rule label or state ID. -In this mode the first -.Fl k -argument is used to specify the type -of the second argument. -The following command would kill all states that have been created -from rules carrying the label -.Dq foobar : -.Pp -.Dl # pfctl -k label -k foobar -.Pp -To kill one specific state by its unique state ID -(as shown by pfctl -s state -vv), -use the -.Ar id -modifier and as a second argument the state ID and optional creator ID. -To kill a state with ID 4823e84500000003 use: -.Pp -.Dl # pfctl -k id -k 4823e84500000003 -.Pp -To kill a state with ID 4823e84500000018 created from a backup -firewall with hostid 00000002 use: -.Pp -.Dl # pfctl -k id -k 4823e84500000018/2 -.Pp -.It Fl m -Merge in explicitly given options without resetting those -which are omitted. -Allows single options to be modified without disturbing the others: -.Bd -literal -offset indent -# echo "set loginterface fxp0" | pfctl -mf - -.Ed -.It Fl N -Load only the NAT rules present in the rule file. -Other rules and options are ignored. -.It Fl n -Do not actually load rules, just parse them. -.It Fl O -Load only the options present in the rule file. -Other rules and options are ignored. -.It Fl o Ar level -Control the ruleset optimizer, overriding any rule file settings. -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl o Cm none -Disable the ruleset optimizer. -.It Fl o Cm basic -Enable basic ruleset optimizations. -This is the default behaviour. -.It Fl o Cm profile -Enable basic ruleset optimizations with profiling. -.El -For further information on the ruleset optimizer, see -.Xr pf.conf 5 . -.It Fl P -Do not perform service name lookup for port specific rules, -instead display the ports numerically. -.It Fl p Ar device -Use the device file -.Ar device -instead of the default -.Pa /dev/pf . -.It Fl q -Only print errors and warnings. -.It Fl R -Load only the filter rules present in the rule file. -Other rules and options are ignored. -.It Fl r -Perform reverse DNS lookups on states when displaying them. -.It Fl s Ar modifier -Show the filter parameters specified by -.Ar modifier -(may be abbreviated): -.Pp -.Bl -tag -width xxxxxxxxxxxxx -compact -.It Fl s Cm nat -Show the currently loaded NAT rules. -.It Fl s Cm queue -Show the currently loaded queue rules. -When used together with -.Fl v , -per-queue statistics are also shown. -When used together with -.Fl v v , -.Nm -will loop and show updated queue statistics every five seconds, including -measured bandwidth and packets per second. -.It Fl s Cm rules -Show the currently loaded filter rules. -When used together with -.Fl v , -the per-rule statistics (number of evaluations, -packets and bytes) are also shown. -Note that the -.Dq skip step -optimization done automatically by the kernel -will skip evaluation of rules where possible. -Packets passed statefully are counted in the rule that created the state -(even though the rule isn't evaluated more than once for the entire -connection). -.It Fl s Cm Anchors -Show the currently loaded anchors directly attached to the main ruleset. -If -.Fl a Ar anchor -is specified as well, the anchors loaded directly below the given -.Ar anchor -are shown instead. -If -.Fl v -is specified, all anchors attached under the target anchor will be -displayed recursively. -.It Fl s Cm states -Show the contents of the state table. -.It Fl s Cm Sources -Show the contents of the source tracking table. -.It Fl s Cm info -Show filter information (statistics and counters). -When used together with -.Fl v , -source tracking statistics are also shown. -.It Fl s Cm labels -Show per-rule statistics (label, evaluations, packets total, bytes total, -packets in, bytes in, packets out, bytes out, state creations) of -filter rules with labels, useful for accounting. -.It Fl s Cm timeouts -Show the current global timeouts. -.It Fl s Cm memory -Show the current pool memory hard limits. -.It Fl s Cm Tables -Show the list of tables. -.It Fl s Cm osfp -Show the list of operating system fingerprints. -.It Fl s Cm Interfaces -Show the list of interfaces and interface drivers available to PF. -When used together with -.Fl v , -it additionally lists which interfaces have skip rules activated. -When used together with -.Fl vv , -interface statistics are also shown. -.Fl i -can be used to select an interface or a group of interfaces. -.It Fl s Cm all -Show all of the above, except for the lists of interfaces and operating -system fingerprints. -.El -.It Fl T Ar command Op Ar address ... -Specify the -.Ar command -(may be abbreviated) to apply to the table. -Commands include: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl T Cm kill -Kill a table. -.It Fl T Cm flush -Flush all addresses of a table. -.It Fl T Cm add -Add one or more addresses in a table. -Automatically create a nonexisting table. -.It Fl T Cm delete -Delete one or more addresses from a table. -.It Fl T Cm expire Ar number -Delete addresses which had their statistics cleared more than -.Ar number -seconds ago. -For entries which have never had their statistics cleared, -.Ar number -refers to the time they were added to the table. -.It Fl T Cm replace -Replace the addresses of the table. -Automatically create a nonexisting table. -.It Fl T Cm show -Show the content (addresses) of a table. -.It Fl T Cm test -Test if the given addresses match a table. -.It Fl T Cm zero -Clear all the statistics of a table. -.It Fl T Cm load -Load only the table definitions from -.Xr pf.conf 5 . -This is used in conjunction with the -.Fl f -flag, as in: -.Bd -literal -offset indent -# pfctl -Tl -f pf.conf -.Ed -.El -.Pp -For the -.Cm add , -.Cm delete , -.Cm replace , -and -.Cm test -commands, the list of addresses can be specified either directly on the command -line and/or in an unformatted text file, using the -.Fl f -flag. -Comments starting with a -.Sq # -are allowed in the text file. -With these commands, the -.Fl v -flag can also be used once or twice, in which case -.Nm -will print the -detailed result of the operation for each individual address, prefixed by -one of the following letters: -.Pp -.Bl -tag -width XXX -compact -.It A -The address/network has been added. -.It C -The address/network has been changed (negated). -.It D -The address/network has been deleted. -.It M -The address matches -.Po -.Cm test -operation only -.Pc . -.It X -The address/network is duplicated and therefore ignored. -.It Y -The address/network cannot be added/deleted due to conflicting -.Sq \&! -attributes. -.It Z -The address/network has been cleared (statistics). -.El -.Pp -Each table can maintain a set of counters that can be retrieved using the -.Fl v -flag of -.Nm . -For example, the following commands define a wide open firewall which will keep -track of packets going to or coming from the -.Ox -FTP server. -The following commands configure the firewall and send 10 pings to the FTP -server: -.Bd -literal -offset indent -# printf "table counters { ftp.openbsd.org }\en \e - pass out to \en" | pfctl -f- -# ping -qc10 ftp.openbsd.org -.Ed -.Pp -We can now use the table -.Cm show -command to output, for each address and packet direction, the number of packets -and bytes that are being passed or blocked by rules referencing the table. -The time at which the current accounting started is also shown with the -.Dq Cleared -line. -.Bd -literal -offset indent -# pfctl -t test -vTshow - 129.128.5.191 - Cleared: Thu Feb 13 18:55:18 2003 - In/Block: [ Packets: 0 Bytes: 0 ] - In/Pass: [ Packets: 10 Bytes: 840 ] - Out/Block: [ Packets: 0 Bytes: 0 ] - Out/Pass: [ Packets: 10 Bytes: 840 ] -.Ed -.Pp -Similarly, it is possible to view global information about the tables -by using the -.Fl v -modifier twice and the -.Fl s -.Cm Tables -command. -This will display the number of addresses on each table, -the number of rules which reference the table, and the global -packet statistics for the whole table: -.Bd -literal -offset indent -# pfctl -vvsTables ---a-r-C test - Addresses: 1 - Cleared: Thu Feb 13 18:55:18 2003 - References: [ Anchors: 0 Rules: 1 ] - Evaluations: [ NoMatch: 3496 Match: 1 ] - In/Block: [ Packets: 0 Bytes: 0 ] - In/Pass: [ Packets: 10 Bytes: 840 ] - In/XPass: [ Packets: 0 Bytes: 0 ] - Out/Block: [ Packets: 0 Bytes: 0 ] - Out/Pass: [ Packets: 10 Bytes: 840 ] - Out/XPass: [ Packets: 0 Bytes: 0 ] -.Ed -.Pp -As we can see here, only one packet \- the initial ping request \- matched the -table, but all packets passing as the result of the state are correctly -accounted for. -Reloading the table(s) or ruleset will not affect packet accounting in any way. -The two -.Dq XPass -counters are incremented instead of the -.Dq Pass -counters when a -.Dq stateful -packet is passed but doesn't match the table anymore. -This will happen in our example if someone flushes the table while the -.Xr ping 8 -command is running. -.Pp -When used with a single -.Fl v , -.Nm -will only display the first line containing the table flags and name. -The flags are defined as follows: -.Pp -.Bl -tag -width XXX -compact -.It c -For constant tables, which cannot be altered outside -.Xr pf.conf 5 . -.It p -For persistent tables, which don't get automatically killed when no rules -refer to them. -.It a -For tables which are part of the -.Em active -tableset. -Tables without this flag do not really exist, cannot contain addresses, and are -only listed if the -.Fl g -flag is given. -.It i -For tables which are part of the -.Em inactive -tableset. -This flag can only be witnessed briefly during the loading of -.Xr pf.conf 5 . -.It r -For tables which are referenced (used) by rules. -.It h -This flag is set when a table in the main ruleset is hidden by one or more -tables of the same name from anchors attached below it. -.It C -This flag is set when per-address counters are enabled on the table. -.El -.It Fl t Ar table -Specify the name of the table. -.It Fl v -Produce more verbose output. -A second use of -.Fl v -will produce even more verbose output including ruleset warnings. -See the previous section for its effect on table commands. -.It Fl x Ar level -Set the debug -.Ar level -(may be abbreviated) to one of the following: -.Pp -.Bl -tag -width xxxxxxxxxxxx -compact -.It Fl x Cm none -Don't generate debug messages. -.It Fl x Cm urgent -Generate debug messages only for serious errors. -.It Fl x Cm misc -Generate debug messages for various errors. -.It Fl x Cm loud -Generate debug messages for common conditions. -.El -.It Fl z -Clear per-rule statistics. -.El -.Sh FILES -.Bl -tag -width "/etc/pf.conf" -compact -.It Pa /etc/pf.conf -Packet filter rules file. -.It Pa /etc/pf.os -Passive operating system fingerprint database. -.El -.Sh SEE ALSO -.Xr pf 4 , -.Xr pf.conf 5 , -.Xr pf.os 5 , -.Xr rc.conf 5 , -.Xr services 5 , -.Xr sysctl.conf 5 , -.Xr authpf 8 , -.Xr ftp-proxy 8 , -.Xr rc 8 , -.Xr sysctl 8 -.Sh HISTORY -The -.Nm -program and the -.Xr pf 4 -filter mechanism first appeared in -.Ox 3.0 . diff --git a/contrib/pf/pfctl/pfctl.c b/contrib/pf/pfctl/pfctl.c deleted file mode 100644 index 90a2bb5b7c7dd..0000000000000 --- a/contrib/pf/pfctl/pfctl.c +++ /dev/null @@ -1,2391 +0,0 @@ -/* $OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include - -#ifdef __FreeBSD__ -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -void usage(void); -int pfctl_enable(int, int); -int pfctl_disable(int, int); -int pfctl_clear_stats(int, int); -int pfctl_clear_interface_flags(int, int); -int pfctl_clear_rules(int, int, char *); -int pfctl_clear_nat(int, int, char *); -int pfctl_clear_altq(int, int); -int pfctl_clear_src_nodes(int, int); -int pfctl_clear_states(int, const char *, int); -void pfctl_addrprefix(char *, struct pf_addr *); -int pfctl_kill_src_nodes(int, const char *, int); -int pfctl_net_kill_states(int, const char *, int); -int pfctl_label_kill_states(int, const char *, int); -int pfctl_id_kill_states(int, const char *, int); -void pfctl_init_options(struct pfctl *); -int pfctl_load_options(struct pfctl *); -int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int); -int pfctl_load_timeout(struct pfctl *, unsigned int, unsigned int); -int pfctl_load_debug(struct pfctl *, unsigned int); -int pfctl_load_logif(struct pfctl *, char *); -int pfctl_load_hostid(struct pfctl *, unsigned int); -int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int, - char *); -void pfctl_print_rule_counters(struct pf_rule *, int); -int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int); -int pfctl_show_nat(int, int, char *); -int pfctl_show_src_nodes(int, int); -int pfctl_show_states(int, const char *, int); -int pfctl_show_status(int, int); -int pfctl_show_timeouts(int, int); -int pfctl_show_limits(int, int); -void pfctl_debug(int, u_int32_t, int); -int pfctl_test_altqsupport(int, int); -int pfctl_show_anchors(int, int, char *); -int pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *); -int pfctl_load_ruleset(struct pfctl *, char *, - struct pf_ruleset *, int, int); -int pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int); -const char *pfctl_lookup_option(char *, const char **); - -struct pf_anchor_global pf_anchors; -struct pf_anchor pf_main_anchor; - -const char *clearopt; -char *rulesopt; -const char *showopt; -const char *debugopt; -char *anchoropt; -const char *optiopt = NULL; -char *pf_device = "/dev/pf"; -char *ifaceopt; -char *tableopt; -const char *tblcmdopt; -int src_node_killers; -char *src_node_kill[2]; -int state_killers; -char *state_kill[2]; -int loadopt; -int altqsupport; - -int dev = -1; -int first_title = 1; -int labels = 0; - -#define INDENT(d, o) do { \ - if (o) { \ - int i; \ - for (i=0; i < d; i++) \ - printf(" "); \ - } \ - } while (0); \ - - -static const struct { - const char *name; - int index; -} pf_limits[] = { - { "states", PF_LIMIT_STATES }, - { "src-nodes", PF_LIMIT_SRC_NODES }, - { "frags", PF_LIMIT_FRAGS }, - { "table-entries", PF_LIMIT_TABLE_ENTRIES }, - { NULL, 0 } -}; - -struct pf_hint { - const char *name; - int timeout; -}; -static const struct pf_hint pf_hint_normal[] = { - { "tcp.first", 2 * 60 }, - { "tcp.opening", 30 }, - { "tcp.established", 24 * 60 * 60 }, - { "tcp.closing", 15 * 60 }, - { "tcp.finwait", 45 }, - { "tcp.closed", 90 }, - { "tcp.tsdiff", 30 }, - { NULL, 0 } -}; -static const struct pf_hint pf_hint_satellite[] = { - { "tcp.first", 3 * 60 }, - { "tcp.opening", 30 + 5 }, - { "tcp.established", 24 * 60 * 60 }, - { "tcp.closing", 15 * 60 + 5 }, - { "tcp.finwait", 45 + 5 }, - { "tcp.closed", 90 + 5 }, - { "tcp.tsdiff", 60 }, - { NULL, 0 } -}; -static const struct pf_hint pf_hint_conservative[] = { - { "tcp.first", 60 * 60 }, - { "tcp.opening", 15 * 60 }, - { "tcp.established", 5 * 24 * 60 * 60 }, - { "tcp.closing", 60 * 60 }, - { "tcp.finwait", 10 * 60 }, - { "tcp.closed", 3 * 60 }, - { "tcp.tsdiff", 60 }, - { NULL, 0 } -}; -static const struct pf_hint pf_hint_aggressive[] = { - { "tcp.first", 30 }, - { "tcp.opening", 5 }, - { "tcp.established", 5 * 60 * 60 }, - { "tcp.closing", 60 }, - { "tcp.finwait", 30 }, - { "tcp.closed", 30 }, - { "tcp.tsdiff", 10 }, - { NULL, 0 } -}; - -static const struct { - const char *name; - const struct pf_hint *hint; -} pf_hints[] = { - { "normal", pf_hint_normal }, - { "satellite", pf_hint_satellite }, - { "high-latency", pf_hint_satellite }, - { "conservative", pf_hint_conservative }, - { "aggressive", pf_hint_aggressive }, - { NULL, NULL } -}; - -static const char *clearopt_list[] = { - "nat", "queue", "rules", "Sources", - "states", "info", "Tables", "osfp", "all", NULL -}; - -static const char *showopt_list[] = { - "nat", "queue", "rules", "Anchors", "Sources", "states", "info", - "Interfaces", "labels", "timeouts", "memory", "Tables", "osfp", - "all", NULL -}; - -static const char *tblcmdopt_list[] = { - "kill", "flush", "add", "delete", "load", "replace", "show", - "test", "zero", "expire", NULL -}; - -static const char *debugopt_list[] = { - "none", "urgent", "misc", "loud", NULL -}; - -static const char *optiopt_list[] = { - "none", "basic", "profile", NULL -}; - -void -usage(void) -{ - extern char *__progname; - - fprintf(stderr, "usage: %s [-AdeghmNnOPqRrvz] ", __progname); - fprintf(stderr, "[-a anchor] [-D macro=value] [-F modifier]\n"); - fprintf(stderr, "\t[-f file] [-i interface] [-K host | network]\n"); - fprintf(stderr, "\t[-k host | network | label | id] "); - fprintf(stderr, "[-o level] [-p device]\n"); - fprintf(stderr, "\t[-s modifier] "); - fprintf(stderr, "[-t table -T command [address ...]] [-x level]\n"); - exit(1); -} - -int -pfctl_enable(int dev, int opts) -{ - if (ioctl(dev, DIOCSTART)) { - if (errno == EEXIST) - errx(1, "pf already enabled"); -#ifdef __FreeBSD__ - else if (errno == ESRCH) - errx(1, "pfil registeration failed"); -#endif - else - err(1, "DIOCSTART"); - } - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf enabled\n"); - - if (altqsupport && ioctl(dev, DIOCSTARTALTQ)) - if (errno != EEXIST) - err(1, "DIOCSTARTALTQ"); - - return (0); -} - -int -pfctl_disable(int dev, int opts) -{ - if (ioctl(dev, DIOCSTOP)) { - if (errno == ENOENT) - errx(1, "pf not enabled"); - else - err(1, "DIOCSTOP"); - } - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf disabled\n"); - - if (altqsupport && ioctl(dev, DIOCSTOPALTQ)) - if (errno != ENOENT) - err(1, "DIOCSTOPALTQ"); - - return (0); -} - -int -pfctl_clear_stats(int dev, int opts) -{ - if (ioctl(dev, DIOCCLRSTATUS)) - err(1, "DIOCCLRSTATUS"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf: statistics cleared\n"); - return (0); -} - -int -pfctl_clear_interface_flags(int dev, int opts) -{ - struct pfioc_iface pi; - - if ((opts & PF_OPT_NOACTION) == 0) { - bzero(&pi, sizeof(pi)); - pi.pfiio_flags = PFI_IFLAG_SKIP; - - if (ioctl(dev, DIOCCLRIFFLAG, &pi)) - err(1, "DIOCCLRIFFLAG"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "pf: interface flags reset\n"); - } - return (0); -} - -int -pfctl_clear_rules(int dev, int opts, char *anchorname) -{ - struct pfr_buffer t; - - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname) || - pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname) || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) - err(1, "pfctl_clear_rules"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "rules cleared\n"); - return (0); -} - -int -pfctl_clear_nat(int dev, int opts, char *anchorname) -{ - struct pfr_buffer t; - - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname) || - pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname) || - pfctl_add_trans(&t, PF_RULESET_RDR, anchorname) || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) - err(1, "pfctl_clear_nat"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "nat cleared\n"); - return (0); -} - -int -pfctl_clear_altq(int dev, int opts) -{ - struct pfr_buffer t; - - if (!altqsupport) - return (-1); - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) - err(1, "pfctl_clear_altq"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "altq cleared\n"); - return (0); -} - -int -pfctl_clear_src_nodes(int dev, int opts) -{ - if (ioctl(dev, DIOCCLRSRCNODES)) - err(1, "DIOCCLRSRCNODES"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "source tracking entries cleared\n"); - return (0); -} - -int -pfctl_clear_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - - memset(&psk, 0, sizeof(psk)); - if (iface != NULL && strlcpy(psk.psk_ifname, iface, - sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) - errx(1, "invalid interface: %s", iface); - - if (ioctl(dev, DIOCCLRSTATES, &psk)) - err(1, "DIOCCLRSTATES"); - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "%d states cleared\n", psk.psk_killed); - return (0); -} - -void -pfctl_addrprefix(char *addr, struct pf_addr *mask) -{ - char *p; - const char *errstr; - int prefix, ret_ga, q, r; - struct addrinfo hints, *res; - - if ((p = strchr(addr, '/')) == NULL) - return; - - *p++ = '\0'; - prefix = strtonum(p, 0, 128, &errstr); - if (errstr) - errx(1, "prefix is %s: %s", errstr, p); - - bzero(&hints, sizeof(hints)); - /* prefix only with numeric addresses */ - hints.ai_flags |= AI_NUMERICHOST; - - if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } - - if (res->ai_family == AF_INET && prefix > 32) - errx(1, "prefix too long for AF_INET"); - else if (res->ai_family == AF_INET6 && prefix > 128) - errx(1, "prefix too long for AF_INET6"); - - q = prefix >> 3; - r = prefix & 7; - switch (res->ai_family) { - case AF_INET: - bzero(&mask->v4, sizeof(mask->v4)); - mask->v4.s_addr = htonl((u_int32_t) - (0xffffffffffULL << (32 - prefix))); - break; - case AF_INET6: - bzero(&mask->v6, sizeof(mask->v6)); - if (q > 0) - memset((void *)&mask->v6, 0xff, q); - if (r > 0) - *((u_char *)&mask->v6 + q) = - (0xff00 >> r) & 0xff; - break; - } - freeaddrinfo(res); -} - -int -pfctl_kill_src_nodes(int dev, const char *iface, int opts) -{ - struct pfioc_src_node_kill psnk; - struct addrinfo *res[2], *resp[2]; - struct sockaddr last_src, last_dst; - int killed, sources, dests; - int ret_ga; - - killed = sources = dests = 0; - - memset(&psnk, 0, sizeof(psnk)); - memset(&psnk.psnk_src.addr.v.a.mask, 0xff, - sizeof(psnk.psnk_src.addr.v.a.mask)); - memset(&last_src, 0xff, sizeof(last_src)); - memset(&last_dst, 0xff, sizeof(last_dst)); - - pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask); - - if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { - if (resp[0]->ai_addr == NULL) - continue; - /* We get lots of duplicates. Catch the easy ones */ - if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) - continue; - last_src = *(struct sockaddr *)resp[0]->ai_addr; - - psnk.psnk_af = resp[0]->ai_family; - sources++; - - if (psnk.psnk_af == AF_INET) - psnk.psnk_src.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; - else if (psnk.psnk_af == AF_INET6) - psnk.psnk_src.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[0]->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", psnk.psnk_af); - - if (src_node_killers > 1) { - dests = 0; - memset(&psnk.psnk_dst.addr.v.a.mask, 0xff, - sizeof(psnk.psnk_dst.addr.v.a.mask)); - memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(src_node_kill[1], - &psnk.psnk_dst.addr.v.a.mask); - if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL, - &res[1]))) { - errx(1, "getaddrinfo: %s", - gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[1] = res[1]; resp[1]; - resp[1] = resp[1]->ai_next) { - if (resp[1]->ai_addr == NULL) - continue; - if (psnk.psnk_af != resp[1]->ai_family) - continue; - - if (memcmp(&last_dst, resp[1]->ai_addr, - sizeof(last_dst)) == 0) - continue; - last_dst = *(struct sockaddr *)resp[1]->ai_addr; - - dests++; - - if (psnk.psnk_af == AF_INET) - psnk.psnk_dst.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[1]-> - ai_addr)->sin_addr; - else if (psnk.psnk_af == AF_INET6) - psnk.psnk_dst.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[1]-> - ai_addr)->sin6_addr; - else - errx(1, "Unknown address family %d", - psnk.psnk_af); - - if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) - err(1, "DIOCKILLSRCNODES"); - killed += psnk.psnk_killed; - } - freeaddrinfo(res[1]); - } else { - if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) - err(1, "DIOCKILLSRCNODES"); - killed += psnk.psnk_killed; - } - } - - freeaddrinfo(res[0]); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d src nodes from %d sources and %d " - "destinations\n", killed, sources, dests); - return (0); -} - -int -pfctl_net_kill_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - struct addrinfo *res[2], *resp[2]; - struct sockaddr last_src, last_dst; - int killed, sources, dests; - int ret_ga; - - killed = sources = dests = 0; - - memset(&psk, 0, sizeof(psk)); - memset(&psk.psk_src.addr.v.a.mask, 0xff, - sizeof(psk.psk_src.addr.v.a.mask)); - memset(&last_src, 0xff, sizeof(last_src)); - memset(&last_dst, 0xff, sizeof(last_dst)); - if (iface != NULL && strlcpy(psk.psk_ifname, iface, - sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) - errx(1, "invalid interface: %s", iface); - - pfctl_addrprefix(state_kill[0], &psk.psk_src.addr.v.a.mask); - - if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { - if (resp[0]->ai_addr == NULL) - continue; - /* We get lots of duplicates. Catch the easy ones */ - if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) - continue; - last_src = *(struct sockaddr *)resp[0]->ai_addr; - - psk.psk_af = resp[0]->ai_family; - sources++; - - if (psk.psk_af == AF_INET) - psk.psk_src.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; - else if (psk.psk_af == AF_INET6) - psk.psk_src.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[0]->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", psk.psk_af); - - if (state_killers > 1) { - dests = 0; - memset(&psk.psk_dst.addr.v.a.mask, 0xff, - sizeof(psk.psk_dst.addr.v.a.mask)); - memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(state_kill[1], - &psk.psk_dst.addr.v.a.mask); - if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, - &res[1]))) { - errx(1, "getaddrinfo: %s", - gai_strerror(ret_ga)); - /* NOTREACHED */ - } - for (resp[1] = res[1]; resp[1]; - resp[1] = resp[1]->ai_next) { - if (resp[1]->ai_addr == NULL) - continue; - if (psk.psk_af != resp[1]->ai_family) - continue; - - if (memcmp(&last_dst, resp[1]->ai_addr, - sizeof(last_dst)) == 0) - continue; - last_dst = *(struct sockaddr *)resp[1]->ai_addr; - - dests++; - - if (psk.psk_af == AF_INET) - psk.psk_dst.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[1]-> - ai_addr)->sin_addr; - else if (psk.psk_af == AF_INET6) - psk.psk_dst.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[1]-> - ai_addr)->sin6_addr; - else - errx(1, "Unknown address family %d", - psk.psk_af); - - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - killed += psk.psk_killed; - } - freeaddrinfo(res[1]); - } else { - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - killed += psk.psk_killed; - } - } - - freeaddrinfo(res[0]); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d states from %d sources and %d " - "destinations\n", killed, sources, dests); - return (0); -} - -int -pfctl_label_kill_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - - if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { - warnx("no label specified"); - usage(); - } - memset(&psk, 0, sizeof(psk)); - if (iface != NULL && strlcpy(psk.psk_ifname, iface, - sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) - errx(1, "invalid interface: %s", iface); - - if (strlcpy(psk.psk_label, state_kill[1], sizeof(psk.psk_label)) >= - sizeof(psk.psk_label)) - errx(1, "label too long: %s", state_kill[1]); - - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d states\n", psk.psk_killed); - - return (0); -} - -int -pfctl_id_kill_states(int dev, const char *iface, int opts) -{ - struct pfioc_state_kill psk; - - if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { - warnx("no id specified"); - usage(); - } - - memset(&psk, 0, sizeof(psk)); - if ((sscanf(state_kill[1], "%jx/%x", - &psk.psk_pfcmp.id, &psk.psk_pfcmp.creatorid)) == 2) - HTONL(psk.psk_pfcmp.creatorid); - else if ((sscanf(state_kill[1], "%jx", &psk.psk_pfcmp.id)) == 1) { - psk.psk_pfcmp.creatorid = 0; - } else { - warnx("wrong id format specified"); - usage(); - } - if (psk.psk_pfcmp.id == 0) { - warnx("cannot kill id 0"); - usage(); - } - - psk.psk_pfcmp.id = htobe64(psk.psk_pfcmp.id); - if (ioctl(dev, DIOCKILLSTATES, &psk)) - err(1, "DIOCKILLSTATES"); - - if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "killed %d states\n", psk.psk_killed); - - return (0); -} - -int -pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr, - u_int32_t ticket, int r_action, char *anchorname) -{ - struct pfioc_pooladdr pp; - struct pf_pooladdr *pa; - u_int32_t pnr, mpnr; - - memset(&pp, 0, sizeof(pp)); - memcpy(pp.anchor, anchorname, sizeof(pp.anchor)); - pp.r_action = r_action; - pp.r_num = nr; - pp.ticket = ticket; - if (ioctl(dev, DIOCGETADDRS, &pp)) { - warn("DIOCGETADDRS"); - return (-1); - } - mpnr = pp.nr; - TAILQ_INIT(&pool->list); - for (pnr = 0; pnr < mpnr; ++pnr) { - pp.nr = pnr; - if (ioctl(dev, DIOCGETADDR, &pp)) { - warn("DIOCGETADDR"); - return (-1); - } - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "calloc"); - bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr)); - TAILQ_INSERT_TAIL(&pool->list, pa, entries); - } - - return (0); -} - -void -pfctl_move_pool(struct pf_pool *src, struct pf_pool *dst) -{ - struct pf_pooladdr *pa; - - while ((pa = TAILQ_FIRST(&src->list)) != NULL) { - TAILQ_REMOVE(&src->list, pa, entries); - TAILQ_INSERT_TAIL(&dst->list, pa, entries); - } -} - -void -pfctl_clear_pool(struct pf_pool *pool) -{ - struct pf_pooladdr *pa; - - while ((pa = TAILQ_FIRST(&pool->list)) != NULL) { - TAILQ_REMOVE(&pool->list, pa, entries); - free(pa); - } -} - -void -pfctl_print_rule_counters(struct pf_rule *rule, int opts) -{ - if (opts & PF_OPT_DEBUG) { - const char *t[PF_SKIP_COUNT] = { "i", "d", "f", - "p", "sa", "sp", "da", "dp" }; - int i; - - printf(" [ Skip steps: "); - for (i = 0; i < PF_SKIP_COUNT; ++i) { - if (rule->skip[i].nr == rule->nr + 1) - continue; - printf("%s=", t[i]); - if (rule->skip[i].nr == -1) - printf("end "); - else - printf("%u ", rule->skip[i].nr); - } - printf("]\n"); - - printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n", - rule->qname, rule->qid, rule->pqname, rule->pqid); - } - if (opts & PF_OPT_VERBOSE) { - printf(" [ Evaluations: %-8llu Packets: %-8llu " - "Bytes: %-10llu States: %-6u]\n", - (unsigned long long)rule->evaluations, - (unsigned long long)(rule->packets[0] + - rule->packets[1]), - (unsigned long long)(rule->bytes[0] + - rule->bytes[1]), rule->states_cur); - if (!(opts & PF_OPT_DEBUG)) - printf(" [ Inserted: uid %u pid %u " - "State Creations: %-6u]\n", - (unsigned)rule->cuid, (unsigned)rule->cpid, - rule->states_tot); - } -} - -void -pfctl_print_title(char *title) -{ - if (!first_title) - printf("\n"); - first_title = 0; - printf("%s\n", title); -} - -int -pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, - char *anchorname, int depth) -{ - struct pfioc_rule pr; - u_int32_t nr, mnr, header = 0; - int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG); - int numeric = opts & PF_OPT_NUMERIC; - int len = strlen(path); - int brace; - char *p; - - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); - else - snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname); - - memset(&pr, 0, sizeof(pr)); - memcpy(pr.anchor, path, sizeof(pr.anchor)); - if (opts & PF_OPT_SHOWALL) { - pr.rule.action = PF_PASS; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - goto error; - } - header++; - } - pr.rule.action = PF_SCRUB; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - goto error; - } - if (opts & PF_OPT_SHOWALL) { - if (format == PFCTL_SHOW_RULES && (pr.nr > 0 || header)) - pfctl_print_title("FILTER RULES:"); - else if (format == PFCTL_SHOW_LABELS && labels) - pfctl_print_title("LABEL COUNTERS:"); - } - mnr = pr.nr; - if (opts & PF_OPT_CLRRULECTRS) - pr.action = PF_GET_CLR_CNTR; - - for (nr = 0; nr < mnr; ++nr) { - pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); - goto error; - } - - if (pfctl_get_pool(dev, &pr.rule.rpool, - nr, pr.ticket, PF_SCRUB, path) != 0) - goto error; - - switch (format) { - case PFCTL_SHOW_LABELS: - break; - case PFCTL_SHOW_RULES: - if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) - labels = 1; - print_rule(&pr.rule, pr.anchor_call, rule_numbers, numeric); - printf("\n"); - pfctl_print_rule_counters(&pr.rule, opts); - break; - case PFCTL_SHOW_NOTHING: - break; - } - pfctl_clear_pool(&pr.rule.rpool); - } - pr.rule.action = PF_PASS; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - goto error; - } - mnr = pr.nr; - for (nr = 0; nr < mnr; ++nr) { - pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); - goto error; - } - - if (pfctl_get_pool(dev, &pr.rule.rpool, - nr, pr.ticket, PF_PASS, path) != 0) - goto error; - - switch (format) { - case PFCTL_SHOW_LABELS: - if (pr.rule.label[0]) { - printf("%s %llu %llu %llu %llu" - " %llu %llu %llu %llu\n", - pr.rule.label, - (unsigned long long)pr.rule.evaluations, - (unsigned long long)(pr.rule.packets[0] + - pr.rule.packets[1]), - (unsigned long long)(pr.rule.bytes[0] + - pr.rule.bytes[1]), - (unsigned long long)pr.rule.packets[0], - (unsigned long long)pr.rule.bytes[0], - (unsigned long long)pr.rule.packets[1], - (unsigned long long)pr.rule.bytes[1], - (unsigned long long)pr.rule.states_tot); - } - break; - case PFCTL_SHOW_RULES: - brace = 0; - if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) - labels = 1; - INDENT(depth, !(opts & PF_OPT_VERBOSE)); - if (pr.anchor_call[0] && - ((((p = strrchr(pr.anchor_call, '_')) != NULL) && - ((void *)p == (void *)pr.anchor_call || - *(--p) == '/')) || (opts & PF_OPT_RECURSE))) { - brace++; - if ((p = strrchr(pr.anchor_call, '/')) != - NULL) - p++; - else - p = &pr.anchor_call[0]; - } else - p = &pr.anchor_call[0]; - - print_rule(&pr.rule, p, rule_numbers, numeric); - if (brace) - printf(" {\n"); - else - printf("\n"); - pfctl_print_rule_counters(&pr.rule, opts); - if (brace) { - pfctl_show_rules(dev, path, opts, format, - p, depth + 1); - INDENT(depth, !(opts & PF_OPT_VERBOSE)); - printf("}\n"); - } - break; - case PFCTL_SHOW_NOTHING: - break; - } - pfctl_clear_pool(&pr.rule.rpool); - } - path[len] = '\0'; - return (0); - - error: - path[len] = '\0'; - return (-1); -} - -int -pfctl_show_nat(int dev, int opts, char *anchorname) -{ - struct pfioc_rule pr; - u_int32_t mnr, nr; - static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT }; - int i, dotitle = opts & PF_OPT_SHOWALL; - - memset(&pr, 0, sizeof(pr)); - memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); - for (i = 0; i < 3; i++) { - pr.rule.action = nattype[i]; - if (ioctl(dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - return (-1); - } - mnr = pr.nr; - for (nr = 0; nr < mnr; ++nr) { - pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); - return (-1); - } - if (pfctl_get_pool(dev, &pr.rule.rpool, nr, - pr.ticket, nattype[i], anchorname) != 0) - return (-1); - if (dotitle) { - pfctl_print_title("TRANSLATION RULES:"); - dotitle = 0; - } - print_rule(&pr.rule, pr.anchor_call, - opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC); - printf("\n"); - pfctl_print_rule_counters(&pr.rule, opts); - pfctl_clear_pool(&pr.rule.rpool); - } - } - return (0); -} - -int -pfctl_show_src_nodes(int dev, int opts) -{ - struct pfioc_src_nodes psn; - struct pf_src_node *p; - char *inbuf = NULL, *newinbuf = NULL; - unsigned int len = 0; - int i; - - memset(&psn, 0, sizeof(psn)); - for (;;) { - psn.psn_len = len; - if (len) { - newinbuf = realloc(inbuf, len); - if (newinbuf == NULL) - err(1, "realloc"); - psn.psn_buf = inbuf = newinbuf; - } - if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) { - warn("DIOCGETSRCNODES"); - free(inbuf); - return (-1); - } - if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len) - break; - if (len == 0 && psn.psn_len == 0) - goto done; - if (len == 0 && psn.psn_len != 0) - len = psn.psn_len; - if (psn.psn_len == 0) - goto done; /* no src_nodes */ - len *= 2; - } - p = psn.psn_src_nodes; - if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL)) - pfctl_print_title("SOURCE TRACKING NODES:"); - for (i = 0; i < psn.psn_len; i += sizeof(*p)) { - print_src_node(p, opts); - p++; - } -done: - free(inbuf); - return (0); -} - -int -pfctl_show_states(int dev, const char *iface, int opts) -{ - struct pfioc_states ps; - struct pfsync_state *p; - char *inbuf = NULL, *newinbuf = NULL; - unsigned int len = 0; - int i, dotitle = (opts & PF_OPT_SHOWALL); - - memset(&ps, 0, sizeof(ps)); - for (;;) { - ps.ps_len = len; - if (len) { - newinbuf = realloc(inbuf, len); - if (newinbuf == NULL) - err(1, "realloc"); - ps.ps_buf = inbuf = newinbuf; - } - if (ioctl(dev, DIOCGETSTATES, &ps) < 0) { - warn("DIOCGETSTATES"); - free(inbuf); - return (-1); - } - if (ps.ps_len + sizeof(struct pfioc_states) < len) - break; - if (len == 0 && ps.ps_len == 0) - goto done; - if (len == 0 && ps.ps_len != 0) - len = ps.ps_len; - if (ps.ps_len == 0) - goto done; /* no states */ - len *= 2; - } - p = ps.ps_states; - for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) { - if (iface != NULL && strcmp(p->ifname, iface)) - continue; - if (dotitle) { - pfctl_print_title("STATES:"); - dotitle = 0; - } - print_state(p, opts); - } -done: - free(inbuf); - return (0); -} - -int -pfctl_show_status(int dev, int opts) -{ - struct pf_status status; - - if (ioctl(dev, DIOCGETSTATUS, &status)) { - warn("DIOCGETSTATUS"); - return (-1); - } - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("INFO:"); - print_status(&status, opts); - return (0); -} - -int -pfctl_show_timeouts(int dev, int opts) -{ - struct pfioc_tm pt; - int i; - - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("TIMEOUTS:"); - memset(&pt, 0, sizeof(pt)); - for (i = 0; pf_timeouts[i].name; i++) { - pt.timeout = pf_timeouts[i].timeout; - if (ioctl(dev, DIOCGETTIMEOUT, &pt)) - err(1, "DIOCGETTIMEOUT"); - printf("%-20s %10d", pf_timeouts[i].name, pt.seconds); - if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START && - pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END) - printf(" states"); - else - printf("s"); - printf("\n"); - } - return (0); - -} - -int -pfctl_show_limits(int dev, int opts) -{ - struct pfioc_limit pl; - int i; - - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("LIMITS:"); - memset(&pl, 0, sizeof(pl)); - for (i = 0; pf_limits[i].name; i++) { - pl.index = pf_limits[i].index; - if (ioctl(dev, DIOCGETLIMIT, &pl)) - err(1, "DIOCGETLIMIT"); - printf("%-13s ", pf_limits[i].name); - if (pl.limit == UINT_MAX) - printf("unlimited\n"); - else - printf("hard limit %8u\n", pl.limit); - } - return (0); -} - -/* callbacks for rule/nat/rdr/addr */ -int -pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af) -{ - struct pf_pooladdr *pa; - - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) - err(1, "DIOCBEGINADDRS"); - } - - pf->paddr.af = af; - TAILQ_FOREACH(pa, &p->list, entries) { - memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr)); - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) - err(1, "DIOCADDADDR"); - } - } - return (0); -} - -int -pfctl_add_rule(struct pfctl *pf, struct pf_rule *r, const char *anchor_call) -{ - u_int8_t rs_num; - struct pf_rule *rule; - struct pf_ruleset *rs; - char *p; - - rs_num = pf_get_ruleset_number(r->action); - if (rs_num == PF_RULESET_MAX) - errx(1, "Invalid rule type %d", r->action); - - rs = &pf->anchor->ruleset; - - if (anchor_call[0] && r->anchor == NULL) { - /* - * Don't make non-brace anchors part of the main anchor pool. - */ - if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL) - err(1, "pfctl_add_rule: calloc"); - - pf_init_ruleset(&r->anchor->ruleset); - r->anchor->ruleset.anchor = r->anchor; - if (strlcpy(r->anchor->path, anchor_call, - sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path)) - errx(1, "pfctl_add_rule: strlcpy"); - if ((p = strrchr(anchor_call, '/')) != NULL) { - if (!strlen(p)) - err(1, "pfctl_add_rule: bad anchor name %s", - anchor_call); - } else - p = (char *)anchor_call; - if (strlcpy(r->anchor->name, p, - sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name)) - errx(1, "pfctl_add_rule: strlcpy"); - } - - if ((rule = calloc(1, sizeof(*rule))) == NULL) - err(1, "calloc"); - bcopy(r, rule, sizeof(*rule)); - TAILQ_INIT(&rule->rpool.list); - pfctl_move_pool(&r->rpool, &rule->rpool); - - TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries); - return (0); -} - -int -pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a) -{ - int osize = pf->trans->pfrb_size; - - if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) || - pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) || - pfctl_add_trans(pf->trans, PF_RULESET_RDR, path)) - return (1); - } - if (a == pf->astack[0] && ((altqsupport && - (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) { - if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path)) - return (2); - } - if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) || - pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path)) - return (3); - } - if (pf->loadopt & PFCTL_FLAG_TABLE) - if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path)) - return (4); - if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize)) - return (5); - - return (0); -} - -int -pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs, - int rs_num, int depth) -{ - struct pf_rule *r; - int error, len = strlen(path); - int brace = 0; - - pf->anchor = rs->anchor; - - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name); - else - snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name); - - if (depth) { - if (TAILQ_FIRST(rs->rules[rs_num].active.ptr) != NULL) { - brace++; - if (pf->opts & PF_OPT_VERBOSE) - printf(" {\n"); - if ((pf->opts & PF_OPT_NOACTION) == 0 && - (error = pfctl_ruleset_trans(pf, - path, rs->anchor))) { - printf("pfctl_load_rulesets: " - "pfctl_ruleset_trans %d\n", error); - goto error; - } - } else if (pf->opts & PF_OPT_VERBOSE) - printf("\n"); - - } - - if (pf->optimize && rs_num == PF_RULESET_FILTER) - pfctl_optimize_ruleset(pf, rs); - - while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) { - TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries); - if ((error = pfctl_load_rule(pf, path, r, depth))) - goto error; - if (r->anchor) { - if ((error = pfctl_load_ruleset(pf, path, - &r->anchor->ruleset, rs_num, depth + 1))) - goto error; - } else if (pf->opts & PF_OPT_VERBOSE) - printf("\n"); - free(r); - } - if (brace && pf->opts & PF_OPT_VERBOSE) { - INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE)); - printf("}\n"); - } - path[len] = '\0'; - return (0); - - error: - path[len] = '\0'; - return (error); - -} - -int -pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth) -{ - u_int8_t rs_num = pf_get_ruleset_number(r->action); - char *name; - struct pfioc_rule pr; - int len = strlen(path); - - bzero(&pr, sizeof(pr)); - /* set up anchor before adding to path for anchor_call */ - if ((pf->opts & PF_OPT_NOACTION) == 0) - pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path); - if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor)) - errx(1, "pfctl_load_rule: strlcpy"); - - if (r->anchor) { - if (r->anchor->match) { - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, - "/%s", r->anchor->name); - else - snprintf(&path[len], MAXPATHLEN - len, - "%s", r->anchor->name); - name = path; - } else - name = r->anchor->path; - } else - name = ""; - - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (pfctl_add_pool(pf, &r->rpool, r->af)) - return (1); - pr.pool_ticket = pf->paddr.ticket; - memcpy(&pr.rule, r, sizeof(pr.rule)); - if (r->anchor && strlcpy(pr.anchor_call, name, - sizeof(pr.anchor_call)) >= sizeof(pr.anchor_call)) - errx(1, "pfctl_load_rule: strlcpy"); - if (ioctl(pf->dev, DIOCADDRULE, &pr)) - err(1, "DIOCADDRULE"); - } - - if (pf->opts & PF_OPT_VERBOSE) { - INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2)); - print_rule(r, r->anchor ? r->anchor->name : "", - pf->opts & PF_OPT_VERBOSE2, - pf->opts & PF_OPT_NUMERIC); - } - path[len] = '\0'; - pfctl_clear_pool(&r->rpool); - return (0); -} - -int -pfctl_add_altq(struct pfctl *pf, struct pf_altq *a) -{ - if (altqsupport && - (loadopt & PFCTL_FLAG_ALTQ) != 0) { - memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq)); - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) { - if (errno == ENXIO) - errx(1, "qtype not configured"); - else if (errno == ENODEV) - errx(1, "%s: driver does not support " - "altq", a->ifname); - else - err(1, "DIOCADDALTQ"); - } - } - pfaltq_store(&pf->paltq->altq); - } - return (0); -} - -int -pfctl_rules(int dev, char *filename, int opts, int optimize, - char *anchorname, struct pfr_buffer *trans) -{ -#define ERR(x) do { warn(x); goto _error; } while(0) -#define ERRX(x) do { warnx(x); goto _error; } while(0) - - struct pfr_buffer *t, buf; - struct pfioc_altq pa; - struct pfctl pf; - struct pf_ruleset *rs; - struct pfr_table trs; - char *path; - int osize; - - RB_INIT(&pf_anchors); - memset(&pf_main_anchor, 0, sizeof(pf_main_anchor)); - pf_init_ruleset(&pf_main_anchor.ruleset); - pf_main_anchor.ruleset.anchor = &pf_main_anchor; - if (trans == NULL) { - bzero(&buf, sizeof(buf)); - buf.pfrb_type = PFRB_TRANS; - t = &buf; - osize = 0; - } else { - t = trans; - osize = t->pfrb_size; - } - - memset(&pa, 0, sizeof(pa)); - memset(&pf, 0, sizeof(pf)); - memset(&trs, 0, sizeof(trs)); - if ((path = calloc(1, MAXPATHLEN)) == NULL) - ERRX("pfctl_rules: calloc"); - if (strlcpy(trs.pfrt_anchor, anchorname, - sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor)) - ERRX("pfctl_rules: strlcpy"); - pf.dev = dev; - pf.opts = opts; - pf.optimize = optimize; - pf.loadopt = loadopt; - - /* non-brace anchor, create without resolving the path */ - if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL) - ERRX("pfctl_rules: calloc"); - rs = &pf.anchor->ruleset; - pf_init_ruleset(rs); - rs->anchor = pf.anchor; - if (strlcpy(pf.anchor->path, anchorname, - sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path)) - errx(1, "pfctl_add_rule: strlcpy"); - if (strlcpy(pf.anchor->name, anchorname, - sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name)) - errx(1, "pfctl_add_rule: strlcpy"); - - - pf.astack[0] = pf.anchor; - pf.asd = 0; - if (anchorname[0]) - pf.loadopt &= ~PFCTL_FLAG_ALTQ; - pf.paltq = &pa; - pf.trans = t; - pfctl_init_options(&pf); - - if ((opts & PF_OPT_NOACTION) == 0) { - /* - * XXX For the time being we need to open transactions for - * the main ruleset before parsing, because tables are still - * loaded at parse time. - */ - if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor)) - ERRX("pfctl_rules"); - if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) - pa.ticket = - pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname); - if (pf.loadopt & PFCTL_FLAG_TABLE) - pf.astack[0]->ruleset.tticket = - pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname); - } - - if (parse_config(filename, &pf) < 0) { - if ((opts & PF_OPT_NOACTION) == 0) - ERRX("Syntax error in config file: " - "pf rules not loaded"); - else - goto _error; - } - - if ((pf.loadopt & PFCTL_FLAG_FILTER && - (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) || - (pf.loadopt & PFCTL_FLAG_NAT && - (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) || - pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) || - pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) || - (pf.loadopt & PFCTL_FLAG_FILTER && - pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) { - if ((opts & PF_OPT_NOACTION) == 0) - ERRX("Unable to load rules into kernel"); - else - goto _error; - } - - if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0)) - if (check_commit_altq(dev, opts) != 0) - ERRX("errors in altq config"); - - /* process "load anchor" directives */ - if (!anchorname[0]) - if (pfctl_load_anchors(dev, &pf, t) == -1) - ERRX("load anchors"); - - if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) { - if (!anchorname[0]) - if (pfctl_load_options(&pf)) - goto _error; - if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) - ERR("DIOCXCOMMIT"); - } - return (0); - -_error: - if (trans == NULL) { /* main ruleset */ - if ((opts & PF_OPT_NOACTION) == 0) - if (pfctl_trans(dev, t, DIOCXROLLBACK, osize)) - err(1, "DIOCXROLLBACK"); - exit(1); - } else { /* sub ruleset */ - return (-1); - } - -#undef ERR -#undef ERRX -} - -FILE * -pfctl_fopen(const char *name, const char *mode) -{ - struct stat st; - FILE *fp; - - fp = fopen(name, mode); - if (fp == NULL) - return (NULL); - if (fstat(fileno(fp), &st)) { - fclose(fp); - return (NULL); - } - if (S_ISDIR(st.st_mode)) { - fclose(fp); - errno = EISDIR; - return (NULL); - } - return (fp); -} - -void -pfctl_init_options(struct pfctl *pf) -{ - - pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; - pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; - pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; - pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; - pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; - pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; - pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; - pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; - pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; - pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; - pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; - pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; - pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; - pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; - pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL; - pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL; - pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL; - pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL; - pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; - pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; - - pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT; - pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT; - pf->limit[PF_LIMIT_SRC_NODES] = PFSNODE_HIWAT; - pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT; - - pf->debug = PF_DEBUG_URGENT; -} - -int -pfctl_load_options(struct pfctl *pf) -{ - int i, error = 0; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - /* load limits */ - for (i = 0; i < PF_LIMIT_MAX; i++) { - if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i]) - continue; - if (pfctl_load_limit(pf, i, pf->limit[i])) - error = 1; - } - - /* - * If we've set the limit, but haven't explicitly set adaptive - * timeouts, do it now with a start of 60% and end of 120%. - */ - if (pf->limit_set[PF_LIMIT_STATES] && - !pf->timeout_set[PFTM_ADAPTIVE_START] && - !pf->timeout_set[PFTM_ADAPTIVE_END]) { - pf->timeout[PFTM_ADAPTIVE_START] = - (pf->limit[PF_LIMIT_STATES] / 10) * 6; - pf->timeout_set[PFTM_ADAPTIVE_START] = 1; - pf->timeout[PFTM_ADAPTIVE_END] = - (pf->limit[PF_LIMIT_STATES] / 10) * 12; - pf->timeout_set[PFTM_ADAPTIVE_END] = 1; - } - - /* load timeouts */ - for (i = 0; i < PFTM_MAX; i++) { - if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i]) - continue; - if (pfctl_load_timeout(pf, i, pf->timeout[i])) - error = 1; - } - - /* load debug */ - if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set) - if (pfctl_load_debug(pf, pf->debug)) - error = 1; - - /* load logif */ - if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set) - if (pfctl_load_logif(pf, pf->ifname)) - error = 1; - - /* load hostid */ - if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set) - if (pfctl_load_hostid(pf, pf->hostid)) - error = 1; - - return (error); -} - -int -pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit) -{ - int i; - - - for (i = 0; pf_limits[i].name; i++) { - if (strcasecmp(opt, pf_limits[i].name) == 0) { - pf->limit[pf_limits[i].index] = limit; - pf->limit_set[pf_limits[i].index] = 1; - break; - } - } - if (pf_limits[i].name == NULL) { - warnx("Bad pool name."); - return (1); - } - - if (pf->opts & PF_OPT_VERBOSE) - printf("set limit %s %d\n", opt, limit); - - return (0); -} - -int -pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit) -{ - struct pfioc_limit pl; - - memset(&pl, 0, sizeof(pl)); - pl.index = index; - pl.limit = limit; - if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) { - if (errno == EBUSY) - warnx("Current pool size exceeds requested hard limit"); - else - warnx("DIOCSETLIMIT"); - return (1); - } - return (0); -} - -int -pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) -{ - int i; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - for (i = 0; pf_timeouts[i].name; i++) { - if (strcasecmp(opt, pf_timeouts[i].name) == 0) { - pf->timeout[pf_timeouts[i].timeout] = seconds; - pf->timeout_set[pf_timeouts[i].timeout] = 1; - break; - } - } - - if (pf_timeouts[i].name == NULL) { - warnx("Bad timeout name."); - return (1); - } - - - if (pf->opts & PF_OPT_VERBOSE && ! quiet) - printf("set timeout %s %d\n", opt, seconds); - - return (0); -} - -int -pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds) -{ - struct pfioc_tm pt; - - memset(&pt, 0, sizeof(pt)); - pt.timeout = timeout; - pt.seconds = seconds; - if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) { - warnx("DIOCSETTIMEOUT"); - return (1); - } - return (0); -} - -int -pfctl_set_optimization(struct pfctl *pf, const char *opt) -{ - const struct pf_hint *hint; - int i, r; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - for (i = 0; pf_hints[i].name; i++) - if (strcasecmp(opt, pf_hints[i].name) == 0) - break; - - hint = pf_hints[i].hint; - if (hint == NULL) { - warnx("invalid state timeouts optimization"); - return (1); - } - - for (i = 0; hint[i].name; i++) - if ((r = pfctl_set_timeout(pf, hint[i].name, - hint[i].timeout, 1))) - return (r); - - if (pf->opts & PF_OPT_VERBOSE) - printf("set optimization %s\n", opt); - - return (0); -} - -int -pfctl_set_logif(struct pfctl *pf, char *ifname) -{ - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - if (!strcmp(ifname, "none")) { - free(pf->ifname); - pf->ifname = NULL; - } else { - pf->ifname = strdup(ifname); - if (!pf->ifname) - errx(1, "pfctl_set_logif: strdup"); - } - pf->ifname_set = 1; - - if (pf->opts & PF_OPT_VERBOSE) - printf("set loginterface %s\n", ifname); - - return (0); -} - -int -pfctl_load_logif(struct pfctl *pf, char *ifname) -{ - struct pfioc_if pi; - - memset(&pi, 0, sizeof(pi)); - if (ifname && strlcpy(pi.ifname, ifname, - sizeof(pi.ifname)) >= sizeof(pi.ifname)) { - warnx("pfctl_load_logif: strlcpy"); - return (1); - } - if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) { - warnx("DIOCSETSTATUSIF"); - return (1); - } - return (0); -} - -int -pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) -{ - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - HTONL(hostid); - - pf->hostid = hostid; - pf->hostid_set = 1; - - if (pf->opts & PF_OPT_VERBOSE) - printf("set hostid 0x%08x\n", ntohl(hostid)); - - return (0); -} - -int -pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid) -{ - if (ioctl(dev, DIOCSETHOSTID, &hostid)) { - warnx("DIOCSETHOSTID"); - return (1); - } - return (0); -} - -int -pfctl_set_debug(struct pfctl *pf, char *d) -{ - u_int32_t level; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - if (!strcmp(d, "none")) - pf->debug = PF_DEBUG_NONE; - else if (!strcmp(d, "urgent")) - pf->debug = PF_DEBUG_URGENT; - else if (!strcmp(d, "misc")) - pf->debug = PF_DEBUG_MISC; - else if (!strcmp(d, "loud")) - pf->debug = PF_DEBUG_NOISY; - else { - warnx("unknown debug level \"%s\"", d); - return (-1); - } - - pf->debug_set = 1; - - if ((pf->opts & PF_OPT_NOACTION) == 0) - if (ioctl(dev, DIOCSETDEBUG, &level)) - err(1, "DIOCSETDEBUG"); - - if (pf->opts & PF_OPT_VERBOSE) - printf("set debug %s\n", d); - - return (0); -} - -int -pfctl_load_debug(struct pfctl *pf, unsigned int level) -{ - if (ioctl(pf->dev, DIOCSETDEBUG, &level)) { - warnx("DIOCSETDEBUG"); - return (1); - } - return (0); -} - -int -pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how) -{ - struct pfioc_iface pi; - - if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); - - bzero(&pi, sizeof(pi)); - - pi.pfiio_flags = flags; - - if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >= - sizeof(pi.pfiio_name)) - errx(1, "pfctl_set_interface_flags: strlcpy"); - - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (how == 0) { - if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi)) - err(1, "DIOCCLRIFFLAG"); - } else { - if (ioctl(pf->dev, DIOCSETIFFLAG, &pi)) - err(1, "DIOCSETIFFLAG"); - } - } - return (0); -} - -void -pfctl_debug(int dev, u_int32_t level, int opts) -{ - if (ioctl(dev, DIOCSETDEBUG, &level)) - err(1, "DIOCSETDEBUG"); - if ((opts & PF_OPT_QUIET) == 0) { - fprintf(stderr, "debug level set to '"); - switch (level) { - case PF_DEBUG_NONE: - fprintf(stderr, "none"); - break; - case PF_DEBUG_URGENT: - fprintf(stderr, "urgent"); - break; - case PF_DEBUG_MISC: - fprintf(stderr, "misc"); - break; - case PF_DEBUG_NOISY: - fprintf(stderr, "loud"); - break; - default: - fprintf(stderr, ""); - break; - } - fprintf(stderr, "'\n"); - } -} - -int -pfctl_test_altqsupport(int dev, int opts) -{ - struct pfioc_altq pa; - - if (ioctl(dev, DIOCGETALTQS, &pa)) { - if (errno == ENODEV) { - if (!(opts & PF_OPT_QUIET)) - fprintf(stderr, "No ALTQ support in kernel\n" - "ALTQ related functions disabled\n"); - return (0); - } else - err(1, "DIOCGETALTQS"); - } - return (1); -} - -int -pfctl_show_anchors(int dev, int opts, char *anchorname) -{ - struct pfioc_ruleset pr; - u_int32_t mnr, nr; - - memset(&pr, 0, sizeof(pr)); - memcpy(pr.path, anchorname, sizeof(pr.path)); - if (ioctl(dev, DIOCGETRULESETS, &pr)) { - if (errno == EINVAL) - fprintf(stderr, "Anchor '%s' not found.\n", - anchorname); - else - err(1, "DIOCGETRULESETS"); - return (-1); - } - mnr = pr.nr; - for (nr = 0; nr < mnr; ++nr) { - char sub[MAXPATHLEN]; - - pr.nr = nr; - if (ioctl(dev, DIOCGETRULESET, &pr)) - err(1, "DIOCGETRULESET"); - if (!strcmp(pr.name, PF_RESERVED_ANCHOR)) - continue; - sub[0] = 0; - if (pr.path[0]) { - strlcat(sub, pr.path, sizeof(sub)); - strlcat(sub, "/", sizeof(sub)); - } - strlcat(sub, pr.name, sizeof(sub)); - if (sub[0] != '_' || (opts & PF_OPT_VERBOSE)) - printf(" %s\n", sub); - if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub)) - return (-1); - } - return (0); -} - -const char * -pfctl_lookup_option(char *cmd, const char **list) -{ - if (cmd != NULL && *cmd) - for (; *list; list++) - if (!strncmp(cmd, *list, strlen(cmd))) - return (*list); - return (NULL); -} - -int -main(int argc, char *argv[]) -{ - int error = 0; - int ch; - int mode = O_RDONLY; - int opts = 0; - int optimize = PF_OPTIMIZE_BASIC; - char anchorname[MAXPATHLEN]; - char *path; - - if (argc < 2) - usage(); - - while ((ch = getopt(argc, argv, - "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) { - switch (ch) { - case 'a': - anchoropt = optarg; - break; - case 'd': - opts |= PF_OPT_DISABLE; - mode = O_RDWR; - break; - case 'D': - if (pfctl_cmdline_symset(optarg) < 0) - warnx("could not parse macro definition %s", - optarg); - break; - case 'e': - opts |= PF_OPT_ENABLE; - mode = O_RDWR; - break; - case 'q': - opts |= PF_OPT_QUIET; - break; - case 'F': - clearopt = pfctl_lookup_option(optarg, clearopt_list); - if (clearopt == NULL) { - warnx("Unknown flush modifier '%s'", optarg); - usage(); - } - mode = O_RDWR; - break; - case 'i': - ifaceopt = optarg; - break; - case 'k': - if (state_killers >= 2) { - warnx("can only specify -k twice"); - usage(); - /* NOTREACHED */ - } - state_kill[state_killers++] = optarg; - mode = O_RDWR; - break; - case 'K': - if (src_node_killers >= 2) { - warnx("can only specify -K twice"); - usage(); - /* NOTREACHED */ - } - src_node_kill[src_node_killers++] = optarg; - mode = O_RDWR; - break; - case 'm': - opts |= PF_OPT_MERGE; - break; - case 'n': - opts |= PF_OPT_NOACTION; - break; - case 'N': - loadopt |= PFCTL_FLAG_NAT; - break; - case 'r': - opts |= PF_OPT_USEDNS; - break; - case 'f': - rulesopt = optarg; - mode = O_RDWR; - break; - case 'g': - opts |= PF_OPT_DEBUG; - break; - case 'A': - loadopt |= PFCTL_FLAG_ALTQ; - break; - case 'R': - loadopt |= PFCTL_FLAG_FILTER; - break; - case 'o': - optiopt = pfctl_lookup_option(optarg, optiopt_list); - if (optiopt == NULL) { - warnx("Unknown optimization '%s'", optarg); - usage(); - } - opts |= PF_OPT_OPTIMIZE; - break; - case 'O': - loadopt |= PFCTL_FLAG_OPTION; - break; - case 'p': - pf_device = optarg; - break; - case 'P': - opts |= PF_OPT_NUMERIC; - break; - case 's': - showopt = pfctl_lookup_option(optarg, showopt_list); - if (showopt == NULL) { - warnx("Unknown show modifier '%s'", optarg); - usage(); - } - break; - case 't': - tableopt = optarg; - break; - case 'T': - tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list); - if (tblcmdopt == NULL) { - warnx("Unknown table command '%s'", optarg); - usage(); - } - break; - case 'v': - if (opts & PF_OPT_VERBOSE) - opts |= PF_OPT_VERBOSE2; - opts |= PF_OPT_VERBOSE; - break; - case 'x': - debugopt = pfctl_lookup_option(optarg, debugopt_list); - if (debugopt == NULL) { - warnx("Unknown debug level '%s'", optarg); - usage(); - } - mode = O_RDWR; - break; - case 'z': - opts |= PF_OPT_CLRRULECTRS; - mode = O_RDWR; - break; - case 'h': - /* FALLTHROUGH */ - default: - usage(); - /* NOTREACHED */ - } - } - - if (tblcmdopt != NULL) { - argc -= optind; - argv += optind; - ch = *tblcmdopt; - if (ch == 'l') { - loadopt |= PFCTL_FLAG_TABLE; - tblcmdopt = NULL; - } else - mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY; - } else if (argc != optind) { - warnx("unknown command line argument: %s ...", argv[optind]); - usage(); - /* NOTREACHED */ - } - if (loadopt == 0) - loadopt = ~0; - - if ((path = calloc(1, MAXPATHLEN)) == NULL) - errx(1, "pfctl: calloc"); - memset(anchorname, 0, sizeof(anchorname)); - if (anchoropt != NULL) { - int len = strlen(anchoropt); - - if (anchoropt[len - 1] == '*') { - if (len >= 2 && anchoropt[len - 2] == '/') - anchoropt[len - 2] = '\0'; - else - anchoropt[len - 1] = '\0'; - opts |= PF_OPT_RECURSE; - } - if (strlcpy(anchorname, anchoropt, - sizeof(anchorname)) >= sizeof(anchorname)) - errx(1, "anchor name '%s' too long", - anchoropt); - loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE; - } - - if ((opts & PF_OPT_NOACTION) == 0) { - dev = open(pf_device, mode); - if (dev == -1) - err(1, "%s", pf_device); - altqsupport = pfctl_test_altqsupport(dev, opts); - } else { - dev = open(pf_device, O_RDONLY); - if (dev >= 0) - opts |= PF_OPT_DUMMYACTION; - /* turn off options */ - opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE); - clearopt = showopt = debugopt = NULL; -#if defined(__FreeBSD__) && !defined(ENABLE_ALTQ) - altqsupport = 0; -#else - altqsupport = 1; -#endif - } - - if (opts & PF_OPT_DISABLE) - if (pfctl_disable(dev, opts)) - error = 1; - - if (showopt != NULL) { - switch (*showopt) { - case 'A': - pfctl_show_anchors(dev, opts, anchorname); - break; - case 'r': - pfctl_load_fingerprints(dev, opts); - pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, - anchorname, 0); - break; - case 'l': - pfctl_load_fingerprints(dev, opts); - pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, - anchorname, 0); - break; - case 'n': - pfctl_load_fingerprints(dev, opts); - pfctl_show_nat(dev, opts, anchorname); - break; - case 'q': - pfctl_show_altq(dev, ifaceopt, opts, - opts & PF_OPT_VERBOSE2); - break; - case 's': - pfctl_show_states(dev, ifaceopt, opts); - break; - case 'S': - pfctl_show_src_nodes(dev, opts); - break; - case 'i': - pfctl_show_status(dev, opts); - break; - case 't': - pfctl_show_timeouts(dev, opts); - break; - case 'm': - pfctl_show_limits(dev, opts); - break; - case 'a': - opts |= PF_OPT_SHOWALL; - pfctl_load_fingerprints(dev, opts); - - pfctl_show_nat(dev, opts, anchorname); - pfctl_show_rules(dev, path, opts, 0, anchorname, 0); - pfctl_show_altq(dev, ifaceopt, opts, 0); - pfctl_show_states(dev, ifaceopt, opts); - pfctl_show_src_nodes(dev, opts); - pfctl_show_status(dev, opts); - pfctl_show_rules(dev, path, opts, 1, anchorname, 0); - pfctl_show_timeouts(dev, opts); - pfctl_show_limits(dev, opts); - pfctl_show_tables(anchorname, opts); - pfctl_show_fingerprints(opts); - break; - case 'T': - pfctl_show_tables(anchorname, opts); - break; - case 'o': - pfctl_load_fingerprints(dev, opts); - pfctl_show_fingerprints(opts); - break; - case 'I': - pfctl_show_ifaces(ifaceopt, opts); - break; - } - } - - if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL) - pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING, - anchorname, 0); - - if (clearopt != NULL) { - if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) - errx(1, "anchor names beginning with '_' cannot " - "be modified from the command line"); - - switch (*clearopt) { - case 'r': - pfctl_clear_rules(dev, opts, anchorname); - break; - case 'n': - pfctl_clear_nat(dev, opts, anchorname); - break; - case 'q': - pfctl_clear_altq(dev, opts); - break; - case 's': - pfctl_clear_states(dev, ifaceopt, opts); - break; - case 'S': - pfctl_clear_src_nodes(dev, opts); - break; - case 'i': - pfctl_clear_stats(dev, opts); - break; - case 'a': - pfctl_clear_rules(dev, opts, anchorname); - pfctl_clear_nat(dev, opts, anchorname); - pfctl_clear_tables(anchorname, opts); - if (!*anchorname) { - pfctl_clear_altq(dev, opts); - pfctl_clear_states(dev, ifaceopt, opts); - pfctl_clear_src_nodes(dev, opts); - pfctl_clear_stats(dev, opts); - pfctl_clear_fingerprints(dev, opts); - pfctl_clear_interface_flags(dev, opts); - } - break; - case 'o': - pfctl_clear_fingerprints(dev, opts); - break; - case 'T': - pfctl_clear_tables(anchorname, opts); - break; - } - } - if (state_killers) { - if (!strcmp(state_kill[0], "label")) - pfctl_label_kill_states(dev, ifaceopt, opts); - else if (!strcmp(state_kill[0], "id")) - pfctl_id_kill_states(dev, ifaceopt, opts); - else - pfctl_net_kill_states(dev, ifaceopt, opts); - } - - if (src_node_killers) - pfctl_kill_src_nodes(dev, ifaceopt, opts); - - if (tblcmdopt != NULL) { - error = pfctl_command_tables(argc, argv, tableopt, - tblcmdopt, rulesopt, anchorname, opts); - rulesopt = NULL; - } - if (optiopt != NULL) { - switch (*optiopt) { - case 'n': - optimize = 0; - break; - case 'b': - optimize |= PF_OPTIMIZE_BASIC; - break; - case 'o': - case 'p': - optimize |= PF_OPTIMIZE_PROFILE; - break; - } - } - - if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) && - !anchorname[0]) - if (pfctl_clear_interface_flags(dev, opts | PF_OPT_QUIET)) - error = 1; - - if (rulesopt != NULL && !(opts & (PF_OPT_MERGE|PF_OPT_NOACTION)) && - !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION)) - if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) - error = 1; - - if (rulesopt != NULL) { - if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) - errx(1, "anchor names beginning with '_' cannot " - "be modified from the command line"); - if (pfctl_rules(dev, rulesopt, opts, optimize, - anchorname, NULL)) - error = 1; - else if (!(opts & PF_OPT_NOACTION) && - (loadopt & PFCTL_FLAG_TABLE)) - warn_namespace_collision(NULL); - } - - if (opts & PF_OPT_ENABLE) - if (pfctl_enable(dev, opts)) - error = 1; - - if (debugopt != NULL) { - switch (*debugopt) { - case 'n': - pfctl_debug(dev, PF_DEBUG_NONE, opts); - break; - case 'u': - pfctl_debug(dev, PF_DEBUG_URGENT, opts); - break; - case 'm': - pfctl_debug(dev, PF_DEBUG_MISC, opts); - break; - case 'l': - pfctl_debug(dev, PF_DEBUG_NOISY, opts); - break; - } - } - - exit(error); -} diff --git a/contrib/pf/pfctl/pfctl.h b/contrib/pf/pfctl/pfctl.h deleted file mode 100644 index 2c69bc2068b0c..0000000000000 --- a/contrib/pf/pfctl/pfctl.h +++ /dev/null @@ -1,130 +0,0 @@ -/* $OpenBSD: pfctl.h,v 1.42 2007/12/05 12:01:47 chl Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _PFCTL_H_ -#define _PFCTL_H_ - -enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING }; - -enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS, - PFRB_IFACES, PFRB_TRANS, PFRB_MAX }; -struct pfr_buffer { - int pfrb_type; /* type of content, see enum above */ - int pfrb_size; /* number of objects in buffer */ - int pfrb_msize; /* maximum number of objects in buffer */ - void *pfrb_caddr; /* malloc'ated memory area */ -}; -#define PFRB_FOREACH(var, buf) \ - for ((var) = pfr_buf_next((buf), NULL); \ - (var) != NULL; \ - (var) = pfr_buf_next((buf), (var))) - -int pfr_get_fd(void); -int pfr_clr_tables(struct pfr_table *, int *, int); -int pfr_add_tables(struct pfr_table *, int, int *, int); -int pfr_del_tables(struct pfr_table *, int, int *, int); -int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int); -int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); -int pfr_clr_tstats(struct pfr_table *, int, int *, int); -int pfr_clr_addrs(struct pfr_table *, int *, int); -int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); -int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); -int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *, - int *, int *, int *, int); -int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int); -int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int); -int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); -int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, - int *, int, int); -void pfr_buf_clear(struct pfr_buffer *); -int pfr_buf_add(struct pfr_buffer *, const void *); -void *pfr_buf_next(struct pfr_buffer *, const void *); -int pfr_buf_grow(struct pfr_buffer *, int); -int pfr_buf_load(struct pfr_buffer *, char *, int, - int (*)(struct pfr_buffer *, char *, int)); -char *pfr_strerror(int); -int pfi_get_ifaces(const char *, struct pfi_kif *, int *); -int pfi_clr_istats(const char *, int *, int); - -void pfctl_print_title(char *); -int pfctl_clear_tables(const char *, int); -int pfctl_show_tables(const char *, int); -int pfctl_command_tables(int, char *[], char *, const char *, char *, - const char *, int); -int pfctl_show_altq(int, const char *, int, int); -void warn_namespace_collision(const char *); -int pfctl_show_ifaces(const char *, int); -FILE *pfctl_fopen(const char *, const char *); - -#ifdef __FreeBSD__ -extern int altqsupport; -extern int dummynetsupport; -#define HTONL(x) (x) = htonl((__uint32_t)(x)) -#endif - -#ifndef DEFAULT_PRIORITY -#define DEFAULT_PRIORITY 1 -#endif - -#ifndef DEFAULT_QLIMIT -#define DEFAULT_QLIMIT 50 -#endif - -/* - * generalized service curve used for admission control - */ -struct segment { - LIST_ENTRY(segment) _next; - double x, y, d, m; -}; - -extern int loadopt; - -int check_commit_altq(int, int); -void pfaltq_store(struct pf_altq *); -struct pf_altq *pfaltq_lookup(const char *); -char *rate2str(double); - -void print_addr(struct pf_addr_wrap *, sa_family_t, int); -void print_host(struct pf_addr *, u_int16_t p, sa_family_t, int); -void print_seq(struct pfsync_state_peer *); -void print_state(struct pfsync_state *, int); -int unmask(struct pf_addr *, sa_family_t); - -int pfctl_cmdline_symset(char *); -int pfctl_add_trans(struct pfr_buffer *, int, const char *); -u_int32_t - pfctl_get_ticket(struct pfr_buffer *, int, const char *); -int pfctl_trans(int, struct pfr_buffer *, u_long, int); - -#endif /* _PFCTL_H_ */ diff --git a/contrib/pf/pfctl/pfctl_altq.c b/contrib/pf/pfctl/pfctl_altq.c deleted file mode 100644 index 40e11d5a9eb99..0000000000000 --- a/contrib/pf/pfctl/pfctl_altq.c +++ /dev/null @@ -1,1258 +0,0 @@ -/* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */ - -/* - * Copyright (c) 2002 - * Sony Computer Science Laboratories Inc. - * Copyright (c) 2002, 2003 Henning Brauer - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) - -TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs); -LIST_HEAD(gen_sc, segment) rtsc, lssc; - -struct pf_altq *qname_to_pfaltq(const char *, const char *); -u_int32_t qname_to_qid(const char *); - -static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *); -static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); -static int check_commit_cbq(int, int, struct pf_altq *); -static int print_cbq_opts(const struct pf_altq *); - -static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); -static int check_commit_priq(int, int, struct pf_altq *); -static int print_priq_opts(const struct pf_altq *); - -static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *); -static int check_commit_hfsc(int, int, struct pf_altq *); -static int print_hfsc_opts(const struct pf_altq *, - const struct node_queue_opt *); - -static void gsc_add_sc(struct gen_sc *, struct service_curve *); -static int is_gsc_under_sc(struct gen_sc *, - struct service_curve *); -static void gsc_destroy(struct gen_sc *); -static struct segment *gsc_getentry(struct gen_sc *, double); -static int gsc_add_seg(struct gen_sc *, double, double, double, - double); -static double sc_x2y(struct service_curve *, double); - -#ifdef __FreeBSD__ -u_int32_t getifspeed(int, char *); -#else -u_int32_t getifspeed(char *); -#endif -u_long getifmtu(char *); -int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, - u_int32_t); -u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); -void print_hfsc_sc(const char *, u_int, u_int, u_int, - const struct node_hfsc_sc *); - -void -pfaltq_store(struct pf_altq *a) -{ - struct pf_altq *altq; - - if ((altq = malloc(sizeof(*altq))) == NULL) - err(1, "malloc"); - memcpy(altq, a, sizeof(struct pf_altq)); - TAILQ_INSERT_TAIL(&altqs, altq, entries); -} - -struct pf_altq * -pfaltq_lookup(const char *ifname) -{ - struct pf_altq *altq; - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && - altq->qname[0] == 0) - return (altq); - } - return (NULL); -} - -struct pf_altq * -qname_to_pfaltq(const char *qname, const char *ifname) -{ - struct pf_altq *altq; - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && - strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) - return (altq); - } - return (NULL); -} - -u_int32_t -qname_to_qid(const char *qname) -{ - struct pf_altq *altq; - - /* - * We guarantee that same named queues on different interfaces - * have the same qid, so we do NOT need to limit matching on - * one interface! - */ - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) - return (altq->qid); - } - return (0); -} - -void -print_altq(const struct pf_altq *a, unsigned int level, - struct node_queue_bw *bw, struct node_queue_opt *qopts) -{ - if (a->qname[0] != 0) { - print_queue(a, level, bw, 1, qopts); - return; - } - -#ifdef __FreeBSD__ - if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) - printf("INACTIVE "); -#endif - - printf("altq on %s ", a->ifname); - - switch (a->scheduler) { - case ALTQT_CBQ: - if (!print_cbq_opts(a)) - printf("cbq "); - break; - case ALTQT_PRIQ: - if (!print_priq_opts(a)) - printf("priq "); - break; - case ALTQT_HFSC: - if (!print_hfsc_opts(a, qopts)) - printf("hfsc "); - break; - } - - if (bw != NULL && bw->bw_percent > 0) { - if (bw->bw_percent < 100) - printf("bandwidth %u%% ", bw->bw_percent); - } else - printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); - - if (a->qlimit != DEFAULT_QLIMIT) - printf("qlimit %u ", a->qlimit); - printf("tbrsize %u ", a->tbrsize); -} - -void -print_queue(const struct pf_altq *a, unsigned int level, - struct node_queue_bw *bw, int print_interface, - struct node_queue_opt *qopts) -{ - unsigned int i; - -#ifdef __FreeBSD__ - if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) - printf("INACTIVE "); -#endif - printf("queue "); - for (i = 0; i < level; ++i) - printf(" "); - printf("%s ", a->qname); - if (print_interface) - printf("on %s ", a->ifname); - if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { - if (bw != NULL && bw->bw_percent > 0) { - if (bw->bw_percent < 100) - printf("bandwidth %u%% ", bw->bw_percent); - } else - printf("bandwidth %s ", rate2str((double)a->bandwidth)); - } - if (a->priority != DEFAULT_PRIORITY) - printf("priority %u ", a->priority); - if (a->qlimit != DEFAULT_QLIMIT) - printf("qlimit %u ", a->qlimit); - switch (a->scheduler) { - case ALTQT_CBQ: - print_cbq_opts(a); - break; - case ALTQT_PRIQ: - print_priq_opts(a); - break; - case ALTQT_HFSC: - print_hfsc_opts(a, qopts); - break; - } -} - -/* - * eval_pfaltq computes the discipline parameters. - */ -int -eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, - struct node_queue_opt *opts) -{ - u_int rate, size, errors = 0; - - if (bw->bw_absolute > 0) - pa->ifbandwidth = bw->bw_absolute; - else -#ifdef __FreeBSD__ - if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { -#else - if ((rate = getifspeed(pa->ifname)) == 0) { -#endif - fprintf(stderr, "interface %s does not know its bandwidth, " - "please specify an absolute bandwidth\n", - pa->ifname); - errors++; - } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) - pa->ifbandwidth = rate; - - errors += eval_queue_opts(pa, opts, pa->ifbandwidth); - - /* if tbrsize is not specified, use heuristics */ - if (pa->tbrsize == 0) { - rate = pa->ifbandwidth; - if (rate <= 1 * 1000 * 1000) - size = 1; - else if (rate <= 10 * 1000 * 1000) - size = 4; - else if (rate <= 200 * 1000 * 1000) - size = 8; - else - size = 24; - size = size * getifmtu(pa->ifname); - if (size > 0xffff) - size = 0xffff; - pa->tbrsize = size; - } - return (errors); -} - -/* - * check_commit_altq does consistency check for each interface - */ -int -check_commit_altq(int dev, int opts) -{ - struct pf_altq *altq; - int error = 0; - - /* call the discipline check for each interface. */ - TAILQ_FOREACH(altq, &altqs, entries) { - if (altq->qname[0] == 0) { - switch (altq->scheduler) { - case ALTQT_CBQ: - error = check_commit_cbq(dev, opts, altq); - break; - case ALTQT_PRIQ: - error = check_commit_priq(dev, opts, altq); - break; - case ALTQT_HFSC: - error = check_commit_hfsc(dev, opts, altq); - break; - default: - break; - } - } - } - return (error); -} - -/* - * eval_pfqueue computes the queue parameters. - */ -int -eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, - struct node_queue_opt *opts) -{ - /* should be merged with expand_queue */ - struct pf_altq *if_pa, *parent, *altq; - u_int32_t bwsum; - int error = 0; - - /* find the corresponding interface and copy fields used by queues */ - if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) { - fprintf(stderr, "altq not defined on %s\n", pa->ifname); - return (1); - } - pa->scheduler = if_pa->scheduler; - pa->ifbandwidth = if_pa->ifbandwidth; - - if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { - fprintf(stderr, "queue %s already exists on interface %s\n", - pa->qname, pa->ifname); - return (1); - } - pa->qid = qname_to_qid(pa->qname); - - parent = NULL; - if (pa->parent[0] != 0) { - parent = qname_to_pfaltq(pa->parent, pa->ifname); - if (parent == NULL) { - fprintf(stderr, "parent %s not found for %s\n", - pa->parent, pa->qname); - return (1); - } - pa->parent_qid = parent->qid; - } - if (pa->qlimit == 0) - pa->qlimit = DEFAULT_QLIMIT; - - if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { - pa->bandwidth = eval_bwspec(bw, - parent == NULL ? 0 : parent->bandwidth); - - if (pa->bandwidth > pa->ifbandwidth) { - fprintf(stderr, "bandwidth for %s higher than " - "interface\n", pa->qname); - return (1); - } - /* check the sum of the child bandwidth is under parent's */ - if (parent != NULL) { - if (pa->bandwidth > parent->bandwidth) { - warnx("bandwidth for %s higher than parent", - pa->qname); - return (1); - } - bwsum = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, - IFNAMSIZ) == 0 && - altq->qname[0] != 0 && - strncmp(altq->parent, pa->parent, - PF_QNAME_SIZE) == 0) - bwsum += altq->bandwidth; - } - bwsum += pa->bandwidth; - if (bwsum > parent->bandwidth) { - warnx("the sum of the child bandwidth higher" - " than parent \"%s\"", parent->qname); - } - } - } - - if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) - return (1); - - switch (pa->scheduler) { - case ALTQT_CBQ: - error = eval_pfqueue_cbq(pf, pa); - break; - case ALTQT_PRIQ: - error = eval_pfqueue_priq(pf, pa); - break; - case ALTQT_HFSC: - error = eval_pfqueue_hfsc(pf, pa); - break; - default: - break; - } - return (error); -} - -/* - * CBQ support functions - */ -#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ -#define RM_NS_PER_SEC (1000000000) - -static int -eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa) -{ - struct cbq_opts *opts; - u_int ifmtu; - - if (pa->priority >= CBQ_MAXPRI) { - warnx("priority out of range: max %d", CBQ_MAXPRI - 1); - return (-1); - } - - ifmtu = getifmtu(pa->ifname); - opts = &pa->pq_u.cbq_opts; - - if (opts->pktsize == 0) { /* use default */ - opts->pktsize = ifmtu; - if (opts->pktsize > MCLBYTES) /* do what TCP does */ - opts->pktsize &= ~MCLBYTES; - } else if (opts->pktsize > ifmtu) - opts->pktsize = ifmtu; - if (opts->maxpktsize == 0) /* use default */ - opts->maxpktsize = ifmtu; - else if (opts->maxpktsize > ifmtu) - opts->pktsize = ifmtu; - - if (opts->pktsize > opts->maxpktsize) - opts->pktsize = opts->maxpktsize; - - if (pa->parent[0] == 0) - opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); - - cbq_compute_idletime(pf, pa); - return (0); -} - -/* - * compute ns_per_byte, maxidle, minidle, and offtime - */ -static int -cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) -{ - struct cbq_opts *opts; - double maxidle_s, maxidle, minidle; - double offtime, nsPerByte, ifnsPerByte, ptime, cptime; - double z, g, f, gton, gtom; - u_int minburst, maxburst; - - opts = &pa->pq_u.cbq_opts; - ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; - minburst = opts->minburst; - maxburst = opts->maxburst; - - if (pa->bandwidth == 0) - f = 0.0001; /* small enough? */ - else - f = ((double) pa->bandwidth / (double) pa->ifbandwidth); - - nsPerByte = ifnsPerByte / f; - ptime = (double)opts->pktsize * ifnsPerByte; - cptime = ptime * (1.0 - f) / f; - - if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { - /* - * this causes integer overflow in kernel! - * (bandwidth < 6Kbps when max_pkt_size=1500) - */ - if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) - warnx("queue bandwidth must be larger than %s", - rate2str(ifnsPerByte * (double)opts->maxpktsize / - (double)INT_MAX * (double)pa->ifbandwidth)); - fprintf(stderr, "cbq: queue %s is too slow!\n", - pa->qname); - nsPerByte = (double)(INT_MAX / opts->maxpktsize); - } - - if (maxburst == 0) { /* use default */ - if (cptime > 10.0 * 1000000) - maxburst = 4; - else - maxburst = 16; - } - if (minburst == 0) /* use default */ - minburst = 2; - if (minburst > maxburst) - minburst = maxburst; - - z = (double)(1 << RM_FILTER_GAIN); - g = (1.0 - 1.0 / z); - gton = pow(g, (double)maxburst); - gtom = pow(g, (double)(minburst-1)); - maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); - maxidle_s = (1.0 - g); - if (maxidle > maxidle_s) - maxidle = ptime * maxidle; - else - maxidle = ptime * maxidle_s; - offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); - minidle = -((double)opts->maxpktsize * (double)nsPerByte); - - /* scale parameters */ - maxidle = ((maxidle * 8.0) / nsPerByte) * - pow(2.0, (double)RM_FILTER_GAIN); - offtime = (offtime * 8.0) / nsPerByte * - pow(2.0, (double)RM_FILTER_GAIN); - minidle = ((minidle * 8.0) / nsPerByte) * - pow(2.0, (double)RM_FILTER_GAIN); - - maxidle = maxidle / 1000.0; - offtime = offtime / 1000.0; - minidle = minidle / 1000.0; - - opts->minburst = minburst; - opts->maxburst = maxburst; - opts->ns_per_byte = (u_int)nsPerByte; - opts->maxidle = (u_int)fabs(maxidle); - opts->minidle = (int)minidle; - opts->offtime = (u_int)fabs(offtime); - - return (0); -} - -static int -check_commit_cbq(int dev, int opts, struct pf_altq *pa) -{ - struct pf_altq *altq; - int root_class, default_class; - int error = 0; - - /* - * check if cbq has one root queue and one default queue - * for this interface - */ - root_class = default_class = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) - root_class++; - if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) - default_class++; - } - if (root_class != 1) { - warnx("should have one root queue on %s", pa->ifname); - error++; - } - if (default_class != 1) { - warnx("should have one default queue on %s", pa->ifname); - error++; - } - return (error); -} - -static int -print_cbq_opts(const struct pf_altq *a) -{ - const struct cbq_opts *opts; - - opts = &a->pq_u.cbq_opts; - if (opts->flags) { - printf("cbq("); - if (opts->flags & CBQCLF_RED) - printf(" red"); - if (opts->flags & CBQCLF_ECN) - printf(" ecn"); - if (opts->flags & CBQCLF_RIO) - printf(" rio"); - if (opts->flags & CBQCLF_CLEARDSCP) - printf(" cleardscp"); - if (opts->flags & CBQCLF_FLOWVALVE) - printf(" flowvalve"); - if (opts->flags & CBQCLF_BORROW) - printf(" borrow"); - if (opts->flags & CBQCLF_WRR) - printf(" wrr"); - if (opts->flags & CBQCLF_EFFICIENT) - printf(" efficient"); - if (opts->flags & CBQCLF_ROOTCLASS) - printf(" root"); - if (opts->flags & CBQCLF_DEFCLASS) - printf(" default"); - printf(" ) "); - - return (1); - } else - return (0); -} - -/* - * PRIQ support functions - */ -static int -eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) -{ - struct pf_altq *altq; - - if (pa->priority >= PRIQ_MAXPRI) { - warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); - return (-1); - } - /* the priority should be unique for the interface */ - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 && - altq->qname[0] != 0 && altq->priority == pa->priority) { - warnx("%s and %s have the same priority", - altq->qname, pa->qname); - return (-1); - } - } - - return (0); -} - -static int -check_commit_priq(int dev, int opts, struct pf_altq *pa) -{ - struct pf_altq *altq; - int default_class; - int error = 0; - - /* - * check if priq has one default class for this interface - */ - default_class = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) - default_class++; - } - if (default_class != 1) { - warnx("should have one default queue on %s", pa->ifname); - error++; - } - return (error); -} - -static int -print_priq_opts(const struct pf_altq *a) -{ - const struct priq_opts *opts; - - opts = &a->pq_u.priq_opts; - - if (opts->flags) { - printf("priq("); - if (opts->flags & PRCF_RED) - printf(" red"); - if (opts->flags & PRCF_ECN) - printf(" ecn"); - if (opts->flags & PRCF_RIO) - printf(" rio"); - if (opts->flags & PRCF_CLEARDSCP) - printf(" cleardscp"); - if (opts->flags & PRCF_DEFAULTCLASS) - printf(" default"); - printf(" ) "); - - return (1); - } else - return (0); -} - -/* - * HFSC support functions - */ -static int -eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) -{ - struct pf_altq *altq, *parent; - struct hfsc_opts *opts; - struct service_curve sc; - - opts = &pa->pq_u.hfsc_opts; - - if (pa->parent[0] == 0) { - /* root queue */ - opts->lssc_m1 = pa->ifbandwidth; - opts->lssc_m2 = pa->ifbandwidth; - opts->lssc_d = 0; - return (0); - } - - LIST_INIT(&rtsc); - LIST_INIT(&lssc); - - /* if link_share is not specified, use bandwidth */ - if (opts->lssc_m2 == 0) - opts->lssc_m2 = pa->bandwidth; - - if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || - (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || - (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { - warnx("m2 is zero for %s", pa->qname); - return (-1); - } - - if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || - (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || - (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { - warnx("m1 must be zero for convex curve: %s", pa->qname); - return (-1); - } - - /* - * admission control: - * for the real-time service curve, the sum of the service curves - * should not exceed 80% of the interface bandwidth. 20% is reserved - * not to over-commit the actual interface bandwidth. - * for the linkshare service curve, the sum of the child service - * curve should not exceed the parent service curve. - * for the upper-limit service curve, the assigned bandwidth should - * be smaller than the interface bandwidth, and the upper-limit should - * be larger than the real-time service curve when both are defined. - */ - parent = qname_to_pfaltq(pa->parent, pa->ifname); - if (parent == NULL) - errx(1, "parent %s not found for %s", pa->parent, pa->qname); - - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - - /* if the class has a real-time service curve, add it. */ - if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) { - sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1; - sc.d = altq->pq_u.hfsc_opts.rtsc_d; - sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2; - gsc_add_sc(&rtsc, &sc); - } - - if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) - continue; - - /* if the class has a linkshare service curve, add it. */ - if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) { - sc.m1 = altq->pq_u.hfsc_opts.lssc_m1; - sc.d = altq->pq_u.hfsc_opts.lssc_d; - sc.m2 = altq->pq_u.hfsc_opts.lssc_m2; - gsc_add_sc(&lssc, &sc); - } - } - - /* check the real-time service curve. reserve 20% of interface bw */ - if (opts->rtsc_m2 != 0) { - /* add this queue to the sum */ - sc.m1 = opts->rtsc_m1; - sc.d = opts->rtsc_d; - sc.m2 = opts->rtsc_m2; - gsc_add_sc(&rtsc, &sc); - /* compare the sum with 80% of the interface */ - sc.m1 = 0; - sc.d = 0; - sc.m2 = pa->ifbandwidth / 100 * 80; - if (!is_gsc_under_sc(&rtsc, &sc)) { - warnx("real-time sc exceeds 80%% of the interface " - "bandwidth (%s)", rate2str((double)sc.m2)); - goto err_ret; - } - } - - /* check the linkshare service curve. */ - if (opts->lssc_m2 != 0) { - /* add this queue to the child sum */ - sc.m1 = opts->lssc_m1; - sc.d = opts->lssc_d; - sc.m2 = opts->lssc_m2; - gsc_add_sc(&lssc, &sc); - /* compare the sum of the children with parent's sc */ - sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; - sc.d = parent->pq_u.hfsc_opts.lssc_d; - sc.m2 = parent->pq_u.hfsc_opts.lssc_m2; - if (!is_gsc_under_sc(&lssc, &sc)) { - warnx("linkshare sc exceeds parent's sc"); - goto err_ret; - } - } - - /* check the upper-limit service curve. */ - if (opts->ulsc_m2 != 0) { - if (opts->ulsc_m1 > pa->ifbandwidth || - opts->ulsc_m2 > pa->ifbandwidth) { - warnx("upper-limit larger than interface bandwidth"); - goto err_ret; - } - if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { - warnx("upper-limit sc smaller than real-time sc"); - goto err_ret; - } - } - - gsc_destroy(&rtsc); - gsc_destroy(&lssc); - - return (0); - -err_ret: - gsc_destroy(&rtsc); - gsc_destroy(&lssc); - return (-1); -} - -static int -check_commit_hfsc(int dev, int opts, struct pf_altq *pa) -{ - struct pf_altq *altq, *def = NULL; - int default_class; - int error = 0; - - /* check if hfsc has one default queue for this interface */ - default_class = 0; - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (altq->parent[0] == 0) /* dummy root */ - continue; - if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { - default_class++; - def = altq; - } - } - if (default_class != 1) { - warnx("should have one default queue on %s", pa->ifname); - return (1); - } - /* make sure the default queue is a leaf */ - TAILQ_FOREACH(altq, &altqs, entries) { - if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) - continue; - if (altq->qname[0] == 0) /* this is for interface */ - continue; - if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { - warnx("default queue is not a leaf"); - error++; - } - } - return (error); -} - -static int -print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) -{ - const struct hfsc_opts *opts; - const struct node_hfsc_sc *rtsc, *lssc, *ulsc; - - opts = &a->pq_u.hfsc_opts; - if (qopts == NULL) - rtsc = lssc = ulsc = NULL; - else { - rtsc = &qopts->data.hfsc_opts.realtime; - lssc = &qopts->data.hfsc_opts.linkshare; - ulsc = &qopts->data.hfsc_opts.upperlimit; - } - - if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || - (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || - opts->lssc_d != 0))) { - printf("hfsc("); - if (opts->flags & HFCF_RED) - printf(" red"); - if (opts->flags & HFCF_ECN) - printf(" ecn"); - if (opts->flags & HFCF_RIO) - printf(" rio"); - if (opts->flags & HFCF_CLEARDSCP) - printf(" cleardscp"); - if (opts->flags & HFCF_DEFAULTCLASS) - printf(" default"); - if (opts->rtsc_m2 != 0) - print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, - opts->rtsc_m2, rtsc); - if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || - opts->lssc_d != 0)) - print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, - opts->lssc_m2, lssc); - if (opts->ulsc_m2 != 0) - print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, - opts->ulsc_m2, ulsc); - printf(" ) "); - - return (1); - } else - return (0); -} - -/* - * admission control using generalized service curve - */ - -/* add a new service curve to a generalized service curve */ -static void -gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) -{ - if (is_sc_null(sc)) - return; - if (sc->d != 0) - gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); - gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); -} - -/* - * check whether all points of a generalized service curve have - * their y-coordinates no larger than a given two-piece linear - * service curve. - */ -static int -is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) -{ - struct segment *s, *last, *end; - double y; - - if (is_sc_null(sc)) { - if (LIST_EMPTY(gsc)) - return (1); - LIST_FOREACH(s, gsc, _next) { - if (s->m != 0) - return (0); - } - return (1); - } - /* - * gsc has a dummy entry at the end with x = INFINITY. - * loop through up to this dummy entry. - */ - end = gsc_getentry(gsc, INFINITY); - if (end == NULL) - return (1); - last = NULL; - for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { - if (s->y > sc_x2y(sc, s->x)) - return (0); - last = s; - } - /* last now holds the real last segment */ - if (last == NULL) - return (1); - if (last->m > sc->m2) - return (0); - if (last->x < sc->d && last->m > sc->m1) { - y = last->y + (sc->d - last->x) * last->m; - if (y > sc_x2y(sc, sc->d)) - return (0); - } - return (1); -} - -static void -gsc_destroy(struct gen_sc *gsc) -{ - struct segment *s; - - while ((s = LIST_FIRST(gsc)) != NULL) { - LIST_REMOVE(s, _next); - free(s); - } -} - -/* - * return a segment entry starting at x. - * if gsc has no entry starting at x, a new entry is created at x. - */ -static struct segment * -gsc_getentry(struct gen_sc *gsc, double x) -{ - struct segment *new, *prev, *s; - - prev = NULL; - LIST_FOREACH(s, gsc, _next) { - if (s->x == x) - return (s); /* matching entry found */ - else if (s->x < x) - prev = s; - else - break; - } - - /* we have to create a new entry */ - if ((new = calloc(1, sizeof(struct segment))) == NULL) - return (NULL); - - new->x = x; - if (x == INFINITY || s == NULL) - new->d = 0; - else if (s->x == INFINITY) - new->d = INFINITY; - else - new->d = s->x - x; - if (prev == NULL) { - /* insert the new entry at the head of the list */ - new->y = 0; - new->m = 0; - LIST_INSERT_HEAD(gsc, new, _next); - } else { - /* - * the start point intersects with the segment pointed by - * prev. divide prev into 2 segments - */ - if (x == INFINITY) { - prev->d = INFINITY; - if (prev->m == 0) - new->y = prev->y; - else - new->y = INFINITY; - } else { - prev->d = x - prev->x; - new->y = prev->d * prev->m + prev->y; - } - new->m = prev->m; - LIST_INSERT_AFTER(prev, new, _next); - } - return (new); -} - -/* add a segment to a generalized service curve */ -static int -gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) -{ - struct segment *start, *end, *s; - double x2; - - if (d == INFINITY) - x2 = INFINITY; - else - x2 = x + d; - start = gsc_getentry(gsc, x); - end = gsc_getentry(gsc, x2); - if (start == NULL || end == NULL) - return (-1); - - for (s = start; s != end; s = LIST_NEXT(s, _next)) { - s->m += m; - s->y += y + (s->x - x) * m; - } - - end = gsc_getentry(gsc, INFINITY); - for (; s != end; s = LIST_NEXT(s, _next)) { - s->y += m * d; - } - - return (0); -} - -/* get y-projection of a service curve */ -static double -sc_x2y(struct service_curve *sc, double x) -{ - double y; - - if (x <= (double)sc->d) - /* y belongs to the 1st segment */ - y = x * (double)sc->m1; - else - /* y belongs to the 2nd segment */ - y = (double)sc->d * (double)sc->m1 - + (x - (double)sc->d) * (double)sc->m2; - return (y); -} - -/* - * misc utilities - */ -#define R2S_BUFS 8 -#define RATESTR_MAX 16 - -char * -rate2str(double rate) -{ - char *buf; - static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ - static int idx = 0; - int i; - static const char unit[] = " KMG"; - - buf = r2sbuf[idx++]; - if (idx == R2S_BUFS) - idx = 0; - - for (i = 0; rate >= 1000 && i <= 3; i++) - rate /= 1000; - - if ((int)(rate * 100) % 100) - snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); - else - snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); - - return (buf); -} - -#ifdef __FreeBSD__ -/* - * XXX - * FreeBSD does not have SIOCGIFDATA. - * To emulate this, DIOCGIFSPEED ioctl added to pf. - */ -u_int32_t -getifspeed(int pfdev, char *ifname) -{ - struct pf_ifspeed io; - - bzero(&io, sizeof io); - if (strlcpy(io.ifname, ifname, IFNAMSIZ) >= - sizeof(io.ifname)) - errx(1, "getifspeed: strlcpy"); - if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) - err(1, "DIOCGIFSPEED"); - return ((u_int32_t)io.baudrate); -} -#else -u_int32_t -getifspeed(char *ifname) -{ - int s; - struct ifreq ifr; - struct if_data ifrdat; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - err(1, "socket"); - bzero(&ifr, sizeof(ifr)); - if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= - sizeof(ifr.ifr_name)) - errx(1, "getifspeed: strlcpy"); - ifr.ifr_data = (caddr_t)&ifrdat; - if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) - err(1, "SIOCGIFDATA"); - if (close(s)) - err(1, "close"); - return ((u_int32_t)ifrdat.ifi_baudrate); -} -#endif - -u_long -getifmtu(char *ifname) -{ - int s; - struct ifreq ifr; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - err(1, "socket"); - bzero(&ifr, sizeof(ifr)); - if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= - sizeof(ifr.ifr_name)) - errx(1, "getifmtu: strlcpy"); - if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) -#ifdef __FreeBSD__ - ifr.ifr_mtu = 1500; -#else - err(1, "SIOCGIFMTU"); -#endif - if (close(s)) - err(1, "close"); - if (ifr.ifr_mtu > 0) - return (ifr.ifr_mtu); - else { - warnx("could not get mtu for %s, assuming 1500", ifname); - return (1500); - } -} - -int -eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, - u_int32_t ref_bw) -{ - int errors = 0; - - switch (pa->scheduler) { - case ALTQT_CBQ: - pa->pq_u.cbq_opts = opts->data.cbq_opts; - break; - case ALTQT_PRIQ: - pa->pq_u.priq_opts = opts->data.priq_opts; - break; - case ALTQT_HFSC: - pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; - if (opts->data.hfsc_opts.linkshare.used) { - pa->pq_u.hfsc_opts.lssc_m1 = - eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, - ref_bw); - pa->pq_u.hfsc_opts.lssc_m2 = - eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, - ref_bw); - pa->pq_u.hfsc_opts.lssc_d = - opts->data.hfsc_opts.linkshare.d; - } - if (opts->data.hfsc_opts.realtime.used) { - pa->pq_u.hfsc_opts.rtsc_m1 = - eval_bwspec(&opts->data.hfsc_opts.realtime.m1, - ref_bw); - pa->pq_u.hfsc_opts.rtsc_m2 = - eval_bwspec(&opts->data.hfsc_opts.realtime.m2, - ref_bw); - pa->pq_u.hfsc_opts.rtsc_d = - opts->data.hfsc_opts.realtime.d; - } - if (opts->data.hfsc_opts.upperlimit.used) { - pa->pq_u.hfsc_opts.ulsc_m1 = - eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, - ref_bw); - pa->pq_u.hfsc_opts.ulsc_m2 = - eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, - ref_bw); - pa->pq_u.hfsc_opts.ulsc_d = - opts->data.hfsc_opts.upperlimit.d; - } - break; - default: - warnx("eval_queue_opts: unknown scheduler type %u", - opts->qtype); - errors++; - break; - } - - return (errors); -} - -u_int32_t -eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) -{ - if (bw->bw_absolute > 0) - return (bw->bw_absolute); - - if (bw->bw_percent > 0) - return (ref_bw / 100 * bw->bw_percent); - - return (0); -} - -void -print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, - const struct node_hfsc_sc *sc) -{ - printf(" %s", scname); - - if (d != 0) { - printf("("); - if (sc != NULL && sc->m1.bw_percent > 0) - printf("%u%%", sc->m1.bw_percent); - else - printf("%s", rate2str((double)m1)); - printf(" %u", d); - } - - if (sc != NULL && sc->m2.bw_percent > 0) - printf(" %u%%", sc->m2.bw_percent); - else - printf(" %s", rate2str((double)m2)); - - if (d != 0) - printf(")"); -} diff --git a/contrib/pf/pfctl/pfctl_optimize.c b/contrib/pf/pfctl/pfctl_optimize.c deleted file mode 100644 index 9511720ed67dd..0000000000000 --- a/contrib/pf/pfctl/pfctl_optimize.c +++ /dev/null @@ -1,1655 +0,0 @@ -/* $OpenBSD: pfctl_optimize.c,v 1.17 2008/05/06 03:45:21 mpf Exp $ */ - -/* - * Copyright (c) 2004 Mike Frantzen - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -/* The size at which a table becomes faster than individual rules */ -#define TABLE_THRESHOLD 6 - - -/* #define OPT_DEBUG 1 */ -#ifdef OPT_DEBUG -# define DEBUG(str, v...) \ - printf("%s: " str "\n", __FUNCTION__ , ## v) -#else -# define DEBUG(str, v...) ((void)0) -#endif - - -/* - * A container that lets us sort a superblock to optimize the skip step jumps - */ -struct pf_skip_step { - int ps_count; /* number of items */ - TAILQ_HEAD( , pf_opt_rule) ps_rules; - TAILQ_ENTRY(pf_skip_step) ps_entry; -}; - - -/* - * A superblock is a block of adjacent rules of similar action. If there - * are five PASS rules in a row, they all become members of a superblock. - * Once we have a superblock, we are free to re-order any rules within it - * in order to improve performance; if a packet is passed, it doesn't matter - * who passed it. - */ -struct superblock { - TAILQ_HEAD( , pf_opt_rule) sb_rules; - TAILQ_ENTRY(superblock) sb_entry; - struct superblock *sb_profiled_block; - TAILQ_HEAD(skiplist, pf_skip_step) sb_skipsteps[PF_SKIP_COUNT]; -}; -TAILQ_HEAD(superblocks, superblock); - - -/* - * Description of the PF rule structure. - */ -enum { - BARRIER, /* the presence of the field puts the rule in it's own block */ - BREAK, /* the field may not differ between rules in a superblock */ - NOMERGE, /* the field may not differ between rules when combined */ - COMBINED, /* the field may itself be combined with other rules */ - DC, /* we just don't care about the field */ - NEVER}; /* we should never see this field set?!? */ -struct pf_rule_field { - const char *prf_name; - int prf_type; - size_t prf_offset; - size_t prf_size; -} pf_rule_desc[] = { -#define PF_RULE_FIELD(field, ty) \ - {#field, \ - ty, \ - offsetof(struct pf_rule, field), \ - sizeof(((struct pf_rule *)0)->field)} - - - /* - * The presence of these fields in a rule put the rule in it's own - * superblock. Thus it will not be optimized. It also prevents the - * rule from being re-ordered at all. - */ - PF_RULE_FIELD(label, BARRIER), - PF_RULE_FIELD(prob, BARRIER), - PF_RULE_FIELD(max_states, BARRIER), - PF_RULE_FIELD(max_src_nodes, BARRIER), - PF_RULE_FIELD(max_src_states, BARRIER), - PF_RULE_FIELD(max_src_conn, BARRIER), - PF_RULE_FIELD(max_src_conn_rate, BARRIER), - PF_RULE_FIELD(anchor, BARRIER), /* for now */ - - /* - * These fields must be the same between all rules in the same superblock. - * These rules are allowed to be re-ordered but only among like rules. - * For instance we can re-order all 'tag "foo"' rules because they have the - * same tag. But we can not re-order between a 'tag "foo"' and a - * 'tag "bar"' since that would change the meaning of the ruleset. - */ - PF_RULE_FIELD(tagname, BREAK), - PF_RULE_FIELD(keep_state, BREAK), - PF_RULE_FIELD(qname, BREAK), - PF_RULE_FIELD(pqname, BREAK), - PF_RULE_FIELD(rt, BREAK), - PF_RULE_FIELD(allow_opts, BREAK), - PF_RULE_FIELD(rule_flag, BREAK), - PF_RULE_FIELD(action, BREAK), - PF_RULE_FIELD(log, BREAK), - PF_RULE_FIELD(quick, BREAK), - PF_RULE_FIELD(return_ttl, BREAK), - PF_RULE_FIELD(overload_tblname, BREAK), - PF_RULE_FIELD(flush, BREAK), - PF_RULE_FIELD(rpool, BREAK), - PF_RULE_FIELD(logif, BREAK), - - /* - * Any fields not listed in this structure act as BREAK fields - */ - - - /* - * These fields must not differ when we merge two rules together but - * their difference isn't enough to put the rules in different superblocks. - * There are no problems re-ordering any rules with these fields. - */ - PF_RULE_FIELD(af, NOMERGE), - PF_RULE_FIELD(ifnot, NOMERGE), - PF_RULE_FIELD(ifname, NOMERGE), /* hack for IF groups */ - PF_RULE_FIELD(match_tag_not, NOMERGE), - PF_RULE_FIELD(match_tagname, NOMERGE), - PF_RULE_FIELD(os_fingerprint, NOMERGE), - PF_RULE_FIELD(timeout, NOMERGE), - PF_RULE_FIELD(return_icmp, NOMERGE), - PF_RULE_FIELD(return_icmp6, NOMERGE), - PF_RULE_FIELD(uid, NOMERGE), - PF_RULE_FIELD(gid, NOMERGE), - PF_RULE_FIELD(direction, NOMERGE), - PF_RULE_FIELD(proto, NOMERGE), - PF_RULE_FIELD(type, NOMERGE), - PF_RULE_FIELD(code, NOMERGE), - PF_RULE_FIELD(flags, NOMERGE), - PF_RULE_FIELD(flagset, NOMERGE), - PF_RULE_FIELD(tos, NOMERGE), - PF_RULE_FIELD(src.port, NOMERGE), - PF_RULE_FIELD(dst.port, NOMERGE), - PF_RULE_FIELD(src.port_op, NOMERGE), - PF_RULE_FIELD(dst.port_op, NOMERGE), - PF_RULE_FIELD(src.neg, NOMERGE), - PF_RULE_FIELD(dst.neg, NOMERGE), - - /* These fields can be merged */ - PF_RULE_FIELD(src.addr, COMBINED), - PF_RULE_FIELD(dst.addr, COMBINED), - - /* We just don't care about these fields. They're set by the kernel */ - PF_RULE_FIELD(skip, DC), - PF_RULE_FIELD(evaluations, DC), - PF_RULE_FIELD(packets, DC), - PF_RULE_FIELD(bytes, DC), - PF_RULE_FIELD(kif, DC), - PF_RULE_FIELD(states_cur, DC), - PF_RULE_FIELD(states_tot, DC), - PF_RULE_FIELD(src_nodes, DC), - PF_RULE_FIELD(nr, DC), - PF_RULE_FIELD(entries, DC), - PF_RULE_FIELD(qid, DC), - PF_RULE_FIELD(pqid, DC), - PF_RULE_FIELD(anchor_relative, DC), - PF_RULE_FIELD(anchor_wildcard, DC), - PF_RULE_FIELD(tag, DC), - PF_RULE_FIELD(match_tag, DC), - PF_RULE_FIELD(overload_tbl, DC), - - /* These fields should never be set in a PASS/BLOCK rule */ - PF_RULE_FIELD(natpass, NEVER), - PF_RULE_FIELD(max_mss, NEVER), - PF_RULE_FIELD(min_ttl, NEVER), - PF_RULE_FIELD(set_tos, NEVER), -}; - - - -int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t, - struct pf_rule_addr *); -int addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *); -int addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *); -int block_feedback(struct pfctl *, struct superblock *); -int combine_rules(struct pfctl *, struct superblock *); -void comparable_rule(struct pf_rule *, const struct pf_rule *, int); -int construct_superblocks(struct pfctl *, struct pf_opt_queue *, - struct superblocks *); -void exclude_supersets(struct pf_rule *, struct pf_rule *); -int interface_group(const char *); -int load_feedback_profile(struct pfctl *, struct superblocks *); -int optimize_superblock(struct pfctl *, struct superblock *); -int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *); -void remove_from_skipsteps(struct skiplist *, struct superblock *, - struct pf_opt_rule *, struct pf_skip_step *); -int remove_identical_rules(struct pfctl *, struct superblock *); -int reorder_rules(struct pfctl *, struct superblock *, int); -int rules_combineable(struct pf_rule *, struct pf_rule *); -void skip_append(struct superblock *, int, struct pf_skip_step *, - struct pf_opt_rule *); -int skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *); -void skip_init(void); -int skip_cmp_af(struct pf_rule *, struct pf_rule *); -int skip_cmp_dir(struct pf_rule *, struct pf_rule *); -int skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *); -int skip_cmp_dst_port(struct pf_rule *, struct pf_rule *); -int skip_cmp_ifp(struct pf_rule *, struct pf_rule *); -int skip_cmp_proto(struct pf_rule *, struct pf_rule *); -int skip_cmp_src_addr(struct pf_rule *, struct pf_rule *); -int skip_cmp_src_port(struct pf_rule *, struct pf_rule *); -int superblock_inclusive(struct superblock *, struct pf_opt_rule *); -void superblock_free(struct pfctl *, struct superblock *); - - -int (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *); -const char *skip_comparitors_names[PF_SKIP_COUNT]; -#define PF_SKIP_COMPARITORS { \ - { "ifp", PF_SKIP_IFP, skip_cmp_ifp }, \ - { "dir", PF_SKIP_DIR, skip_cmp_dir }, \ - { "af", PF_SKIP_AF, skip_cmp_af }, \ - { "proto", PF_SKIP_PROTO, skip_cmp_proto }, \ - { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \ - { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \ - { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \ - { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \ -} - -struct pfr_buffer table_buffer; -int table_identifier; - - -int -pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs) -{ - struct superblocks superblocks; - struct pf_opt_queue opt_queue; - struct superblock *block; - struct pf_opt_rule *por; - struct pf_rule *r; - struct pf_rulequeue *old_rules; - - DEBUG("optimizing ruleset"); - memset(&table_buffer, 0, sizeof(table_buffer)); - skip_init(); - TAILQ_INIT(&opt_queue); - - old_rules = rs->rules[PF_RULESET_FILTER].active.ptr; - rs->rules[PF_RULESET_FILTER].active.ptr = - rs->rules[PF_RULESET_FILTER].inactive.ptr; - rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules; - - /* - * XXX expanding the pf_opt_rule format throughout pfctl might allow - * us to avoid all this copying. - */ - while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr)) - != NULL) { - TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r, - entries); - if ((por = calloc(1, sizeof(*por))) == NULL) - err(1, "calloc"); - memcpy(&por->por_rule, r, sizeof(*r)); - if (TAILQ_FIRST(&r->rpool.list) != NULL) { - TAILQ_INIT(&por->por_rule.rpool.list); - pfctl_move_pool(&r->rpool, &por->por_rule.rpool); - } else - bzero(&por->por_rule.rpool, - sizeof(por->por_rule.rpool)); - - - TAILQ_INSERT_TAIL(&opt_queue, por, por_entry); - } - - TAILQ_INIT(&superblocks); - if (construct_superblocks(pf, &opt_queue, &superblocks)) - goto error; - - if (pf->optimize & PF_OPTIMIZE_PROFILE) { - if (load_feedback_profile(pf, &superblocks)) - goto error; - } - - TAILQ_FOREACH(block, &superblocks, sb_entry) { - if (optimize_superblock(pf, block)) - goto error; - } - - rs->anchor->refcnt = 0; - while ((block = TAILQ_FIRST(&superblocks))) { - TAILQ_REMOVE(&superblocks, block, sb_entry); - - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - por->por_rule.nr = rs->anchor->refcnt++; - if ((r = calloc(1, sizeof(*r))) == NULL) - err(1, "calloc"); - memcpy(r, &por->por_rule, sizeof(*r)); - TAILQ_INIT(&r->rpool.list); - pfctl_move_pool(&por->por_rule.rpool, &r->rpool); - TAILQ_INSERT_TAIL( - rs->rules[PF_RULESET_FILTER].active.ptr, - r, entries); - free(por); - } - free(block); - } - - return (0); - -error: - while ((por = TAILQ_FIRST(&opt_queue))) { - TAILQ_REMOVE(&opt_queue, por, por_entry); - if (por->por_src_tbl) { - pfr_buf_clear(por->por_src_tbl->pt_buf); - free(por->por_src_tbl->pt_buf); - free(por->por_src_tbl); - } - if (por->por_dst_tbl) { - pfr_buf_clear(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl); - } - free(por); - } - while ((block = TAILQ_FIRST(&superblocks))) { - TAILQ_REMOVE(&superblocks, block, sb_entry); - superblock_free(pf, block); - } - return (1); -} - - -/* - * Go ahead and optimize a superblock - */ -int -optimize_superblock(struct pfctl *pf, struct superblock *block) -{ -#ifdef OPT_DEBUG - struct pf_opt_rule *por; -#endif /* OPT_DEBUG */ - - /* We have a few optimization passes: - * 1) remove duplicate rules or rules that are a subset of other - * rules - * 2) combine otherwise identical rules with different IP addresses - * into a single rule and put the addresses in a table. - * 3) re-order the rules to improve kernel skip steps - * 4) re-order the 'quick' rules based on feedback from the - * active ruleset statistics - * - * XXX combine_rules() doesn't combine v4 and v6 rules. would just - * have to keep af in the table container, make af 'COMBINE' and - * twiddle the af on the merged rule - * XXX maybe add a weighting to the metric on skipsteps when doing - * reordering. sometimes two sequential tables will be better - * that four consecutive interfaces. - * XXX need to adjust the skipstep count of everything after PROTO, - * since they aren't actually checked on a proto mismatch in - * pf_test_{tcp, udp, icmp}() - * XXX should i treat proto=0, af=0 or dir=0 special in skepstep - * calculation since they are a DC? - * XXX keep last skiplist of last superblock to influence this - * superblock. '5 inet6 log' should make '3 inet6' come before '4 - * inet' in the next superblock. - * XXX would be useful to add tables for ports - * XXX we can also re-order some mutually exclusive superblocks to - * try merging superblocks before any of these optimization passes. - * for instance a single 'log in' rule in the middle of non-logging - * out rules. - */ - - /* shortcut. there will be a lot of 1-rule superblocks */ - if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry)) - return (0); - -#ifdef OPT_DEBUG - printf("--- Superblock ---\n"); - TAILQ_FOREACH(por, &block->sb_rules, por_entry) { - printf(" "); - print_rule(&por->por_rule, por->por_rule.anchor ? - por->por_rule.anchor->name : "", 1, 0); - } -#endif /* OPT_DEBUG */ - - - if (remove_identical_rules(pf, block)) - return (1); - if (combine_rules(pf, block)) - return (1); - if ((pf->optimize & PF_OPTIMIZE_PROFILE) && - TAILQ_FIRST(&block->sb_rules)->por_rule.quick && - block->sb_profiled_block) { - if (block_feedback(pf, block)) - return (1); - } else if (reorder_rules(pf, block, 0)) { - return (1); - } - - /* - * Don't add any optimization passes below reorder_rules(). It will - * have divided superblocks into smaller blocks for further refinement - * and doesn't put them back together again. What once was a true - * superblock might have been split into multiple superblocks. - */ - -#ifdef OPT_DEBUG - printf("--- END Superblock ---\n"); -#endif /* OPT_DEBUG */ - return (0); -} - - -/* - * Optimization pass #1: remove identical rules - */ -int -remove_identical_rules(struct pfctl *pf, struct superblock *block) -{ - struct pf_opt_rule *por1, *por2, *por_next, *por2_next; - struct pf_rule a, a2, b, b2; - - for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) { - por_next = TAILQ_NEXT(por1, por_entry); - for (por2 = por_next; por2; por2 = por2_next) { - por2_next = TAILQ_NEXT(por2, por_entry); - comparable_rule(&a, &por1->por_rule, DC); - comparable_rule(&b, &por2->por_rule, DC); - memcpy(&a2, &a, sizeof(a2)); - memcpy(&b2, &b, sizeof(b2)); - - exclude_supersets(&a, &b); - exclude_supersets(&b2, &a2); - if (memcmp(&a, &b, sizeof(a)) == 0) { - DEBUG("removing identical rule nr%d = *nr%d*", - por1->por_rule.nr, por2->por_rule.nr); - TAILQ_REMOVE(&block->sb_rules, por2, por_entry); - if (por_next == por2) - por_next = TAILQ_NEXT(por1, por_entry); - free(por2); - } else if (memcmp(&a2, &b2, sizeof(a2)) == 0) { - DEBUG("removing identical rule *nr%d* = nr%d", - por1->por_rule.nr, por2->por_rule.nr); - TAILQ_REMOVE(&block->sb_rules, por1, por_entry); - free(por1); - break; - } - } - } - - return (0); -} - - -/* - * Optimization pass #2: combine similar rules with different addresses - * into a single rule and a table - */ -int -combine_rules(struct pfctl *pf, struct superblock *block) -{ - struct pf_opt_rule *p1, *p2, *por_next; - int src_eq, dst_eq; - - if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) { - warnx("Must enable table loading for optimizations"); - return (1); - } - - /* First we make a pass to combine the rules. O(n log n) */ - TAILQ_FOREACH(p1, &block->sb_rules, por_entry) { - for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) { - por_next = TAILQ_NEXT(p2, por_entry); - - src_eq = addrs_equal(&p1->por_rule.src, - &p2->por_rule.src); - dst_eq = addrs_equal(&p1->por_rule.dst, - &p2->por_rule.dst); - - if (src_eq && !dst_eq && p1->por_src_tbl == NULL && - p2->por_dst_tbl == NULL && - p2->por_src_tbl == NULL && - rules_combineable(&p1->por_rule, &p2->por_rule) && - addrs_combineable(&p1->por_rule.dst, - &p2->por_rule.dst)) { - DEBUG("can combine rules nr%d = nr%d", - p1->por_rule.nr, p2->por_rule.nr); - if (p1->por_dst_tbl == NULL && - add_opt_table(pf, &p1->por_dst_tbl, - p1->por_rule.af, &p1->por_rule.dst)) - return (1); - if (add_opt_table(pf, &p1->por_dst_tbl, - p1->por_rule.af, &p2->por_rule.dst)) - return (1); - p2->por_dst_tbl = p1->por_dst_tbl; - if (p1->por_dst_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - TAILQ_REMOVE(&block->sb_rules, p2, - por_entry); - free(p2); - } - } else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL - && p2->por_src_tbl == NULL && - p2->por_dst_tbl == NULL && - rules_combineable(&p1->por_rule, &p2->por_rule) && - addrs_combineable(&p1->por_rule.src, - &p2->por_rule.src)) { - DEBUG("can combine rules nr%d = nr%d", - p1->por_rule.nr, p2->por_rule.nr); - if (p1->por_src_tbl == NULL && - add_opt_table(pf, &p1->por_src_tbl, - p1->por_rule.af, &p1->por_rule.src)) - return (1); - if (add_opt_table(pf, &p1->por_src_tbl, - p1->por_rule.af, &p2->por_rule.src)) - return (1); - p2->por_src_tbl = p1->por_src_tbl; - if (p1->por_src_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - TAILQ_REMOVE(&block->sb_rules, p2, - por_entry); - free(p2); - } - } - } - } - - - /* - * Then we make a final pass to create a valid table name and - * insert the name into the rules. - */ - for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) { - por_next = TAILQ_NEXT(p1, por_entry); - assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL); - - if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - if (p1->por_src_tbl->pt_generated) { - /* This rule is included in a table */ - TAILQ_REMOVE(&block->sb_rules, p1, por_entry); - free(p1); - continue; - } - p1->por_src_tbl->pt_generated = 1; - - if ((pf->opts & PF_OPT_NOACTION) == 0 && - pf_opt_create_table(pf, p1->por_src_tbl)) - return (1); - - pf->tdirty = 1; - - if (pf->opts & PF_OPT_VERBOSE) - print_tabledef(p1->por_src_tbl->pt_name, - PFR_TFLAG_CONST, 1, - &p1->por_src_tbl->pt_nodes); - - memset(&p1->por_rule.src.addr, 0, - sizeof(p1->por_rule.src.addr)); - p1->por_rule.src.addr.type = PF_ADDR_TABLE; - strlcpy(p1->por_rule.src.addr.v.tblname, - p1->por_src_tbl->pt_name, - sizeof(p1->por_rule.src.addr.v.tblname)); - - pfr_buf_clear(p1->por_src_tbl->pt_buf); - free(p1->por_src_tbl->pt_buf); - p1->por_src_tbl->pt_buf = NULL; - } - if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >= - TABLE_THRESHOLD) { - if (p1->por_dst_tbl->pt_generated) { - /* This rule is included in a table */ - TAILQ_REMOVE(&block->sb_rules, p1, por_entry); - free(p1); - continue; - } - p1->por_dst_tbl->pt_generated = 1; - - if ((pf->opts & PF_OPT_NOACTION) == 0 && - pf_opt_create_table(pf, p1->por_dst_tbl)) - return (1); - pf->tdirty = 1; - - if (pf->opts & PF_OPT_VERBOSE) - print_tabledef(p1->por_dst_tbl->pt_name, - PFR_TFLAG_CONST, 1, - &p1->por_dst_tbl->pt_nodes); - - memset(&p1->por_rule.dst.addr, 0, - sizeof(p1->por_rule.dst.addr)); - p1->por_rule.dst.addr.type = PF_ADDR_TABLE; - strlcpy(p1->por_rule.dst.addr.v.tblname, - p1->por_dst_tbl->pt_name, - sizeof(p1->por_rule.dst.addr.v.tblname)); - - pfr_buf_clear(p1->por_dst_tbl->pt_buf); - free(p1->por_dst_tbl->pt_buf); - p1->por_dst_tbl->pt_buf = NULL; - } - } - - return (0); -} - - -/* - * Optimization pass #3: re-order rules to improve skip steps - */ -int -reorder_rules(struct pfctl *pf, struct superblock *block, int depth) -{ - struct superblock *newblock; - struct pf_skip_step *skiplist; - struct pf_opt_rule *por; - int i, largest, largest_list, rule_count = 0; - TAILQ_HEAD( , pf_opt_rule) head; - - /* - * Calculate the best-case skip steps. We put each rule in a list - * of other rules with common fields - */ - for (i = 0; i < PF_SKIP_COUNT; i++) { - TAILQ_FOREACH(por, &block->sb_rules, por_entry) { - TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i], - ps_entry) { - if (skip_compare(i, skiplist, por) == 0) - break; - } - if (skiplist == NULL) { - if ((skiplist = calloc(1, sizeof(*skiplist))) == - NULL) - err(1, "calloc"); - TAILQ_INIT(&skiplist->ps_rules); - TAILQ_INSERT_TAIL(&block->sb_skipsteps[i], - skiplist, ps_entry); - } - skip_append(block, i, skiplist, por); - } - } - - TAILQ_FOREACH(por, &block->sb_rules, por_entry) - rule_count++; - - /* - * Now we're going to ignore any fields that are identical between - * all of the rules in the superblock and those fields which differ - * between every rule in the superblock. - */ - largest = 0; - for (i = 0; i < PF_SKIP_COUNT; i++) { - skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]); - if (skiplist->ps_count == rule_count) { - DEBUG("(%d) original skipstep '%s' is all rules", - depth, skip_comparitors_names[i]); - skiplist->ps_count = 0; - } else if (skiplist->ps_count == 1) { - skiplist->ps_count = 0; - } else { - DEBUG("(%d) original skipstep '%s' largest jump is %d", - depth, skip_comparitors_names[i], - skiplist->ps_count); - if (skiplist->ps_count > largest) - largest = skiplist->ps_count; - } - } - if (largest == 0) { - /* Ugh. There is NO commonality in the superblock on which - * optimize the skipsteps optimization. - */ - goto done; - } - - /* - * Now we're going to empty the superblock rule list and re-create - * it based on a more optimal skipstep order. - */ - TAILQ_INIT(&head); - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - TAILQ_INSERT_TAIL(&head, por, por_entry); - } - - - while (!TAILQ_EMPTY(&head)) { - largest = 1; - - /* - * Find the most useful skip steps remaining - */ - for (i = 0; i < PF_SKIP_COUNT; i++) { - skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]); - if (skiplist->ps_count > largest) { - largest = skiplist->ps_count; - largest_list = i; - } - } - - if (largest <= 1) { - /* - * Nothing useful left. Leave remaining rules in order. - */ - DEBUG("(%d) no more commonality for skip steps", depth); - while ((por = TAILQ_FIRST(&head))) { - TAILQ_REMOVE(&head, por, por_entry); - TAILQ_INSERT_TAIL(&block->sb_rules, por, - por_entry); - } - } else { - /* - * There is commonality. Extract those common rules - * and place them in the ruleset adjacent to each - * other. - */ - skiplist = TAILQ_FIRST(&block->sb_skipsteps[ - largest_list]); - DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d", - depth, skip_comparitors_names[largest_list], - largest, TAILQ_FIRST(&TAILQ_FIRST(&block-> - sb_skipsteps [largest_list])->ps_rules)-> - por_rule.nr); - TAILQ_REMOVE(&block->sb_skipsteps[largest_list], - skiplist, ps_entry); - - - /* - * There may be further commonality inside these - * rules. So we'll split them off into they're own - * superblock and pass it back into the optimizer. - */ - if (skiplist->ps_count > 2) { - if ((newblock = calloc(1, sizeof(*newblock))) - == NULL) { - warn("calloc"); - return (1); - } - TAILQ_INIT(&newblock->sb_rules); - for (i = 0; i < PF_SKIP_COUNT; i++) - TAILQ_INIT(&newblock->sb_skipsteps[i]); - TAILQ_INSERT_BEFORE(block, newblock, sb_entry); - DEBUG("(%d) splitting off %d rules from superblock @ #%d", - depth, skiplist->ps_count, - TAILQ_FIRST(&skiplist->ps_rules)-> - por_rule.nr); - } else { - newblock = block; - } - - while ((por = TAILQ_FIRST(&skiplist->ps_rules))) { - TAILQ_REMOVE(&head, por, por_entry); - TAILQ_REMOVE(&skiplist->ps_rules, por, - por_skip_entry[largest_list]); - TAILQ_INSERT_TAIL(&newblock->sb_rules, por, - por_entry); - - /* Remove this rule from all other skiplists */ - remove_from_skipsteps(&block->sb_skipsteps[ - largest_list], block, por, skiplist); - } - free(skiplist); - if (newblock != block) - if (reorder_rules(pf, newblock, depth + 1)) - return (1); - } - } - -done: - for (i = 0; i < PF_SKIP_COUNT; i++) { - while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) { - TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist, - ps_entry); - free(skiplist); - } - } - - return (0); -} - - -/* - * Optimization pass #4: re-order 'quick' rules based on feedback from the - * currently running ruleset - */ -int -block_feedback(struct pfctl *pf, struct superblock *block) -{ - TAILQ_HEAD( , pf_opt_rule) queue; - struct pf_opt_rule *por1, *por2; - u_int64_t total_count = 0; - struct pf_rule a, b; - - - /* - * Walk through all of the profiled superblock's rules and copy - * the counters onto our rules. - */ - TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) { - comparable_rule(&a, &por1->por_rule, DC); - total_count += por1->por_rule.packets[0] + - por1->por_rule.packets[1]; - TAILQ_FOREACH(por2, &block->sb_rules, por_entry) { - if (por2->por_profile_count) - continue; - comparable_rule(&b, &por2->por_rule, DC); - if (memcmp(&a, &b, sizeof(a)) == 0) { - por2->por_profile_count = - por1->por_rule.packets[0] + - por1->por_rule.packets[1]; - break; - } - } - } - superblock_free(pf, block->sb_profiled_block); - block->sb_profiled_block = NULL; - - /* - * Now we pull all of the rules off the superblock and re-insert them - * in sorted order. - */ - - TAILQ_INIT(&queue); - while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) { - TAILQ_REMOVE(&block->sb_rules, por1, por_entry); - TAILQ_INSERT_TAIL(&queue, por1, por_entry); - } - - while ((por1 = TAILQ_FIRST(&queue)) != NULL) { - TAILQ_REMOVE(&queue, por1, por_entry); -/* XXX I should sort all of the unused rules based on skip steps */ - TAILQ_FOREACH(por2, &block->sb_rules, por_entry) { - if (por1->por_profile_count > por2->por_profile_count) { - TAILQ_INSERT_BEFORE(por2, por1, por_entry); - break; - } - } -#ifdef __FreeBSD__ - if (por2 == NULL) -#else - if (por2 == TAILQ_END(&block->sb_rules)) -#endif - TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry); - } - - return (0); -} - - -/* - * Load the current ruleset from the kernel and try to associate them with - * the ruleset we're optimizing. - */ -int -load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) -{ - struct superblock *block, *blockcur; - struct superblocks prof_superblocks; - struct pf_opt_rule *por; - struct pf_opt_queue queue; - struct pfioc_rule pr; - struct pf_rule a, b; - int nr, mnr; - - TAILQ_INIT(&queue); - TAILQ_INIT(&prof_superblocks); - - memset(&pr, 0, sizeof(pr)); - pr.rule.action = PF_PASS; - if (ioctl(pf->dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); - return (1); - } - mnr = pr.nr; - - DEBUG("Loading %d active rules for a feedback profile", mnr); - for (nr = 0; nr < mnr; ++nr) { - struct pf_ruleset *rs; - if ((por = calloc(1, sizeof(*por))) == NULL) { - warn("calloc"); - return (1); - } - pr.nr = nr; - if (ioctl(pf->dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULES"); - return (1); - } - memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule)); - rs = pf_find_or_create_ruleset(pr.anchor_call); - por->por_rule.anchor = rs->anchor; - if (TAILQ_EMPTY(&por->por_rule.rpool.list)) - memset(&por->por_rule.rpool, 0, - sizeof(por->por_rule.rpool)); - TAILQ_INSERT_TAIL(&queue, por, por_entry); - - /* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket, - * PF_PASS, pf->anchor) ??? - * ... pfctl_clear_pool(&pr.rule.rpool) - */ - } - - if (construct_superblocks(pf, &queue, &prof_superblocks)) - return (1); - - - /* - * Now we try to associate the active ruleset's superblocks with - * the superblocks we're compiling. - */ - block = TAILQ_FIRST(superblocks); - blockcur = TAILQ_FIRST(&prof_superblocks); - while (block && blockcur) { - comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, - BREAK); - comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule, - BREAK); - if (memcmp(&a, &b, sizeof(a)) == 0) { - /* The two superblocks lined up */ - block->sb_profiled_block = blockcur; - } else { - DEBUG("superblocks don't line up between #%d and #%d", - TAILQ_FIRST(&block->sb_rules)->por_rule.nr, - TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr); - break; - } - block = TAILQ_NEXT(block, sb_entry); - blockcur = TAILQ_NEXT(blockcur, sb_entry); - } - - - - /* Free any superblocks we couldn't link */ - while (blockcur) { - block = TAILQ_NEXT(blockcur, sb_entry); - superblock_free(pf, blockcur); - blockcur = block; - } - return (0); -} - - -/* - * Compare a rule to a skiplist to see if the rule is a member - */ -int -skip_compare(int skipnum, struct pf_skip_step *skiplist, - struct pf_opt_rule *por) -{ - struct pf_rule *a, *b; - if (skipnum >= PF_SKIP_COUNT || skipnum < 0) - errx(1, "skip_compare() out of bounds"); - a = &por->por_rule; - b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule; - - return ((skip_comparitors[skipnum])(a, b)); -} - - -/* - * Add a rule to a skiplist - */ -void -skip_append(struct superblock *superblock, int skipnum, - struct pf_skip_step *skiplist, struct pf_opt_rule *por) -{ - struct pf_skip_step *prev; - - skiplist->ps_count++; - TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]); - - /* Keep the list of skiplists sorted by whichever is larger */ - while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) && - prev->ps_count < skiplist->ps_count) { - TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum], - skiplist, ps_entry); - TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry); - } -} - - -/* - * Remove a rule from the other skiplist calculations. - */ -void -remove_from_skipsteps(struct skiplist *head, struct superblock *block, - struct pf_opt_rule *por, struct pf_skip_step *active_list) -{ - struct pf_skip_step *sk, *next; - struct pf_opt_rule *p2; - int i, found; - - for (i = 0; i < PF_SKIP_COUNT; i++) { - sk = TAILQ_FIRST(&block->sb_skipsteps[i]); - if (sk == NULL || sk == active_list || sk->ps_count <= 1) - continue; - found = 0; - do { - TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i]) - if (p2 == por) { - TAILQ_REMOVE(&sk->ps_rules, p2, - por_skip_entry[i]); - found = 1; - sk->ps_count--; - break; - } - } while (!found && (sk = TAILQ_NEXT(sk, ps_entry))); - if (found && sk) { - /* Does this change the sorting order? */ - while ((next = TAILQ_NEXT(sk, ps_entry)) && - next->ps_count > sk->ps_count) { - TAILQ_REMOVE(head, sk, ps_entry); - TAILQ_INSERT_AFTER(head, next, sk, ps_entry); - } -#ifdef OPT_DEBUG - next = TAILQ_NEXT(sk, ps_entry); - assert(next == NULL || next->ps_count <= sk->ps_count); -#endif /* OPT_DEBUG */ - } - } -} - - -/* Compare two rules AF field for skiplist construction */ -int -skip_cmp_af(struct pf_rule *a, struct pf_rule *b) -{ - if (a->af != b->af || a->af == 0) - return (1); - return (0); -} - -/* Compare two rules DIRECTION field for skiplist construction */ -int -skip_cmp_dir(struct pf_rule *a, struct pf_rule *b) -{ - if (a->direction == 0 || a->direction != b->direction) - return (1); - return (0); -} - -/* Compare two rules DST Address field for skiplist construction */ -int -skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b) -{ - if (a->dst.neg != b->dst.neg || - a->dst.addr.type != b->dst.addr.type) - return (1); - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - switch (a->dst.addr.type) { - case PF_ADDR_ADDRMASK: - if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr, - sizeof(a->dst.addr.v.a.addr)) || - memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask, - sizeof(a->dst.addr.v.a.mask)) || - (a->dst.addr.v.a.addr.addr32[0] == 0 && - a->dst.addr.v.a.addr.addr32[1] == 0 && - a->dst.addr.v.a.addr.addr32[2] == 0 && - a->dst.addr.v.a.addr.addr32[3] == 0)) - return (1); - return (0); - case PF_ADDR_DYNIFTL: - if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 || - a->dst.addr.iflags != a->dst.addr.iflags || - memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask, - sizeof(a->dst.addr.v.a.mask))) - return (1); - return (0); - case PF_ADDR_NOROUTE: - case PF_ADDR_URPFFAILED: - return (0); - case PF_ADDR_TABLE: - return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname)); - } - return (1); -} - -/* Compare two rules DST port field for skiplist construction */ -int -skip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b) -{ - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op || - a->dst.port[0] != b->dst.port[0] || - a->dst.port[1] != b->dst.port[1]) - return (1); - return (0); -} - -/* Compare two rules IFP field for skiplist construction */ -int -skip_cmp_ifp(struct pf_rule *a, struct pf_rule *b) -{ - if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0') - return (1); - return (a->ifnot != b->ifnot); -} - -/* Compare two rules PROTO field for skiplist construction */ -int -skip_cmp_proto(struct pf_rule *a, struct pf_rule *b) -{ - return (a->proto != b->proto || a->proto == 0); -} - -/* Compare two rules SRC addr field for skiplist construction */ -int -skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b) -{ - if (a->src.neg != b->src.neg || - a->src.addr.type != b->src.addr.type) - return (1); - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - switch (a->src.addr.type) { - case PF_ADDR_ADDRMASK: - if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr, - sizeof(a->src.addr.v.a.addr)) || - memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask, - sizeof(a->src.addr.v.a.mask)) || - (a->src.addr.v.a.addr.addr32[0] == 0 && - a->src.addr.v.a.addr.addr32[1] == 0 && - a->src.addr.v.a.addr.addr32[2] == 0 && - a->src.addr.v.a.addr.addr32[3] == 0)) - return (1); - return (0); - case PF_ADDR_DYNIFTL: - if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 || - a->src.addr.iflags != a->src.addr.iflags || - memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask, - sizeof(a->src.addr.v.a.mask))) - return (1); - return (0); - case PF_ADDR_NOROUTE: - case PF_ADDR_URPFFAILED: - return (0); - case PF_ADDR_TABLE: - return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname)); - } - return (1); -} - -/* Compare two rules SRC port field for skiplist construction */ -int -skip_cmp_src_port(struct pf_rule *a, struct pf_rule *b) -{ - if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op || - a->src.port[0] != b->src.port[0] || - a->src.port[1] != b->src.port[1]) - return (1); - /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 - * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || - * a->proto == IPPROTO_ICMP - * return (1); - */ - return (0); -} - - -void -skip_init(void) -{ - struct { - char *name; - int skipnum; - int (*func)(struct pf_rule *, struct pf_rule *); - } comps[] = PF_SKIP_COMPARITORS; - int skipnum, i; - - for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) { - for (i = 0; i < sizeof(comps)/sizeof(*comps); i++) - if (comps[i].skipnum == skipnum) { - skip_comparitors[skipnum] = comps[i].func; - skip_comparitors_names[skipnum] = comps[i].name; - } - } - for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) - if (skip_comparitors[skipnum] == NULL) - errx(1, "Need to add skip step comparitor to pfctl?!"); -} - -/* - * Add a host/netmask to a table - */ -int -add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af, - struct pf_rule_addr *addr) -{ -#ifdef OPT_DEBUG - char buf[128]; -#endif /* OPT_DEBUG */ - static int tablenum = 0; - struct node_host node_host; - - if (*tbl == NULL) { - if ((*tbl = calloc(1, sizeof(**tbl))) == NULL || - ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) == - NULL) - err(1, "calloc"); - (*tbl)->pt_buf->pfrb_type = PFRB_ADDRS; - SIMPLEQ_INIT(&(*tbl)->pt_nodes); - - /* This is just a temporary table name */ - snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d", - PF_OPT_TABLE_PREFIX, tablenum++); - DEBUG("creating table <%s>", (*tbl)->pt_name); - } - - memset(&node_host, 0, sizeof(node_host)); - node_host.af = af; - node_host.addr = addr->addr; - -#ifdef OPT_DEBUG - DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af, - &node_host.addr.v.a.addr, buf, sizeof(buf)), - unmask(&node_host.addr.v.a.mask, af)); -#endif /* OPT_DEBUG */ - - if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) { - warn("failed to add host"); - return (1); - } - if (pf->opts & PF_OPT_VERBOSE) { - struct node_tinit *ti; - - if ((ti = calloc(1, sizeof(*ti))) == NULL) - err(1, "malloc"); - if ((ti->host = malloc(sizeof(*ti->host))) == NULL) - err(1, "malloc"); - memcpy(ti->host, &node_host, sizeof(*ti->host)); - SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries); - } - - (*tbl)->pt_rulecount++; - if ((*tbl)->pt_rulecount == TABLE_THRESHOLD) - DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name); - - return (0); -} - - -/* - * Do the dirty work of choosing an unused table name and creating it. - * (be careful with the table name, it might already be used in another anchor) - */ -int -pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl) -{ - static int tablenum; - struct pfr_table *t; - - if (table_buffer.pfrb_type == 0) { - /* Initialize the list of tables */ - table_buffer.pfrb_type = PFRB_TABLES; - for (;;) { - pfr_buf_grow(&table_buffer, table_buffer.pfrb_size); - table_buffer.pfrb_size = table_buffer.pfrb_msize; - if (pfr_get_tables(NULL, table_buffer.pfrb_caddr, - &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS)) - err(1, "pfr_get_tables"); - if (table_buffer.pfrb_size <= table_buffer.pfrb_msize) - break; - } - table_identifier = arc4random(); - } - - /* XXX would be *really* nice to avoid duplicating identical tables */ - - /* Now we have to pick a table name that isn't used */ -again: - DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name, - PF_OPT_TABLE_PREFIX, table_identifier, tablenum); - snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d", - PF_OPT_TABLE_PREFIX, table_identifier, tablenum); - PFRB_FOREACH(t, &table_buffer) { - if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) { - /* Collision. Try again */ - DEBUG("wow, table <%s> in use. trying again", - tbl->pt_name); - table_identifier = arc4random(); - goto again; - } - } - tablenum++; - - - if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, - pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) { - warn("failed to create table %s in %s", - tbl->pt_name, pf->astack[0]->name); - return (1); - } - return (0); -} - -/* - * Partition the flat ruleset into a list of distinct superblocks - */ -int -construct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue, - struct superblocks *superblocks) -{ - struct superblock *block = NULL; - struct pf_opt_rule *por; - int i; - - while (!TAILQ_EMPTY(opt_queue)) { - por = TAILQ_FIRST(opt_queue); - TAILQ_REMOVE(opt_queue, por, por_entry); - if (block == NULL || !superblock_inclusive(block, por)) { - if ((block = calloc(1, sizeof(*block))) == NULL) { - warn("calloc"); - return (1); - } - TAILQ_INIT(&block->sb_rules); - for (i = 0; i < PF_SKIP_COUNT; i++) - TAILQ_INIT(&block->sb_skipsteps[i]); - TAILQ_INSERT_TAIL(superblocks, block, sb_entry); - } - TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry); - } - - return (0); -} - - -/* - * Compare two rule addresses - */ -int -addrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b) -{ - if (a->neg != b->neg) - return (0); - return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0); -} - - -/* - * The addresses are not equal, but can we combine them into one table? - */ -int -addrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b) -{ - if (a->addr.type != PF_ADDR_ADDRMASK || - b->addr.type != PF_ADDR_ADDRMASK) - return (0); - if (a->neg != b->neg || a->port_op != b->port_op || - a->port[0] != b->port[0] || a->port[1] != b->port[1]) - return (0); - return (1); -} - - -/* - * Are we allowed to combine these two rules - */ -int -rules_combineable(struct pf_rule *p1, struct pf_rule *p2) -{ - struct pf_rule a, b; - - comparable_rule(&a, p1, COMBINED); - comparable_rule(&b, p2, COMBINED); - return (memcmp(&a, &b, sizeof(a)) == 0); -} - - -/* - * Can a rule be included inside a superblock - */ -int -superblock_inclusive(struct superblock *block, struct pf_opt_rule *por) -{ - struct pf_rule a, b; - int i, j; - - /* First check for hard breaks */ - for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) { - if (pf_rule_desc[i].prf_type == BARRIER) { - for (j = 0; j < pf_rule_desc[i].prf_size; j++) - if (((char *)&por->por_rule)[j + - pf_rule_desc[i].prf_offset] != 0) - return (0); - } - } - - /* per-rule src-track is also a hard break */ - if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK) - return (0); - - /* - * Have to handle interface groups separately. Consider the following - * rules: - * block on EXTIFS to any port 22 - * pass on em0 to any port 22 - * (where EXTIFS is an arbitrary interface group) - * The optimizer may decide to re-order the pass rule in front of the - * block rule. But what if EXTIFS includes em0??? Such a reordering - * would change the meaning of the ruleset. - * We can't just lookup the EXTIFS group and check if em0 is a member - * because the user is allowed to add interfaces to a group during - * runtime. - * Ergo interface groups become a defacto superblock break :-( - */ - if (interface_group(por->por_rule.ifname) || - interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) { - if (strcasecmp(por->por_rule.ifname, - TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0) - return (0); - } - - comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE); - comparable_rule(&b, &por->por_rule, NOMERGE); - if (memcmp(&a, &b, sizeof(a)) == 0) - return (1); - -#ifdef OPT_DEBUG - for (i = 0; i < sizeof(por->por_rule); i++) { - int closest = -1; - if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) { - for (j = 0; j < sizeof(pf_rule_desc) / - sizeof(*pf_rule_desc); j++) { - if (i >= pf_rule_desc[j].prf_offset && - i < pf_rule_desc[j].prf_offset + - pf_rule_desc[j].prf_size) { - DEBUG("superblock break @ %d due to %s", - por->por_rule.nr, - pf_rule_desc[j].prf_name); - return (0); - } - if (i > pf_rule_desc[j].prf_offset) { - if (closest == -1 || - i-pf_rule_desc[j].prf_offset < - i-pf_rule_desc[closest].prf_offset) - closest = j; - } - } - - if (closest >= 0) - DEBUG("superblock break @ %d on %s+%xh", - por->por_rule.nr, - pf_rule_desc[closest].prf_name, - i - pf_rule_desc[closest].prf_offset - - pf_rule_desc[closest].prf_size); - else - DEBUG("superblock break @ %d on field @ %d", - por->por_rule.nr, i); - return (0); - } - } -#endif /* OPT_DEBUG */ - - return (0); -} - - -/* - * Figure out if an interface name is an actual interface or actually a - * group of interfaces. - */ -int -interface_group(const char *ifname) -{ - if (ifname == NULL || !ifname[0]) - return (0); - - /* Real interfaces must end in a number, interface groups do not */ - if (isdigit(ifname[strlen(ifname) - 1])) - return (0); - else - return (1); -} - - -/* - * Make a rule that can directly compared by memcmp() - */ -void -comparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type) -{ - int i; - /* - * To simplify the comparison, we just zero out the fields that are - * allowed to be different and then do a simple memcmp() - */ - memcpy(dst, src, sizeof(*dst)); - for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) - if (pf_rule_desc[i].prf_type >= type) { -#ifdef OPT_DEBUG - assert(pf_rule_desc[i].prf_type != NEVER || - *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0); -#endif /* OPT_DEBUG */ - memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0, - pf_rule_desc[i].prf_size); - } -} - - -/* - * Remove superset information from two rules so we can directly compare them - * with memcmp() - */ -void -exclude_supersets(struct pf_rule *super, struct pf_rule *sub) -{ - if (super->ifname[0] == '\0') - memset(sub->ifname, 0, sizeof(sub->ifname)); - if (super->direction == PF_INOUT) - sub->direction = PF_INOUT; - if ((super->proto == 0 || super->proto == sub->proto) && - super->flags == 0 && super->flagset == 0 && (sub->flags || - sub->flagset)) { - sub->flags = super->flags; - sub->flagset = super->flagset; - } - if (super->proto == 0) - sub->proto = 0; - - if (super->src.port_op == 0) { - sub->src.port_op = 0; - sub->src.port[0] = 0; - sub->src.port[1] = 0; - } - if (super->dst.port_op == 0) { - sub->dst.port_op = 0; - sub->dst.port[0] = 0; - sub->dst.port[1] = 0; - } - - if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg && - !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 && - super->src.addr.v.a.mask.addr32[1] == 0 && - super->src.addr.v.a.mask.addr32[2] == 0 && - super->src.addr.v.a.mask.addr32[3] == 0) - memset(&sub->src.addr, 0, sizeof(sub->src.addr)); - else if (super->src.addr.type == PF_ADDR_ADDRMASK && - sub->src.addr.type == PF_ADDR_ADDRMASK && - super->src.neg == sub->src.neg && - super->af == sub->af && - unmask(&super->src.addr.v.a.mask, super->af) < - unmask(&sub->src.addr.v.a.mask, sub->af) && - super->src.addr.v.a.addr.addr32[0] == - (sub->src.addr.v.a.addr.addr32[0] & - super->src.addr.v.a.mask.addr32[0]) && - super->src.addr.v.a.addr.addr32[1] == - (sub->src.addr.v.a.addr.addr32[1] & - super->src.addr.v.a.mask.addr32[1]) && - super->src.addr.v.a.addr.addr32[2] == - (sub->src.addr.v.a.addr.addr32[2] & - super->src.addr.v.a.mask.addr32[2]) && - super->src.addr.v.a.addr.addr32[3] == - (sub->src.addr.v.a.addr.addr32[3] & - super->src.addr.v.a.mask.addr32[3])) { - /* sub->src.addr is a subset of super->src.addr/mask */ - memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr)); - } - - if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg && - !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 && - super->dst.addr.v.a.mask.addr32[1] == 0 && - super->dst.addr.v.a.mask.addr32[2] == 0 && - super->dst.addr.v.a.mask.addr32[3] == 0) - memset(&sub->dst.addr, 0, sizeof(sub->dst.addr)); - else if (super->dst.addr.type == PF_ADDR_ADDRMASK && - sub->dst.addr.type == PF_ADDR_ADDRMASK && - super->dst.neg == sub->dst.neg && - super->af == sub->af && - unmask(&super->dst.addr.v.a.mask, super->af) < - unmask(&sub->dst.addr.v.a.mask, sub->af) && - super->dst.addr.v.a.addr.addr32[0] == - (sub->dst.addr.v.a.addr.addr32[0] & - super->dst.addr.v.a.mask.addr32[0]) && - super->dst.addr.v.a.addr.addr32[1] == - (sub->dst.addr.v.a.addr.addr32[1] & - super->dst.addr.v.a.mask.addr32[1]) && - super->dst.addr.v.a.addr.addr32[2] == - (sub->dst.addr.v.a.addr.addr32[2] & - super->dst.addr.v.a.mask.addr32[2]) && - super->dst.addr.v.a.addr.addr32[3] == - (sub->dst.addr.v.a.addr.addr32[3] & - super->dst.addr.v.a.mask.addr32[3])) { - /* sub->dst.addr is a subset of super->dst.addr/mask */ - memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr)); - } - - if (super->af == 0) - sub->af = 0; -} - - -void -superblock_free(struct pfctl *pf, struct superblock *block) -{ - struct pf_opt_rule *por; - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - if (por->por_src_tbl) { - if (por->por_src_tbl->pt_buf) { - pfr_buf_clear(por->por_src_tbl->pt_buf); - free(por->por_src_tbl->pt_buf); - } - free(por->por_src_tbl); - } - if (por->por_dst_tbl) { - if (por->por_dst_tbl->pt_buf) { - pfr_buf_clear(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl->pt_buf); - } - free(por->por_dst_tbl); - } - free(por); - } - if (block->sb_profiled_block) - superblock_free(pf, block->sb_profiled_block); - free(block); -} - diff --git a/contrib/pf/pfctl/pfctl_osfp.c b/contrib/pf/pfctl/pfctl_osfp.c deleted file mode 100644 index df789811ddb5e..0000000000000 --- a/contrib/pf/pfctl/pfctl_osfp.c +++ /dev/null @@ -1,1108 +0,0 @@ -/* $OpenBSD: pfctl_osfp.c,v 1.14 2006/04/08 02:13:14 ray Exp $ */ - -/* - * Copyright (c) 2003 Mike Frantzen - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -#ifndef MIN -# define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif /* MIN */ -#ifndef MAX -# define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif /* MAX */ - - -#if 0 -# define DEBUG(fp, str, v...) \ - fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \ - (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v); -#else -# define DEBUG(fp, str, v...) ((void)0) -#endif - - -struct name_entry; -LIST_HEAD(name_list, name_entry); -struct name_entry { - LIST_ENTRY(name_entry) nm_entry; - int nm_num; - char nm_name[PF_OSFP_LEN]; - - struct name_list nm_sublist; - int nm_sublist_num; -}; -struct name_list classes = LIST_HEAD_INITIALIZER(&classes); -int class_count; -int fingerprint_count; - -void add_fingerprint(int, int, struct pf_osfp_ioctl *); -struct name_entry *fingerprint_name_entry(struct name_list *, char *); -void pfctl_flush_my_fingerprints(struct name_list *); -char *get_field(char **, size_t *, int *); -int get_int(char **, size_t *, int *, int *, const char *, - int, int, const char *, int); -int get_str(char **, size_t *, char **, const char *, int, - const char *, int); -int get_tcpopts(const char *, int, const char *, - pf_tcpopts_t *, int *, int *, int *, int *, int *, - int *); -void import_fingerprint(struct pf_osfp_ioctl *); -const char *print_ioctl(struct pf_osfp_ioctl *); -void print_name_list(int, struct name_list *, const char *); -void sort_name_list(int, struct name_list *); -struct name_entry *lookup_name_list(struct name_list *, const char *); - -/* Load fingerprints from a file */ -int -pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) -{ - FILE *in; - char *line; - size_t len; - int i, lineno = 0; - int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale, - wscale_mod, optcnt, ts0; - pf_tcpopts_t packed_tcpopts; - char *class, *version, *subtype, *desc, *tcpopts; - struct pf_osfp_ioctl fp; - - pfctl_flush_my_fingerprints(&classes); - - if ((in = pfctl_fopen(fp_filename, "r")) == NULL) { - warn("%s", fp_filename); - return (1); - } - class = version = subtype = desc = tcpopts = NULL; - - if ((opts & PF_OPT_NOACTION) == 0) - pfctl_clear_fingerprints(dev, opts); - - while ((line = fgetln(in, &len)) != NULL) { - lineno++; - if (class) - free(class); - if (version) - free(version); - if (subtype) - free(subtype); - if (desc) - free(desc); - if (tcpopts) - free(tcpopts); - class = version = subtype = desc = tcpopts = NULL; - memset(&fp, 0, sizeof(fp)); - - /* Chop off comment */ - for (i = 0; i < len; i++) - if (line[i] == '#') { - len = i; - break; - } - /* Chop off whitespace */ - while (len > 0 && isspace(line[len - 1])) - len--; - while (len > 0 && isspace(line[0])) { - len--; - line++; - } - if (len == 0) - continue; - -#define T_DC 0x01 /* Allow don't care */ -#define T_MSS 0x02 /* Allow MSS multiple */ -#define T_MTU 0x04 /* Allow MTU multiple */ -#define T_MOD 0x08 /* Allow modulus */ - -#define GET_INT(v, mod, n, ty, mx) \ - get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno) -#define GET_STR(v, n, mn) \ - get_str(&line, &len, &v, n, mn, fp_filename, lineno) - - if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU| - T_MOD, 0xffff) || - GET_INT(ttl, NULL, "ttl", 0, 0xff) || - GET_INT(df, NULL, "don't fragment frag", 0, 1) || - GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC, - 8192) || - GET_STR(tcpopts, "TCP Options", 1) || - GET_STR(class, "OS class", 1) || - GET_STR(version, "OS version", 0) || - GET_STR(subtype, "OS subtype", 0) || - GET_STR(desc, "OS description", 2)) - continue; - if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts, - &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0)) - continue; - if (len != 0) { - fprintf(stderr, "%s:%d excess field\n", fp_filename, - lineno); - continue; - } - - fp.fp_ttl = ttl; - if (df) - fp.fp_flags |= PF_OSFP_DF; - switch (w_mod) { - case 0: - break; - case T_DC: - fp.fp_flags |= PF_OSFP_WSIZE_DC; - break; - case T_MSS: - fp.fp_flags |= PF_OSFP_WSIZE_MSS; - break; - case T_MTU: - fp.fp_flags |= PF_OSFP_WSIZE_MTU; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_WSIZE_MOD; - break; - } - fp.fp_wsize = window; - - switch (p_mod) { - case T_DC: - fp.fp_flags |= PF_OSFP_PSIZE_DC; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_PSIZE_MOD; - } - fp.fp_psize = psize; - - - switch (wscale_mod) { - case T_DC: - fp.fp_flags |= PF_OSFP_WSCALE_DC; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_WSCALE_MOD; - } - fp.fp_wscale = wscale; - - switch (mss_mod) { - case T_DC: - fp.fp_flags |= PF_OSFP_MSS_DC; - break; - case T_MOD: - fp.fp_flags |= PF_OSFP_MSS_MOD; - break; - } - fp.fp_mss = mss; - - fp.fp_tcpopts = packed_tcpopts; - fp.fp_optcnt = optcnt; - if (ts0) - fp.fp_flags |= PF_OSFP_TS0; - - if (class[0] == '@') - fp.fp_os.fp_enflags |= PF_OSFP_GENERIC; - if (class[0] == '*') - fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL; - - if (class[0] == '@' || class[0] == '*') - strlcpy(fp.fp_os.fp_class_nm, class + 1, - sizeof(fp.fp_os.fp_class_nm)); - else - strlcpy(fp.fp_os.fp_class_nm, class, - sizeof(fp.fp_os.fp_class_nm)); - strlcpy(fp.fp_os.fp_version_nm, version, - sizeof(fp.fp_os.fp_version_nm)); - strlcpy(fp.fp_os.fp_subtype_nm, subtype, - sizeof(fp.fp_os.fp_subtype_nm)); - - add_fingerprint(dev, opts, &fp); - - fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6); - fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip); - add_fingerprint(dev, opts, &fp); - } - - if (class) - free(class); - if (version) - free(version); - if (subtype) - free(subtype); - if (desc) - free(desc); - if (tcpopts) - free(tcpopts); - - fclose(in); - - if (opts & PF_OPT_VERBOSE2) - printf("Loaded %d passive OS fingerprints\n", - fingerprint_count); - return (0); -} - -/* flush the kernel's fingerprints */ -void -pfctl_clear_fingerprints(int dev, int opts) -{ - if (ioctl(dev, DIOCOSFPFLUSH)) - err(1, "DIOCOSFPFLUSH"); -} - -/* flush pfctl's view of the fingerprints */ -void -pfctl_flush_my_fingerprints(struct name_list *list) -{ - struct name_entry *nm; - - while ((nm = LIST_FIRST(list)) != NULL) { - LIST_REMOVE(nm, nm_entry); - pfctl_flush_my_fingerprints(&nm->nm_sublist); - free(nm); - } - fingerprint_count = 0; - class_count = 0; -} - -/* Fetch the active fingerprints from the kernel */ -int -pfctl_load_fingerprints(int dev, int opts) -{ - struct pf_osfp_ioctl io; - int i; - - pfctl_flush_my_fingerprints(&classes); - - for (i = 0; i >= 0; i++) { - memset(&io, 0, sizeof(io)); - io.fp_getnum = i; - if (ioctl(dev, DIOCOSFPGET, &io)) { - if (errno == EBUSY) - break; - warn("DIOCOSFPGET"); - return (1); - } - import_fingerprint(&io); - } - return (0); -} - -/* List the fingerprints */ -void -pfctl_show_fingerprints(int opts) -{ - if (LIST_FIRST(&classes) != NULL) { - if (opts & PF_OPT_SHOWALL) { - pfctl_print_title("OS FINGERPRINTS:"); - printf("%u fingerprints loaded\n", fingerprint_count); - } else { - printf("Class\tVersion\tSubtype(subversion)\n"); - printf("-----\t-------\t-------------------\n"); - sort_name_list(opts, &classes); - print_name_list(opts, &classes, ""); - } - } -} - -/* Lookup a fingerprint */ -pf_osfp_t -pfctl_get_fingerprint(const char *name) -{ - struct name_entry *nm, *class_nm, *version_nm, *subtype_nm; - pf_osfp_t ret = PF_OSFP_NOMATCH; - int class, version, subtype; - int unp_class, unp_version, unp_subtype; - int wr_len, version_len, subtype_len; - char *ptr, *wr_name; - - if (strcasecmp(name, "unknown") == 0) - return (PF_OSFP_UNKNOWN); - - /* Try most likely no version and no subtype */ - if ((nm = lookup_name_list(&classes, name))) { - class = nm->nm_num; - version = PF_OSFP_ANY; - subtype = PF_OSFP_ANY; - goto found; - } else { - - /* Chop it up into class/version/subtype */ - - if ((wr_name = strdup(name)) == NULL) - err(1, "malloc"); - if ((ptr = strchr(wr_name, ' ')) == NULL) { - free(wr_name); - return (PF_OSFP_NOMATCH); - } - *ptr++ = '\0'; - - /* The class is easy to find since it is delimited by a space */ - if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) { - free(wr_name); - return (PF_OSFP_NOMATCH); - } - class = class_nm->nm_num; - - /* Try no subtype */ - if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr))) - { - version = version_nm->nm_num; - subtype = PF_OSFP_ANY; - free(wr_name); - goto found; - } - - - /* - * There must be a version and a subtype. - * We'll do some fuzzy matching to pick up things like: - * Linux 2.2.14 (version=2.2 subtype=14) - * FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE) - * Windows 2000 SP2 (version=2000 subtype=SP2) - */ -#define CONNECTOR(x) ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-') - wr_len = strlen(ptr); - LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) { - version_len = strlen(version_nm->nm_name); - if (wr_len < version_len + 2 || - !CONNECTOR(ptr[version_len])) - continue; - /* first part of the string must be version */ - if (strncasecmp(ptr, version_nm->nm_name, - version_len)) - continue; - - LIST_FOREACH(subtype_nm, &version_nm->nm_sublist, - nm_entry) { - subtype_len = strlen(subtype_nm->nm_name); - if (wr_len != version_len + subtype_len + 1) - continue; - - /* last part of the string must be subtype */ - if (strcasecmp(&ptr[version_len+1], - subtype_nm->nm_name) != 0) - continue; - - /* Found it!! */ - version = version_nm->nm_num; - subtype = subtype_nm->nm_num; - free(wr_name); - goto found; - } - } - - free(wr_name); - return (PF_OSFP_NOMATCH); - } - -found: - PF_OSFP_PACK(ret, class, version, subtype); - if (ret != PF_OSFP_NOMATCH) { - PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype); - if (class != unp_class) { - fprintf(stderr, "warning: fingerprint table overflowed " - "classes\n"); - return (PF_OSFP_NOMATCH); - } - if (version != unp_version) { - fprintf(stderr, "warning: fingerprint table overflowed " - "versions\n"); - return (PF_OSFP_NOMATCH); - } - if (subtype != unp_subtype) { - fprintf(stderr, "warning: fingerprint table overflowed " - "subtypes\n"); - return (PF_OSFP_NOMATCH); - } - } - if (ret == PF_OSFP_ANY) { - /* should never happen */ - fprintf(stderr, "warning: fingerprint packed to 'any'\n"); - return (PF_OSFP_NOMATCH); - } - - return (ret); -} - -/* Lookup a fingerprint name by ID */ -char * -pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len) -{ - int class, version, subtype; - struct name_list *list; - struct name_entry *nm; - - char *class_name, *version_name, *subtype_name; - class_name = version_name = subtype_name = NULL; - - if (fp == PF_OSFP_UNKNOWN) { - strlcpy(buf, "unknown", len); - return (buf); - } - if (fp == PF_OSFP_ANY) { - strlcpy(buf, "any", len); - return (buf); - } - - PF_OSFP_UNPACK(fp, class, version, subtype); - if (class >= (1 << _FP_CLASS_BITS) || - version >= (1 << _FP_VERSION_BITS) || - subtype >= (1 << _FP_SUBTYPE_BITS)) { - warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp); - strlcpy(buf, "nomatch", len); - return (buf); - } - - LIST_FOREACH(nm, &classes, nm_entry) { - if (nm->nm_num == class) { - class_name = nm->nm_name; - if (version == PF_OSFP_ANY) - goto found; - list = &nm->nm_sublist; - LIST_FOREACH(nm, list, nm_entry) { - if (nm->nm_num == version) { - version_name = nm->nm_name; - if (subtype == PF_OSFP_ANY) - goto found; - list = &nm->nm_sublist; - LIST_FOREACH(nm, list, nm_entry) { - if (nm->nm_num == subtype) { - subtype_name = - nm->nm_name; - goto found; - } - } /* foreach subtype */ - strlcpy(buf, "nomatch", len); - return (buf); - } - } /* foreach version */ - strlcpy(buf, "nomatch", len); - return (buf); - } - } /* foreach class */ - - strlcpy(buf, "nomatch", len); - return (buf); - -found: - snprintf(buf, len, "%s", class_name); - if (version_name) { - strlcat(buf, " ", len); - strlcat(buf, version_name, len); - if (subtype_name) { - if (strchr(version_name, ' ')) - strlcat(buf, " ", len); - else if (strchr(version_name, '.') && - isdigit(*subtype_name)) - strlcat(buf, ".", len); - else - strlcat(buf, " ", len); - strlcat(buf, subtype_name, len); - } - } - return (buf); -} - -/* lookup a name in a list */ -struct name_entry * -lookup_name_list(struct name_list *list, const char *name) -{ - struct name_entry *nm; - LIST_FOREACH(nm, list, nm_entry) - if (strcasecmp(name, nm->nm_name) == 0) - return (nm); - - return (NULL); -} - - -void -add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp) -{ - struct pf_osfp_ioctl fptmp; - struct name_entry *nm_class, *nm_version, *nm_subtype; - int class, version, subtype; - -/* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */ -#define EXPAND(field) do { \ - int _dot = -1, _start = -1, _end = -1, _i = 0; \ - /* pick major version out of #.# */ \ - if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') { \ - _dot = fp->field[_i] - '0'; \ - _i += 2; \ - } \ - if (isdigit(fp->field[_i])) \ - _start = fp->field[_i++] - '0'; \ - else \ - break; \ - if (isdigit(fp->field[_i])) \ - _start = (_start * 10) + fp->field[_i++] - '0'; \ - if (fp->field[_i++] != '-') \ - break; \ - if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' && \ - fp->field[_i] - '0' == _dot) \ - _i += 2; \ - else if (_dot != -1) \ - break; \ - if (isdigit(fp->field[_i])) \ - _end = fp->field[_i++] - '0'; \ - else \ - break; \ - if (isdigit(fp->field[_i])) \ - _end = (_end * 10) + fp->field[_i++] - '0'; \ - if (isdigit(fp->field[_i])) \ - _end = (_end * 10) + fp->field[_i++] - '0'; \ - if (fp->field[_i] != '\0') \ - break; \ - memcpy(&fptmp, fp, sizeof(fptmp)); \ - for (;_start <= _end; _start++) { \ - memset(fptmp.field, 0, sizeof(fptmp.field)); \ - fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED; \ - if (_dot == -1) \ - snprintf(fptmp.field, sizeof(fptmp.field), \ - "%d", _start); \ - else \ - snprintf(fptmp.field, sizeof(fptmp.field), \ - "%d.%d", _dot, _start); \ - add_fingerprint(dev, opts, &fptmp); \ - } \ -} while(0) - - /* We allow "#-#" as a version or subtype and we'll expand it */ - EXPAND(fp_os.fp_version_nm); - EXPAND(fp_os.fp_subtype_nm); - - if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0) - errx(1, "fingerprint class \"nomatch\" is reserved"); - - version = PF_OSFP_ANY; - subtype = PF_OSFP_ANY; - - nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); - if (nm_class->nm_num == 0) - nm_class->nm_num = ++class_count; - class = nm_class->nm_num; - - nm_version = fingerprint_name_entry(&nm_class->nm_sublist, - fp->fp_os.fp_version_nm); - if (nm_version) { - if (nm_version->nm_num == 0) - nm_version->nm_num = ++nm_class->nm_sublist_num; - version = nm_version->nm_num; - nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, - fp->fp_os.fp_subtype_nm); - if (nm_subtype) { - if (nm_subtype->nm_num == 0) - nm_subtype->nm_num = - ++nm_version->nm_sublist_num; - subtype = nm_subtype->nm_num; - } - } - - - DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype, - print_ioctl(fp)); - - PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype); - fingerprint_count++; - -#ifdef FAKE_PF_KERNEL - /* Linked to the sys/net/pf_osfp.c. Call pf_osfp_add() */ - if ((errno = pf_osfp_add(fp))) -#else - if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp)) -#endif /* FAKE_PF_KERNEL */ - { - if (errno == EEXIST) { - warn("Duplicate signature for %s %s %s", - fp->fp_os.fp_class_nm, - fp->fp_os.fp_version_nm, - fp->fp_os.fp_subtype_nm); - - } else { - err(1, "DIOCOSFPADD"); - } - } -} - -/* import a fingerprint from the kernel */ -void -import_fingerprint(struct pf_osfp_ioctl *fp) -{ - struct name_entry *nm_class, *nm_version, *nm_subtype; - int class, version, subtype; - - PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype); - - nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); - if (nm_class->nm_num == 0) { - nm_class->nm_num = class; - class_count = MAX(class_count, class); - } - - nm_version = fingerprint_name_entry(&nm_class->nm_sublist, - fp->fp_os.fp_version_nm); - if (nm_version) { - if (nm_version->nm_num == 0) { - nm_version->nm_num = version; - nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num, - version); - } - nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, - fp->fp_os.fp_subtype_nm); - if (nm_subtype) { - if (nm_subtype->nm_num == 0) { - nm_subtype->nm_num = subtype; - nm_version->nm_sublist_num = - MAX(nm_version->nm_sublist_num, subtype); - } - } - } - - - fingerprint_count++; - DEBUG(fp, "import signature %d:%d:%d", class, version, subtype); -} - -/* Find an entry for a fingerprints class/version/subtype */ -struct name_entry * -fingerprint_name_entry(struct name_list *list, char *name) -{ - struct name_entry *nm_entry; - - if (name == NULL || strlen(name) == 0) - return (NULL); - - LIST_FOREACH(nm_entry, list, nm_entry) { - if (strcasecmp(nm_entry->nm_name, name) == 0) { - /* We'll move this to the front of the list later */ - LIST_REMOVE(nm_entry, nm_entry); - break; - } - } - if (nm_entry == NULL) { - nm_entry = calloc(1, sizeof(*nm_entry)); - if (nm_entry == NULL) - err(1, "calloc"); - LIST_INIT(&nm_entry->nm_sublist); - strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name)); - } - LIST_INSERT_HEAD(list, nm_entry, nm_entry); - return (nm_entry); -} - - -void -print_name_list(int opts, struct name_list *nml, const char *prefix) -{ - char newprefix[32]; - struct name_entry *nm; - - LIST_FOREACH(nm, nml, nm_entry) { - snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix, - nm->nm_name); - printf("%s\n", newprefix); - print_name_list(opts, &nm->nm_sublist, newprefix); - } -} - -void -sort_name_list(int opts, struct name_list *nml) -{ - struct name_list new; - struct name_entry *nm, *nmsearch, *nmlast; - - /* yes yes, it's a very slow sort. so sue me */ - - LIST_INIT(&new); - - while ((nm = LIST_FIRST(nml)) != NULL) { - LIST_REMOVE(nm, nm_entry); - nmlast = NULL; - LIST_FOREACH(nmsearch, &new, nm_entry) { - if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) { - LIST_INSERT_BEFORE(nmsearch, nm, nm_entry); - break; - } - nmlast = nmsearch; - } - if (nmsearch == NULL) { - if (nmlast) - LIST_INSERT_AFTER(nmlast, nm, nm_entry); - else - LIST_INSERT_HEAD(&new, nm, nm_entry); - } - - sort_name_list(opts, &nm->nm_sublist); - } - nmlast = NULL; - while ((nm = LIST_FIRST(&new)) != NULL) { - LIST_REMOVE(nm, nm_entry); - if (nmlast == NULL) - LIST_INSERT_HEAD(nml, nm, nm_entry); - else - LIST_INSERT_AFTER(nmlast, nm, nm_entry); - nmlast = nm; - } -} - -/* parse the next integer in a formatted config file line */ -int -get_int(char **line, size_t *len, int *var, int *mod, - const char *name, int flags, int max, const char *filename, int lineno) -{ - int fieldlen, i; - char *field; - long val = 0; - - if (mod) - *mod = 0; - *var = 0; - - field = get_field(line, len, &fieldlen); - if (field == NULL) - return (1); - if (fieldlen == 0) { - fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name); - return (1); - } - - i = 0; - if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*') - && fieldlen >= 1) { - switch (*field) { - case 'S': - if (mod && (flags & T_MSS)) - *mod = T_MSS; - if (fieldlen == 1) - return (0); - break; - case 'T': - if (mod && (flags & T_MTU)) - *mod = T_MTU; - if (fieldlen == 1) - return (0); - break; - case '*': - if (fieldlen != 1) { - fprintf(stderr, "%s:%d long '%c' %s\n", - filename, lineno, *field, name); - return (1); - } - if (mod && (flags & T_DC)) { - *mod = T_DC; - return (0); - } - case '%': - if (mod && (flags & T_MOD)) - *mod = T_MOD; - if (fieldlen == 1) { - fprintf(stderr, "%s:%d modulus %s must have a " - "value\n", filename, lineno, name); - return (1); - } - break; - } - if (mod == NULL || *mod == 0) { - fprintf(stderr, "%s:%d does not allow %c' %s\n", - filename, lineno, *field, name); - return (1); - } - i++; - } - - for (; i < fieldlen; i++) { - if (field[i] < '0' || field[i] > '9') { - fprintf(stderr, "%s:%d non-digit character in %s\n", - filename, lineno, name); - return (1); - } - val = val * 10 + field[i] - '0'; - if (val < 0) { - fprintf(stderr, "%s:%d %s overflowed\n", filename, - lineno, name); - return (1); - } - } - - if (val > max) { - fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno, - name, val, max); - return (1); - } - *var = (int)val; - - return (0); -} - -/* parse the next string in a formatted config file line */ -int -get_str(char **line, size_t *len, char **v, const char *name, int minlen, - const char *filename, int lineno) -{ - int fieldlen; - char *ptr; - - ptr = get_field(line, len, &fieldlen); - if (ptr == NULL) - return (1); - if (fieldlen < minlen) { - fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name); - return (1); - } - if ((*v = malloc(fieldlen + 1)) == NULL) { - perror("malloc()"); - return (1); - } - memcpy(*v, ptr, fieldlen); - (*v)[fieldlen] = '\0'; - - return (0); -} - -/* Parse out the TCP opts */ -int -get_tcpopts(const char *filename, int lineno, const char *tcpopts, - pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale, - int *wscale_mod, int *ts0) -{ - int i, opt; - - *packed = 0; - *optcnt = 0; - *wscale = 0; - *wscale_mod = T_DC; - *mss = 0; - *mss_mod = T_DC; - *ts0 = 0; - if (strcmp(tcpopts, ".") == 0) - return (0); - - for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) { - switch ((opt = toupper(tcpopts[i++]))) { - case 'N': /* FALLTHROUGH */ - case 'S': - *packed = (*packed << PF_OSFP_TCPOPT_BITS) | - (opt == 'N' ? PF_OSFP_TCPOPT_NOP : - PF_OSFP_TCPOPT_SACK); - break; - case 'W': /* FALLTHROUGH */ - case 'M': { - int *this_mod, *this; - - if (opt == 'W') { - this = wscale; - this_mod = wscale_mod; - } else { - this = mss; - this_mod = mss_mod; - } - *this = 0; - *this_mod = 0; - - *packed = (*packed << PF_OSFP_TCPOPT_BITS) | - (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE : - PF_OSFP_TCPOPT_MSS); - if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' || - tcpopts[i + 1] == ',')) { - *this_mod = T_DC; - i++; - break; - } - - if (tcpopts[i] == '%') { - *this_mod = T_MOD; - i++; - } - do { - if (!isdigit(tcpopts[i])) { - fprintf(stderr, "%s:%d unknown " - "character '%c' in %c TCP opt\n", - filename, lineno, tcpopts[i], opt); - return (1); - } - *this = (*this * 10) + tcpopts[i++] - '0'; - } while(tcpopts[i] != ',' && tcpopts[i] != '\0'); - break; - } - case 'T': - if (tcpopts[i] == '0') { - *ts0 = 1; - i++; - } - *packed = (*packed << PF_OSFP_TCPOPT_BITS) | - PF_OSFP_TCPOPT_TS; - break; - } - (*optcnt) ++; - if (tcpopts[i] == '\0') - break; - if (tcpopts[i] != ',') { - fprintf(stderr, "%s:%d unknown option to %c TCP opt\n", - filename, lineno, opt); - return (1); - } - i++; - } - - return (0); -} - -/* rip the next field ouf of a formatted config file line */ -char * -get_field(char **line, size_t *len, int *fieldlen) -{ - char *ret, *ptr = *line; - size_t plen = *len; - - - while (plen && isspace(*ptr)) { - plen--; - ptr++; - } - ret = ptr; - *fieldlen = 0; - - for (; plen > 0 && *ptr != ':'; plen--, ptr++) - (*fieldlen)++; - if (plen) { - *line = ptr + 1; - *len = plen - 1; - } else { - *len = 0; - } - while (*fieldlen && isspace(ret[*fieldlen - 1])) - (*fieldlen)--; - return (ret); -} - - -const char * -print_ioctl(struct pf_osfp_ioctl *fp) -{ - static char buf[1024]; - char tmp[32]; - int i, opt; - - *buf = '\0'; - if (fp->fp_flags & PF_OSFP_WSIZE_DC) - strlcat(buf, "*", sizeof(buf)); - else if (fp->fp_flags & PF_OSFP_WSIZE_MSS) - strlcat(buf, "S", sizeof(buf)); - else if (fp->fp_flags & PF_OSFP_WSIZE_MTU) - strlcat(buf, "T", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_WSIZE_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize); - strlcat(buf, tmp, sizeof(buf)); - } - strlcat(buf, ":", sizeof(buf)); - - snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl); - strlcat(buf, tmp, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - - if (fp->fp_flags & PF_OSFP_DF) - strlcat(buf, "1", sizeof(buf)); - else - strlcat(buf, "0", sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - - if (fp->fp_flags & PF_OSFP_PSIZE_DC) - strlcat(buf, "*", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_PSIZE_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize); - strlcat(buf, tmp, sizeof(buf)); - } - strlcat(buf, ":", sizeof(buf)); - - if (fp->fp_optcnt == 0) - strlcat(buf, ".", sizeof(buf)); - for (i = fp->fp_optcnt - 1; i >= 0; i--) { - opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS); - opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1; - switch (opt) { - case PF_OSFP_TCPOPT_NOP: - strlcat(buf, "N", sizeof(buf)); - break; - case PF_OSFP_TCPOPT_SACK: - strlcat(buf, "S", sizeof(buf)); - break; - case PF_OSFP_TCPOPT_TS: - strlcat(buf, "T", sizeof(buf)); - if (fp->fp_flags & PF_OSFP_TS0) - strlcat(buf, "0", sizeof(buf)); - break; - case PF_OSFP_TCPOPT_MSS: - strlcat(buf, "M", sizeof(buf)); - if (fp->fp_flags & PF_OSFP_MSS_DC) - strlcat(buf, "*", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_MSS_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss); - strlcat(buf, tmp, sizeof(buf)); - } - break; - case PF_OSFP_TCPOPT_WSCALE: - strlcat(buf, "W", sizeof(buf)); - if (fp->fp_flags & PF_OSFP_WSCALE_DC) - strlcat(buf, "*", sizeof(buf)); - else { - if (fp->fp_flags & PF_OSFP_WSCALE_MOD) - strlcat(buf, "%", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale); - strlcat(buf, tmp, sizeof(buf)); - } - break; - } - - if (i != 0) - strlcat(buf, ",", sizeof(buf)); - } - strlcat(buf, ":", sizeof(buf)); - - strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - - snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt, - (long long int)fp->fp_tcpopts); - strlcat(buf, tmp, sizeof(buf)); - - return (buf); -} diff --git a/contrib/pf/pfctl/pfctl_parser.c b/contrib/pf/pfctl/pfctl_parser.c deleted file mode 100644 index f2489956daa46..0000000000000 --- a/contrib/pf/pfctl/pfctl_parser.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* $OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -void print_op (u_int8_t, const char *, const char *); -void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); -void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); -void print_flags (u_int8_t); -void print_fromto(struct pf_rule_addr *, pf_osfp_t, - struct pf_rule_addr *, u_int8_t, u_int8_t, int, int); -int ifa_skip_if(const char *filter, struct node_host *p); - -struct node_host *ifa_grouplookup(const char *, int); -struct node_host *host_if(const char *, int); -struct node_host *host_v4(const char *, int); -struct node_host *host_v6(const char *, int); -struct node_host *host_dns(const char *, int, int); - -const char *tcpflags = "FSRPAUEW"; - -static const struct icmptypeent icmp_type[] = { - { "echoreq", ICMP_ECHO }, - { "echorep", ICMP_ECHOREPLY }, - { "unreach", ICMP_UNREACH }, - { "squench", ICMP_SOURCEQUENCH }, - { "redir", ICMP_REDIRECT }, - { "althost", ICMP_ALTHOSTADDR }, - { "routeradv", ICMP_ROUTERADVERT }, - { "routersol", ICMP_ROUTERSOLICIT }, - { "timex", ICMP_TIMXCEED }, - { "paramprob", ICMP_PARAMPROB }, - { "timereq", ICMP_TSTAMP }, - { "timerep", ICMP_TSTAMPREPLY }, - { "inforeq", ICMP_IREQ }, - { "inforep", ICMP_IREQREPLY }, - { "maskreq", ICMP_MASKREQ }, - { "maskrep", ICMP_MASKREPLY }, - { "trace", ICMP_TRACEROUTE }, - { "dataconv", ICMP_DATACONVERR }, - { "mobredir", ICMP_MOBILE_REDIRECT }, - { "ipv6-where", ICMP_IPV6_WHEREAREYOU }, - { "ipv6-here", ICMP_IPV6_IAMHERE }, - { "mobregreq", ICMP_MOBILE_REGREQUEST }, - { "mobregrep", ICMP_MOBILE_REGREPLY }, - { "skip", ICMP_SKIP }, - { "photuris", ICMP_PHOTURIS } -}; - -static const struct icmptypeent icmp6_type[] = { - { "unreach", ICMP6_DST_UNREACH }, - { "toobig", ICMP6_PACKET_TOO_BIG }, - { "timex", ICMP6_TIME_EXCEEDED }, - { "paramprob", ICMP6_PARAM_PROB }, - { "echoreq", ICMP6_ECHO_REQUEST }, - { "echorep", ICMP6_ECHO_REPLY }, - { "groupqry", ICMP6_MEMBERSHIP_QUERY }, - { "listqry", MLD_LISTENER_QUERY }, - { "grouprep", ICMP6_MEMBERSHIP_REPORT }, - { "listenrep", MLD_LISTENER_REPORT }, - { "groupterm", ICMP6_MEMBERSHIP_REDUCTION }, - { "listendone", MLD_LISTENER_DONE }, - { "routersol", ND_ROUTER_SOLICIT }, - { "routeradv", ND_ROUTER_ADVERT }, - { "neighbrsol", ND_NEIGHBOR_SOLICIT }, - { "neighbradv", ND_NEIGHBOR_ADVERT }, - { "redir", ND_REDIRECT }, - { "routrrenum", ICMP6_ROUTER_RENUMBERING }, - { "wrureq", ICMP6_WRUREQUEST }, - { "wrurep", ICMP6_WRUREPLY }, - { "fqdnreq", ICMP6_FQDN_QUERY }, - { "fqdnrep", ICMP6_FQDN_REPLY }, - { "niqry", ICMP6_NI_QUERY }, - { "nirep", ICMP6_NI_REPLY }, - { "mtraceresp", MLD_MTRACE_RESP }, - { "mtrace", MLD_MTRACE } -}; - -static const struct icmpcodeent icmp_code[] = { - { "net-unr", ICMP_UNREACH, ICMP_UNREACH_NET }, - { "host-unr", ICMP_UNREACH, ICMP_UNREACH_HOST }, - { "proto-unr", ICMP_UNREACH, ICMP_UNREACH_PROTOCOL }, - { "port-unr", ICMP_UNREACH, ICMP_UNREACH_PORT }, - { "needfrag", ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG }, - { "srcfail", ICMP_UNREACH, ICMP_UNREACH_SRCFAIL }, - { "net-unk", ICMP_UNREACH, ICMP_UNREACH_NET_UNKNOWN }, - { "host-unk", ICMP_UNREACH, ICMP_UNREACH_HOST_UNKNOWN }, - { "isolate", ICMP_UNREACH, ICMP_UNREACH_ISOLATED }, - { "net-prohib", ICMP_UNREACH, ICMP_UNREACH_NET_PROHIB }, - { "host-prohib", ICMP_UNREACH, ICMP_UNREACH_HOST_PROHIB }, - { "net-tos", ICMP_UNREACH, ICMP_UNREACH_TOSNET }, - { "host-tos", ICMP_UNREACH, ICMP_UNREACH_TOSHOST }, - { "filter-prohib", ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB }, - { "host-preced", ICMP_UNREACH, ICMP_UNREACH_HOST_PRECEDENCE }, - { "cutoff-preced", ICMP_UNREACH, ICMP_UNREACH_PRECEDENCE_CUTOFF }, - { "redir-net", ICMP_REDIRECT, ICMP_REDIRECT_NET }, - { "redir-host", ICMP_REDIRECT, ICMP_REDIRECT_HOST }, - { "redir-tos-net", ICMP_REDIRECT, ICMP_REDIRECT_TOSNET }, - { "redir-tos-host", ICMP_REDIRECT, ICMP_REDIRECT_TOSHOST }, - { "normal-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NORMAL }, - { "common-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NOROUTE_COMMON }, - { "transit", ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS }, - { "reassemb", ICMP_TIMXCEED, ICMP_TIMXCEED_REASS }, - { "badhead", ICMP_PARAMPROB, ICMP_PARAMPROB_ERRATPTR }, - { "optmiss", ICMP_PARAMPROB, ICMP_PARAMPROB_OPTABSENT }, - { "badlen", ICMP_PARAMPROB, ICMP_PARAMPROB_LENGTH }, - { "unknown-ind", ICMP_PHOTURIS, ICMP_PHOTURIS_UNKNOWN_INDEX }, - { "auth-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_AUTH_FAILED }, - { "decrypt-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_DECRYPT_FAILED } -}; - -static const struct icmpcodeent icmp6_code[] = { - { "admin-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN }, - { "noroute-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE }, - { "notnbr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOTNEIGHBOR }, - { "beyond-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE }, - { "addr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR }, - { "port-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT }, - { "transit", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT }, - { "reassemb", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY }, - { "badhead", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER }, - { "nxthdr", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER }, - { "redironlink", ND_REDIRECT, ND_REDIRECT_ONLINK }, - { "redirrouter", ND_REDIRECT, ND_REDIRECT_ROUTER } -}; - -const struct pf_timeout pf_timeouts[] = { - { "tcp.first", PFTM_TCP_FIRST_PACKET }, - { "tcp.opening", PFTM_TCP_OPENING }, - { "tcp.established", PFTM_TCP_ESTABLISHED }, - { "tcp.closing", PFTM_TCP_CLOSING }, - { "tcp.finwait", PFTM_TCP_FIN_WAIT }, - { "tcp.closed", PFTM_TCP_CLOSED }, - { "tcp.tsdiff", PFTM_TS_DIFF }, - { "udp.first", PFTM_UDP_FIRST_PACKET }, - { "udp.single", PFTM_UDP_SINGLE }, - { "udp.multiple", PFTM_UDP_MULTIPLE }, - { "icmp.first", PFTM_ICMP_FIRST_PACKET }, - { "icmp.error", PFTM_ICMP_ERROR_REPLY }, - { "other.first", PFTM_OTHER_FIRST_PACKET }, - { "other.single", PFTM_OTHER_SINGLE }, - { "other.multiple", PFTM_OTHER_MULTIPLE }, - { "frag", PFTM_FRAG }, - { "interval", PFTM_INTERVAL }, - { "adaptive.start", PFTM_ADAPTIVE_START }, - { "adaptive.end", PFTM_ADAPTIVE_END }, - { "src.track", PFTM_SRC_NODE }, - { NULL, 0 } -}; - -const struct icmptypeent * -geticmptypebynumber(u_int8_t type, sa_family_t af) -{ - unsigned int i; - - if (af != AF_INET6) { - for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0])); - i++) { - if (type == icmp_type[i].type) - return (&icmp_type[i]); - } - } else { - for (i=0; i < (sizeof (icmp6_type) / - sizeof(icmp6_type[0])); i++) { - if (type == icmp6_type[i].type) - return (&icmp6_type[i]); - } - } - return (NULL); -} - -const struct icmptypeent * -geticmptypebyname(char *w, sa_family_t af) -{ - unsigned int i; - - if (af != AF_INET6) { - for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0])); - i++) { - if (!strcmp(w, icmp_type[i].name)) - return (&icmp_type[i]); - } - } else { - for (i=0; i < (sizeof (icmp6_type) / - sizeof(icmp6_type[0])); i++) { - if (!strcmp(w, icmp6_type[i].name)) - return (&icmp6_type[i]); - } - } - return (NULL); -} - -const struct icmpcodeent * -geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af) -{ - unsigned int i; - - if (af != AF_INET6) { - for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0])); - i++) { - if (type == icmp_code[i].type && - code == icmp_code[i].code) - return (&icmp_code[i]); - } - } else { - for (i=0; i < (sizeof (icmp6_code) / - sizeof(icmp6_code[0])); i++) { - if (type == icmp6_code[i].type && - code == icmp6_code[i].code) - return (&icmp6_code[i]); - } - } - return (NULL); -} - -const struct icmpcodeent * -geticmpcodebyname(u_long type, char *w, sa_family_t af) -{ - unsigned int i; - - if (af != AF_INET6) { - for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0])); - i++) { - if (type == icmp_code[i].type && - !strcmp(w, icmp_code[i].name)) - return (&icmp_code[i]); - } - } else { - for (i=0; i < (sizeof (icmp6_code) / - sizeof(icmp6_code[0])); i++) { - if (type == icmp6_code[i].type && - !strcmp(w, icmp6_code[i].name)) - return (&icmp6_code[i]); - } - } - return (NULL); -} - -void -print_op(u_int8_t op, const char *a1, const char *a2) -{ - if (op == PF_OP_IRG) - printf(" %s >< %s", a1, a2); - else if (op == PF_OP_XRG) - printf(" %s <> %s", a1, a2); - else if (op == PF_OP_EQ) - printf(" = %s", a1); - else if (op == PF_OP_NE) - printf(" != %s", a1); - else if (op == PF_OP_LT) - printf(" < %s", a1); - else if (op == PF_OP_LE) - printf(" <= %s", a1); - else if (op == PF_OP_GT) - printf(" > %s", a1); - else if (op == PF_OP_GE) - printf(" >= %s", a1); - else if (op == PF_OP_RRG) - printf(" %s:%s", a1, a2); -} - -void -print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numeric) -{ - char a1[6], a2[6]; - struct servent *s; - - if (!numeric) - s = getservbyport(p1, proto); - else - s = NULL; - p1 = ntohs(p1); - p2 = ntohs(p2); - snprintf(a1, sizeof(a1), "%u", p1); - snprintf(a2, sizeof(a2), "%u", p2); - printf(" port"); - if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE)) - print_op(op, s->s_name, a2); - else - print_op(op, a1, a2); -} - -void -print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) -{ - char a1[11], a2[11]; - - snprintf(a1, sizeof(a1), "%u", u1); - snprintf(a2, sizeof(a2), "%u", u2); - printf(" %s", t); - if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE)) - print_op(op, "unknown", a2); - else - print_op(op, a1, a2); -} - -void -print_flags(u_int8_t f) -{ - int i; - - for (i = 0; tcpflags[i]; ++i) - if (f & (1 << i)) - printf("%c", tcpflags[i]); -} - -void -print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst, - sa_family_t af, u_int8_t proto, int verbose, int numeric) -{ - char buf[PF_OSFP_LEN*3]; - if (src->addr.type == PF_ADDR_ADDRMASK && - dst->addr.type == PF_ADDR_ADDRMASK && - PF_AZERO(&src->addr.v.a.addr, AF_INET6) && - PF_AZERO(&src->addr.v.a.mask, AF_INET6) && - PF_AZERO(&dst->addr.v.a.addr, AF_INET6) && - PF_AZERO(&dst->addr.v.a.mask, AF_INET6) && - !src->neg && !dst->neg && - !src->port_op && !dst->port_op && - osfp == PF_OSFP_ANY) - printf(" all"); - else { - printf(" from "); - if (src->neg) - printf("! "); - print_addr(&src->addr, af, verbose); - if (src->port_op) - print_port(src->port_op, src->port[0], - src->port[1], - proto == IPPROTO_TCP ? "tcp" : "udp", - numeric); - if (osfp != PF_OSFP_ANY) - printf(" os \"%s\"", pfctl_lookup_fingerprint(osfp, buf, - sizeof(buf))); - - printf(" to "); - if (dst->neg) - printf("! "); - print_addr(&dst->addr, af, verbose); - if (dst->port_op) - print_port(dst->port_op, dst->port[0], - dst->port[1], - proto == IPPROTO_TCP ? "tcp" : "udp", - numeric); - } -} - -void -print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2, - sa_family_t af, int id) -{ - struct pf_pooladdr *pooladdr; - - if ((TAILQ_FIRST(&pool->list) != NULL) && - TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) - printf("{ "); - TAILQ_FOREACH(pooladdr, &pool->list, entries){ - switch (id) { - case PF_NAT: - case PF_RDR: - case PF_BINAT: - print_addr(&pooladdr->addr, af, 0); - break; - case PF_PASS: - if (PF_AZERO(&pooladdr->addr.v.a.addr, af)) - printf("%s", pooladdr->ifname); - else { - printf("(%s ", pooladdr->ifname); - print_addr(&pooladdr->addr, af, 0); - printf(")"); - } - break; - default: - break; - } - if (TAILQ_NEXT(pooladdr, entries) != NULL) - printf(", "); - else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) - printf(" }"); - } - switch (id) { - case PF_NAT: - if ((p1 != PF_NAT_PROXY_PORT_LOW || - p2 != PF_NAT_PROXY_PORT_HIGH) && (p1 != 0 || p2 != 0)) { - if (p1 == p2) - printf(" port %u", p1); - else - printf(" port %u:%u", p1, p2); - } - break; - case PF_RDR: - if (p1) { - printf(" port %u", p1); - if (p2 && (p2 != p1)) - printf(":%u", p2); - } - break; - default: - break; - } - switch (pool->opts & PF_POOL_TYPEMASK) { - case PF_POOL_NONE: - break; - case PF_POOL_BITMASK: - printf(" bitmask"); - break; - case PF_POOL_RANDOM: - printf(" random"); - break; - case PF_POOL_SRCHASH: - printf(" source-hash 0x%08x%08x%08x%08x", - pool->key.key32[0], pool->key.key32[1], - pool->key.key32[2], pool->key.key32[3]); - break; - case PF_POOL_ROUNDROBIN: - printf(" round-robin"); - break; - } - if (pool->opts & PF_POOL_STICKYADDR) - printf(" sticky-address"); - if (id == PF_NAT && p1 == 0 && p2 == 0) - printf(" static-port"); -} - -const char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES; -const char *pf_lcounters[LCNT_MAX+1] = LCNT_NAMES; -const char *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES; -const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES; - -void -print_status(struct pf_status *s, int opts) -{ - char statline[80], *running; - time_t runtime; - int i; - char buf[PF_MD5_DIGEST_LENGTH * 2 + 1]; - static const char hex[] = "0123456789abcdef"; - - runtime = time(NULL) - s->since; - running = s->running ? "Enabled" : "Disabled"; - - if (s->since) { - unsigned int sec, min, hrs, day = runtime; - - sec = day % 60; - day /= 60; - min = day % 60; - day /= 60; - hrs = day % 24; - day /= 24; - snprintf(statline, sizeof(statline), - "Status: %s for %u days %.2u:%.2u:%.2u", - running, day, hrs, min, sec); - } else - snprintf(statline, sizeof(statline), "Status: %s", running); - printf("%-44s", statline); - switch (s->debug) { - case PF_DEBUG_NONE: - printf("%15s\n\n", "Debug: None"); - break; - case PF_DEBUG_URGENT: - printf("%15s\n\n", "Debug: Urgent"); - break; - case PF_DEBUG_MISC: - printf("%15s\n\n", "Debug: Misc"); - break; - case PF_DEBUG_NOISY: - printf("%15s\n\n", "Debug: Loud"); - break; - } - - if (opts & PF_OPT_VERBOSE) { - printf("Hostid: 0x%08x\n", ntohl(s->hostid)); - - for (i = 0; i < PF_MD5_DIGEST_LENGTH; i++) { - buf[i + i] = hex[s->pf_chksum[i] >> 4]; - buf[i + i + 1] = hex[s->pf_chksum[i] & 0x0f]; - } - buf[i + i] = '\0'; - printf("Checksum: 0x%s\n\n", buf); - } - - if (s->ifname[0] != 0) { - printf("Interface Stats for %-16s %5s %16s\n", - s->ifname, "IPv4", "IPv6"); - printf(" %-25s %14llu %16llu\n", "Bytes In", - (unsigned long long)s->bcounters[0][0], - (unsigned long long)s->bcounters[1][0]); - printf(" %-25s %14llu %16llu\n", "Bytes Out", - (unsigned long long)s->bcounters[0][1], - (unsigned long long)s->bcounters[1][1]); - printf(" Packets In\n"); - printf(" %-23s %14llu %16llu\n", "Passed", - (unsigned long long)s->pcounters[0][0][PF_PASS], - (unsigned long long)s->pcounters[1][0][PF_PASS]); - printf(" %-23s %14llu %16llu\n", "Blocked", - (unsigned long long)s->pcounters[0][0][PF_DROP], - (unsigned long long)s->pcounters[1][0][PF_DROP]); - printf(" Packets Out\n"); - printf(" %-23s %14llu %16llu\n", "Passed", - (unsigned long long)s->pcounters[0][1][PF_PASS], - (unsigned long long)s->pcounters[1][1][PF_PASS]); - printf(" %-23s %14llu %16llu\n\n", "Blocked", - (unsigned long long)s->pcounters[0][1][PF_DROP], - (unsigned long long)s->pcounters[1][1][PF_DROP]); - } - printf("%-27s %14s %16s\n", "State Table", "Total", "Rate"); - printf(" %-25s %14u %14s\n", "current entries", s->states, ""); - for (i = 0; i < FCNT_MAX; i++) { - printf(" %-25s %14llu ", pf_fcounters[i], - (unsigned long long)s->fcounters[i]); - if (runtime > 0) - printf("%14.1f/s\n", - (double)s->fcounters[i] / (double)runtime); - else - printf("%14s\n", ""); - } - if (opts & PF_OPT_VERBOSE) { - printf("Source Tracking Table\n"); - printf(" %-25s %14u %14s\n", "current entries", - s->src_nodes, ""); - for (i = 0; i < SCNT_MAX; i++) { - printf(" %-25s %14lld ", pf_scounters[i], -#ifdef __FreeBSD__ - (long long)s->scounters[i]); -#else - s->scounters[i]); -#endif - if (runtime > 0) - printf("%14.1f/s\n", - (double)s->scounters[i] / (double)runtime); - else - printf("%14s\n", ""); - } - } - printf("Counters\n"); - for (i = 0; i < PFRES_MAX; i++) { - printf(" %-25s %14llu ", pf_reasons[i], - (unsigned long long)s->counters[i]); - if (runtime > 0) - printf("%14.1f/s\n", - (double)s->counters[i] / (double)runtime); - else - printf("%14s\n", ""); - } - if (opts & PF_OPT_VERBOSE) { - printf("Limit Counters\n"); - for (i = 0; i < LCNT_MAX; i++) { - printf(" %-25s %14lld ", pf_lcounters[i], -#ifdef __FreeBSD__ - (unsigned long long)s->lcounters[i]); -#else - s->lcounters[i]); -#endif - if (runtime > 0) - printf("%14.1f/s\n", - (double)s->lcounters[i] / (double)runtime); - else - printf("%14s\n", ""); - } - } -} - -void -print_src_node(struct pf_src_node *sn, int opts) -{ - struct pf_addr_wrap aw; - int min, sec; - - memset(&aw, 0, sizeof(aw)); - if (sn->af == AF_INET) - aw.v.a.mask.addr32[0] = 0xffffffff; - else - memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); - - aw.v.a.addr = sn->addr; - print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2); - printf(" -> "); - aw.v.a.addr = sn->raddr; - print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2); - printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states, - sn->conn, sn->conn_rate.count / 1000, - (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds); - if (opts & PF_OPT_VERBOSE) { - sec = sn->creation % 60; - sn->creation /= 60; - min = sn->creation % 60; - sn->creation /= 60; - printf(" age %.2u:%.2u:%.2u", sn->creation, min, sec); - if (sn->states == 0) { - sec = sn->expire % 60; - sn->expire /= 60; - min = sn->expire % 60; - sn->expire /= 60; - printf(", expires in %.2u:%.2u:%.2u", - sn->expire, min, sec); - } - printf(", %llu pkts, %llu bytes", -#ifdef __FreeBSD__ - (unsigned long long)(sn->packets[0] + sn->packets[1]), - (unsigned long long)(sn->bytes[0] + sn->bytes[1])); -#else - sn->packets[0] + sn->packets[1], - sn->bytes[0] + sn->bytes[1]); -#endif - switch (sn->ruletype) { - case PF_NAT: - if (sn->rule.nr != -1) - printf(", nat rule %u", sn->rule.nr); - break; - case PF_RDR: - if (sn->rule.nr != -1) - printf(", rdr rule %u", sn->rule.nr); - break; - case PF_PASS: - if (sn->rule.nr != -1) - printf(", filter rule %u", sn->rule.nr); - break; - } - printf("\n"); - } -} - -void -print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) -{ - static const char *actiontypes[] = { "pass", "block", "scrub", - "no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr" }; - static const char *anchortypes[] = { "anchor", "anchor", "anchor", - "anchor", "nat-anchor", "nat-anchor", "binat-anchor", - "binat-anchor", "rdr-anchor", "rdr-anchor" }; - int i, opts; - - if (verbose) - printf("@%d ", r->nr); - if (r->action > PF_NORDR) - printf("action(%d)", r->action); - else if (anchor_call[0]) { - if (anchor_call[0] == '_') { - printf("%s", anchortypes[r->action]); - } else - printf("%s \"%s\"", anchortypes[r->action], - anchor_call); - } else { - printf("%s", actiontypes[r->action]); - if (r->natpass) - printf(" pass"); - } - if (r->action == PF_DROP) { - if (r->rule_flag & PFRULE_RETURN) - printf(" return"); - else if (r->rule_flag & PFRULE_RETURNRST) { - if (!r->return_ttl) - printf(" return-rst"); - else - printf(" return-rst(ttl %d)", r->return_ttl); - } else if (r->rule_flag & PFRULE_RETURNICMP) { - const struct icmpcodeent *ic, *ic6; - - ic = geticmpcodebynumber(r->return_icmp >> 8, - r->return_icmp & 255, AF_INET); - ic6 = geticmpcodebynumber(r->return_icmp6 >> 8, - r->return_icmp6 & 255, AF_INET6); - - switch (r->af) { - case AF_INET: - printf(" return-icmp"); - if (ic == NULL) - printf("(%u)", r->return_icmp & 255); - else - printf("(%s)", ic->name); - break; - case AF_INET6: - printf(" return-icmp6"); - if (ic6 == NULL) - printf("(%u)", r->return_icmp6 & 255); - else - printf("(%s)", ic6->name); - break; - default: - printf(" return-icmp"); - if (ic == NULL) - printf("(%u, ", r->return_icmp & 255); - else - printf("(%s, ", ic->name); - if (ic6 == NULL) - printf("%u)", r->return_icmp6 & 255); - else - printf("%s)", ic6->name); - break; - } - } else - printf(" drop"); - } - if (r->direction == PF_IN) - printf(" in"); - else if (r->direction == PF_OUT) - printf(" out"); - if (r->log) { - printf(" log"); - if (r->log & ~PF_LOG || r->logif) { - int count = 0; - - printf(" ("); - if (r->log & PF_LOG_ALL) - printf("%sall", count++ ? ", " : ""); - if (r->log & PF_LOG_SOCKET_LOOKUP) - printf("%suser", count++ ? ", " : ""); - if (r->logif) - printf("%sto pflog%u", count++ ? ", " : "", - r->logif); - printf(")"); - } - } - if (r->quick) - printf(" quick"); - if (r->ifname[0]) { - if (r->ifnot) - printf(" on ! %s", r->ifname); - else - printf(" on %s", r->ifname); - } - if (r->rt) { - if (r->rt == PF_ROUTETO) - printf(" route-to"); - else if (r->rt == PF_REPLYTO) - printf(" reply-to"); - else if (r->rt == PF_DUPTO) - printf(" dup-to"); - else if (r->rt == PF_FASTROUTE) - printf(" fastroute"); - if (r->rt != PF_FASTROUTE) { - printf(" "); - print_pool(&r->rpool, 0, 0, r->af, PF_PASS); - } - } - if (r->af) { - if (r->af == AF_INET) - printf(" inet"); - else - printf(" inet6"); - } - if (r->proto) { - struct protoent *p; - - if ((p = getprotobynumber(r->proto)) != NULL) - printf(" proto %s", p->p_name); - else - printf(" proto %u", r->proto); - } - print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto, - verbose, numeric); - if (r->uid.op) - print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user", - UID_MAX); - if (r->gid.op) - print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group", - GID_MAX); - if (r->flags || r->flagset) { - printf(" flags "); - print_flags(r->flags); - printf("/"); - print_flags(r->flagset); - } else if (r->action == PF_PASS && - (!r->proto || r->proto == IPPROTO_TCP) && - !(r->rule_flag & PFRULE_FRAGMENT) && - !anchor_call[0] && r->keep_state) - printf(" flags any"); - if (r->type) { - const struct icmptypeent *it; - - it = geticmptypebynumber(r->type-1, r->af); - if (r->af != AF_INET6) - printf(" icmp-type"); - else - printf(" icmp6-type"); - if (it != NULL) - printf(" %s", it->name); - else - printf(" %u", r->type-1); - if (r->code) { - const struct icmpcodeent *ic; - - ic = geticmpcodebynumber(r->type-1, r->code-1, r->af); - if (ic != NULL) - printf(" code %s", ic->name); - else - printf(" code %u", r->code-1); - } - } - if (r->tos) - printf(" tos 0x%2.2x", r->tos); - if (!r->keep_state && r->action == PF_PASS && !anchor_call[0]) - printf(" no state"); - else if (r->keep_state == PF_STATE_NORMAL) - printf(" keep state"); - else if (r->keep_state == PF_STATE_MODULATE) - printf(" modulate state"); - else if (r->keep_state == PF_STATE_SYNPROXY) - printf(" synproxy state"); - if (r->prob) { - char buf[20]; - - snprintf(buf, sizeof(buf), "%f", r->prob*100.0/(UINT_MAX+1.0)); - for (i = strlen(buf)-1; i > 0; i--) { - if (buf[i] == '0') - buf[i] = '\0'; - else { - if (buf[i] == '.') - buf[i] = '\0'; - break; - } - } - printf(" probability %s%%", buf); - } - opts = 0; - if (r->max_states || r->max_src_nodes || r->max_src_states) - opts = 1; - if (r->rule_flag & PFRULE_NOSYNC) - opts = 1; - if (r->rule_flag & PFRULE_SRCTRACK) - opts = 1; - if (r->rule_flag & PFRULE_IFBOUND) - opts = 1; - if (r->rule_flag & PFRULE_STATESLOPPY) - opts = 1; - for (i = 0; !opts && i < PFTM_MAX; ++i) - if (r->timeout[i]) - opts = 1; - if (opts) { - printf(" ("); - if (r->max_states) { - printf("max %u", r->max_states); - opts = 0; - } - if (r->rule_flag & PFRULE_NOSYNC) { - if (!opts) - printf(", "); - printf("no-sync"); - opts = 0; - } - if (r->rule_flag & PFRULE_SRCTRACK) { - if (!opts) - printf(", "); - printf("source-track"); - if (r->rule_flag & PFRULE_RULESRCTRACK) - printf(" rule"); - else - printf(" global"); - opts = 0; - } - if (r->max_src_states) { - if (!opts) - printf(", "); - printf("max-src-states %u", r->max_src_states); - opts = 0; - } - if (r->max_src_conn) { - if (!opts) - printf(", "); - printf("max-src-conn %u", r->max_src_conn); - opts = 0; - } - if (r->max_src_conn_rate.limit) { - if (!opts) - printf(", "); - printf("max-src-conn-rate %u/%u", - r->max_src_conn_rate.limit, - r->max_src_conn_rate.seconds); - opts = 0; - } - if (r->max_src_nodes) { - if (!opts) - printf(", "); - printf("max-src-nodes %u", r->max_src_nodes); - opts = 0; - } - if (r->overload_tblname[0]) { - if (!opts) - printf(", "); - printf("overload <%s>", r->overload_tblname); - if (r->flush) - printf(" flush"); - if (r->flush & PF_FLUSH_GLOBAL) - printf(" global"); - } - if (r->rule_flag & PFRULE_IFBOUND) { - if (!opts) - printf(", "); - printf("if-bound"); - opts = 0; - } - if (r->rule_flag & PFRULE_STATESLOPPY) { - if (!opts) - printf(", "); - printf("sloppy"); - opts = 0; - } - for (i = 0; i < PFTM_MAX; ++i) - if (r->timeout[i]) { - int j; - - if (!opts) - printf(", "); - opts = 0; - for (j = 0; pf_timeouts[j].name != NULL; - ++j) - if (pf_timeouts[j].timeout == i) - break; - printf("%s %u", pf_timeouts[j].name == NULL ? - "inv.timeout" : pf_timeouts[j].name, - r->timeout[i]); - } - printf(")"); - } - if (r->rule_flag & PFRULE_FRAGMENT) - printf(" fragment"); - if (r->rule_flag & PFRULE_NODF) - printf(" no-df"); - if (r->rule_flag & PFRULE_RANDOMID) - printf(" random-id"); - if (r->min_ttl) - printf(" min-ttl %d", r->min_ttl); - if (r->max_mss) - printf(" max-mss %d", r->max_mss); - if (r->rule_flag & PFRULE_SET_TOS) - printf(" set-tos 0x%2.2x", r->set_tos); - if (r->allow_opts) - printf(" allow-opts"); - if (r->action == PF_SCRUB) { - if (r->rule_flag & PFRULE_REASSEMBLE_TCP) - printf(" reassemble tcp"); - - if (r->rule_flag & PFRULE_FRAGDROP) - printf(" fragment drop-ovl"); - else if (r->rule_flag & PFRULE_FRAGCROP) - printf(" fragment crop"); - else - printf(" fragment reassemble"); - } - if (r->label[0]) - printf(" label \"%s\"", r->label); - if (r->qname[0] && r->pqname[0]) - printf(" queue(%s, %s)", r->qname, r->pqname); - else if (r->qname[0]) - printf(" queue %s", r->qname); - if (r->tagname[0]) - printf(" tag %s", r->tagname); - if (r->match_tagname[0]) { - if (r->match_tag_not) - printf(" !"); - printf(" tagged %s", r->match_tagname); - } - if (r->rtableid != -1) - printf(" rtable %u", r->rtableid); - if (r->divert.port) { -#ifdef __FreeBSD__ - printf(" divert-to %u", ntohs(r->divert.port)); -#else - if (PF_AZERO(&r->divert.addr, r->af)) { - printf(" divert-reply"); - } else { - /* XXX cut&paste from print_addr */ - char buf[48]; - - printf(" divert-to "); - if (inet_ntop(r->af, &r->divert.addr, buf, - sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - printf(" port %u", ntohs(r->divert.port)); - } -#endif - } - if (!anchor_call[0] && (r->action == PF_NAT || - r->action == PF_BINAT || r->action == PF_RDR)) { - printf(" -> "); - print_pool(&r->rpool, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], r->af, r->action); - } -} - -void -print_tabledef(const char *name, int flags, int addrs, - struct node_tinithead *nodes) -{ - struct node_tinit *ti, *nti; - struct node_host *h; - - printf("table <%s>", name); - if (flags & PFR_TFLAG_CONST) - printf(" const"); - if (flags & PFR_TFLAG_PERSIST) - printf(" persist"); - if (flags & PFR_TFLAG_COUNTERS) - printf(" counters"); - SIMPLEQ_FOREACH(ti, nodes, entries) { - if (ti->file) { - printf(" file \"%s\"", ti->file); - continue; - } - printf(" {"); - for (;;) { - for (h = ti->host; h != NULL; h = h->next) { - printf(h->not ? " !" : " "); - print_addr(&h->addr, h->af, 0); - } - nti = SIMPLEQ_NEXT(ti, entries); - if (nti != NULL && nti->file == NULL) - ti = nti; /* merge lists */ - else - break; - } - printf(" }"); - } - if (addrs && SIMPLEQ_EMPTY(nodes)) - printf(" { }"); - printf("\n"); -} - -int -parse_flags(char *s) -{ - char *p, *q; - u_int8_t f = 0; - - for (p = s; *p; p++) { - if ((q = strchr(tcpflags, *p)) == NULL) - return -1; - else - f |= 1 << (q - tcpflags); - } - return (f ? f : PF_TH_ALL); -} - -void -set_ipmask(struct node_host *h, u_int8_t b) -{ - struct pf_addr *m, *n; - int i, j = 0; - - m = &h->addr.v.a.mask; - memset(m, 0, sizeof(*m)); - - while (b >= 32) { - m->addr32[j++] = 0xffffffff; - b -= 32; - } - for (i = 31; i > 31-b; --i) - m->addr32[j] |= (1 << i); - if (b) - m->addr32[j] = htonl(m->addr32[j]); - - /* Mask off bits of the address that will never be used. */ - n = &h->addr.v.a.addr; - if (h->addr.type == PF_ADDR_ADDRMASK) - for (i = 0; i < 4; i++) - n->addr32[i] = n->addr32[i] & m->addr32[i]; -} - -int -check_netmask(struct node_host *h, sa_family_t af) -{ - struct node_host *n = NULL; - struct pf_addr *m; - - for (n = h; n != NULL; n = n->next) { - if (h->addr.type == PF_ADDR_TABLE) - continue; - m = &h->addr.v.a.mask; - /* fix up netmask for dynaddr */ - if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL && - unmask(m, AF_INET6) > 32) - set_ipmask(n, 32); - /* netmasks > 32 bit are invalid on v4 */ - if (af == AF_INET && - (m->addr32[1] || m->addr32[2] || m->addr32[3])) { - fprintf(stderr, "netmask %u invalid for IPv4 address\n", - unmask(m, AF_INET6)); - return (1); - } - } - return (0); -} - -/* interface lookup routines */ - -struct node_host *iftab; - -void -ifa_load(void) -{ - struct ifaddrs *ifap, *ifa; - struct node_host *n = NULL, *h = NULL; - - if (getifaddrs(&ifap) < 0) - err(1, "getifaddrs"); - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (!(ifa->ifa_addr->sa_family == AF_INET || - ifa->ifa_addr->sa_family == AF_INET6 || - ifa->ifa_addr->sa_family == AF_LINK)) - continue; - n = calloc(1, sizeof(struct node_host)); - if (n == NULL) - err(1, "address: calloc"); - n->af = ifa->ifa_addr->sa_family; - n->ifa_flags = ifa->ifa_flags; -#ifdef __KAME__ - if (n->af == AF_INET6 && - IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *) - ifa->ifa_addr)->sin6_addr) && - ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id == - 0) { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - sin6->sin6_scope_id = sin6->sin6_addr.s6_addr[2] << 8 | - sin6->sin6_addr.s6_addr[3]; - sin6->sin6_addr.s6_addr[2] = 0; - sin6->sin6_addr.s6_addr[3] = 0; - } -#endif - n->ifindex = 0; - if (n->af == AF_INET) { - memcpy(&n->addr.v.a.addr, &((struct sockaddr_in *) - ifa->ifa_addr)->sin_addr.s_addr, - sizeof(struct in_addr)); - memcpy(&n->addr.v.a.mask, &((struct sockaddr_in *) - ifa->ifa_netmask)->sin_addr.s_addr, - sizeof(struct in_addr)); - if (ifa->ifa_broadaddr != NULL) - memcpy(&n->bcast, &((struct sockaddr_in *) - ifa->ifa_broadaddr)->sin_addr.s_addr, - sizeof(struct in_addr)); - if (ifa->ifa_dstaddr != NULL) - memcpy(&n->peer, &((struct sockaddr_in *) - ifa->ifa_dstaddr)->sin_addr.s_addr, - sizeof(struct in_addr)); - } else if (n->af == AF_INET6) { - memcpy(&n->addr.v.a.addr, &((struct sockaddr_in6 *) - ifa->ifa_addr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - memcpy(&n->addr.v.a.mask, &((struct sockaddr_in6 *) - ifa->ifa_netmask)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - if (ifa->ifa_broadaddr != NULL) - memcpy(&n->bcast, &((struct sockaddr_in6 *) - ifa->ifa_broadaddr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - if (ifa->ifa_dstaddr != NULL) - memcpy(&n->peer, &((struct sockaddr_in6 *) - ifa->ifa_dstaddr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - n->ifindex = ((struct sockaddr_in6 *) - ifa->ifa_addr)->sin6_scope_id; - } - if ((n->ifname = strdup(ifa->ifa_name)) == NULL) - err(1, "ifa_load: strdup"); - n->next = NULL; - n->tail = n; - if (h == NULL) - h = n; - else { - h->tail->next = n; - h->tail = n; - } - } - - iftab = h; - freeifaddrs(ifap); -} - -struct node_host * -ifa_exists(const char *ifa_name) -{ - struct node_host *n; - struct ifgroupreq ifgr; - int s; - - if (iftab == NULL) - ifa_load(); - - /* check wether this is a group */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - err(1, "socket"); - bzero(&ifgr, sizeof(ifgr)); - strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); - if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) { - /* fake a node_host */ - if ((n = calloc(1, sizeof(*n))) == NULL) - err(1, "calloc"); - if ((n->ifname = strdup(ifa_name)) == NULL) - err(1, "strdup"); - close(s); - return (n); - } - close(s); - - for (n = iftab; n; n = n->next) { - if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ)) - return (n); - } - - return (NULL); -} - -struct node_host * -ifa_grouplookup(const char *ifa_name, int flags) -{ - struct ifg_req *ifg; - struct ifgroupreq ifgr; - int s, len; - struct node_host *n, *h = NULL; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - err(1, "socket"); - bzero(&ifgr, sizeof(ifgr)); - strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); - if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { - close(s); - return (NULL); - } - - len = ifgr.ifgr_len; - if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) - err(1, "calloc"); - if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) - err(1, "SIOCGIFGMEMB"); - - for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); - ifg++) { - len -= sizeof(struct ifg_req); - if ((n = ifa_lookup(ifg->ifgrq_member, flags)) == NULL) - continue; - if (h == NULL) - h = n; - else { - h->tail->next = n; - h->tail = n->tail; - } - } - free(ifgr.ifgr_groups); - close(s); - - return (h); -} - -struct node_host * -ifa_lookup(const char *ifa_name, int flags) -{ - struct node_host *p = NULL, *h = NULL, *n = NULL; - int got4 = 0, got6 = 0; - const char *last_if = NULL; - - if ((h = ifa_grouplookup(ifa_name, flags)) != NULL) - return (h); - - if (!strncmp(ifa_name, "self", IFNAMSIZ)) - ifa_name = NULL; - - if (iftab == NULL) - ifa_load(); - - for (p = iftab; p; p = p->next) { - if (ifa_skip_if(ifa_name, p)) - continue; - if ((flags & PFI_AFLAG_BROADCAST) && p->af != AF_INET) - continue; - if ((flags & PFI_AFLAG_BROADCAST) && - !(p->ifa_flags & IFF_BROADCAST)) - continue; - if ((flags & PFI_AFLAG_PEER) && - !(p->ifa_flags & IFF_POINTOPOINT)) - continue; - if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0) - continue; - if (last_if == NULL || strcmp(last_if, p->ifname)) - got4 = got6 = 0; - last_if = p->ifname; - if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET && got4) - continue; - if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6) - continue; - if (p->af == AF_INET) - got4 = 1; - else - got6 = 1; - n = calloc(1, sizeof(struct node_host)); - if (n == NULL) - err(1, "address: calloc"); - n->af = p->af; - if (flags & PFI_AFLAG_BROADCAST) - memcpy(&n->addr.v.a.addr, &p->bcast, - sizeof(struct pf_addr)); - else if (flags & PFI_AFLAG_PEER) - memcpy(&n->addr.v.a.addr, &p->peer, - sizeof(struct pf_addr)); - else - memcpy(&n->addr.v.a.addr, &p->addr.v.a.addr, - sizeof(struct pf_addr)); - if (flags & PFI_AFLAG_NETWORK) - set_ipmask(n, unmask(&p->addr.v.a.mask, n->af)); - else { - if (n->af == AF_INET) { - if (p->ifa_flags & IFF_LOOPBACK && - p->ifa_flags & IFF_LINK1) - memcpy(&n->addr.v.a.mask, - &p->addr.v.a.mask, - sizeof(struct pf_addr)); - else - set_ipmask(n, 32); - } else - set_ipmask(n, 128); - } - n->ifindex = p->ifindex; - - n->next = NULL; - n->tail = n; - if (h == NULL) - h = n; - else { - h->tail->next = n; - h->tail = n; - } - } - return (h); -} - -int -ifa_skip_if(const char *filter, struct node_host *p) -{ - int n; - - if (p->af != AF_INET && p->af != AF_INET6) - return (1); - if (filter == NULL || !*filter) - return (0); - if (!strcmp(p->ifname, filter)) - return (0); /* exact match */ - n = strlen(filter); - if (n < 1 || n >= IFNAMSIZ) - return (1); /* sanity check */ - if (filter[n-1] >= '0' && filter[n-1] <= '9') - return (1); /* only do exact match in that case */ - if (strncmp(p->ifname, filter, n)) - return (1); /* prefix doesn't match */ - return (p->ifname[n] < '0' || p->ifname[n] > '9'); -} - - -struct node_host * -host(const char *s) -{ - struct node_host *h = NULL; - int mask, v4mask, v6mask, cont = 1; - char *p, *q, *ps; - - if ((p = strrchr(s, '/')) != NULL) { - mask = strtol(p+1, &q, 0); - if (!q || *q || mask > 128 || q == (p+1)) { - fprintf(stderr, "invalid netmask '%s'\n", p); - return (NULL); - } - if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) - err(1, "host: malloc"); - strlcpy(ps, s, strlen(s) - strlen(p) + 1); - v4mask = v6mask = mask; - } else { - if ((ps = strdup(s)) == NULL) - err(1, "host: strdup"); - v4mask = 32; - v6mask = 128; - mask = -1; - } - - /* interface with this name exists? */ - if (cont && (h = host_if(ps, mask)) != NULL) - cont = 0; - - /* IPv4 address? */ - if (cont && (h = host_v4(s, mask)) != NULL) - cont = 0; - - /* IPv6 address? */ - if (cont && (h = host_v6(ps, v6mask)) != NULL) - cont = 0; - - /* dns lookup */ - if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL) - cont = 0; - free(ps); - - if (h == NULL || cont == 1) { - fprintf(stderr, "no IP address found for %s\n", s); - return (NULL); - } - return (h); -} - -struct node_host * -host_if(const char *s, int mask) -{ - struct node_host *n, *h = NULL; - char *p, *ps; - int flags = 0; - - if ((ps = strdup(s)) == NULL) - err(1, "host_if: strdup"); - while ((p = strrchr(ps, ':')) != NULL) { - if (!strcmp(p+1, "network")) - flags |= PFI_AFLAG_NETWORK; - else if (!strcmp(p+1, "broadcast")) - flags |= PFI_AFLAG_BROADCAST; - else if (!strcmp(p+1, "peer")) - flags |= PFI_AFLAG_PEER; - else if (!strcmp(p+1, "0")) - flags |= PFI_AFLAG_NOALIAS; - else { - free(ps); - return (NULL); - } - *p = '\0'; - } - if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */ - fprintf(stderr, "illegal combination of interface modifiers\n"); - free(ps); - return (NULL); - } - if ((flags & (PFI_AFLAG_NETWORK|PFI_AFLAG_BROADCAST)) && mask > -1) { - fprintf(stderr, "network or broadcast lookup, but " - "extra netmask given\n"); - free(ps); - return (NULL); - } - if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) { - /* interface with this name exists */ - h = ifa_lookup(ps, flags); - for (n = h; n != NULL && mask > -1; n = n->next) - set_ipmask(n, mask); - } - - free(ps); - return (h); -} - -struct node_host * -host_v4(const char *s, int mask) -{ - struct node_host *h = NULL; - struct in_addr ina; - int bits = 32; - - memset(&ina, 0, sizeof(struct in_addr)); - if (strrchr(s, '/') != NULL) { - if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) - return (NULL); - } else { - if (inet_pton(AF_INET, s, &ina) != 1) - return (NULL); - } - - h = calloc(1, sizeof(struct node_host)); - if (h == NULL) - err(1, "address: calloc"); - h->ifname = NULL; - h->af = AF_INET; - h->addr.v.a.addr.addr32[0] = ina.s_addr; - set_ipmask(h, bits); - h->next = NULL; - h->tail = h; - - return (h); -} - -struct node_host * -host_v6(const char *s, int mask) -{ - struct addrinfo hints, *res; - struct node_host *h = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET6; - hints.ai_socktype = SOCK_DGRAM; /*dummy*/ - hints.ai_flags = AI_NUMERICHOST; - if (getaddrinfo(s, "0", &hints, &res) == 0) { - h = calloc(1, sizeof(struct node_host)); - if (h == NULL) - err(1, "address: calloc"); - h->ifname = NULL; - h->af = AF_INET6; - memcpy(&h->addr.v.a.addr, - &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, - sizeof(h->addr.v.a.addr)); - h->ifindex = - ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; - set_ipmask(h, mask); - freeaddrinfo(res); - h->next = NULL; - h->tail = h; - } - - return (h); -} - -struct node_host * -host_dns(const char *s, int v4mask, int v6mask) -{ - struct addrinfo hints, *res0, *res; - struct node_host *n, *h = NULL; - int error, noalias = 0; - int got4 = 0, got6 = 0; - char *p, *ps; - - if ((ps = strdup(s)) == NULL) - err(1, "host_dns: strdup"); - if ((p = strrchr(ps, ':')) != NULL && !strcmp(p, ":0")) { - noalias = 1; - *p = '\0'; - } - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; /* DUMMY */ - error = getaddrinfo(ps, NULL, &hints, &res0); - if (error) { - free(ps); - return (h); - } - - for (res = res0; res; res = res->ai_next) { - if (res->ai_family != AF_INET && - res->ai_family != AF_INET6) - continue; - if (noalias) { - if (res->ai_family == AF_INET) { - if (got4) - continue; - got4 = 1; - } else { - if (got6) - continue; - got6 = 1; - } - } - n = calloc(1, sizeof(struct node_host)); - if (n == NULL) - err(1, "host_dns: calloc"); - n->ifname = NULL; - n->af = res->ai_family; - if (res->ai_family == AF_INET) { - memcpy(&n->addr.v.a.addr, - &((struct sockaddr_in *) - res->ai_addr)->sin_addr.s_addr, - sizeof(struct in_addr)); - set_ipmask(n, v4mask); - } else { - memcpy(&n->addr.v.a.addr, - &((struct sockaddr_in6 *) - res->ai_addr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - n->ifindex = - ((struct sockaddr_in6 *) - res->ai_addr)->sin6_scope_id; - set_ipmask(n, v6mask); - } - n->next = NULL; - n->tail = n; - if (h == NULL) - h = n; - else { - h->tail->next = n; - h->tail = n; - } - } - freeaddrinfo(res0); - free(ps); - - return (h); -} - -/* - * convert a hostname to a list of addresses and put them in the given buffer. - * test: - * if set to 1, only simple addresses are accepted (no netblock, no "!"). - */ -int -append_addr(struct pfr_buffer *b, char *s, int test) -{ - char *r; - struct node_host *h, *n; - int rv, not = 0; - - for (r = s; *r == '!'; r++) - not = !not; - if ((n = host(r)) == NULL) { - errno = 0; - return (-1); - } - rv = append_addr_host(b, n, test, not); - do { - h = n; - n = n->next; - free(h); - } while (n != NULL); - return (rv); -} - -/* - * same as previous function, but with a pre-parsed input and the ability - * to "negate" the result. Does not free the node_host list. - * not: - * setting it to 1 is equivalent to adding "!" in front of parameter s. - */ -int -append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not) -{ - int bits; - struct pfr_addr addr; - - do { - bzero(&addr, sizeof(addr)); - addr.pfra_not = n->not ^ not; - addr.pfra_af = n->af; - addr.pfra_net = unmask(&n->addr.v.a.mask, n->af); - switch (n->af) { - case AF_INET: - addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0]; - bits = 32; - break; - case AF_INET6: - memcpy(&addr.pfra_ip6addr, &n->addr.v.a.addr.v6, - sizeof(struct in6_addr)); - bits = 128; - break; - default: - errno = EINVAL; - return (-1); - } - if ((test && (not || addr.pfra_net != bits)) || - addr.pfra_net > bits) { - errno = EINVAL; - return (-1); - } - if (pfr_buf_add(b, &addr)) - return (-1); - } while ((n = n->next) != NULL); - - return (0); -} - -int -pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor) -{ - struct pfioc_trans_e trans; - - bzero(&trans, sizeof(trans)); - trans.rs_num = rs_num; - if (strlcpy(trans.anchor, anchor, - sizeof(trans.anchor)) >= sizeof(trans.anchor)) - errx(1, "pfctl_add_trans: strlcpy"); - - return pfr_buf_add(buf, &trans); -} - -u_int32_t -pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor) -{ - struct pfioc_trans_e *p; - - PFRB_FOREACH(p, buf) - if (rs_num == p->rs_num && !strcmp(anchor, p->anchor)) - return (p->ticket); - errx(1, "pfctl_get_ticket: assertion failed"); -} - -int -pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from) -{ - struct pfioc_trans trans; - - bzero(&trans, sizeof(trans)); - trans.size = buf->pfrb_size - from; - trans.esize = sizeof(struct pfioc_trans_e); - trans.array = ((struct pfioc_trans_e *)buf->pfrb_caddr) + from; - return ioctl(dev, cmd, &trans); -} diff --git a/contrib/pf/pfctl/pfctl_parser.h b/contrib/pf/pfctl/pfctl_parser.h deleted file mode 100644 index 4560d66bb0edc..0000000000000 --- a/contrib/pf/pfctl/pfctl_parser.h +++ /dev/null @@ -1,305 +0,0 @@ -/* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _PFCTL_PARSER_H_ -#define _PFCTL_PARSER_H_ - -#define PF_OSFP_FILE "/etc/pf.os" - -#define PF_OPT_DISABLE 0x0001 -#define PF_OPT_ENABLE 0x0002 -#define PF_OPT_VERBOSE 0x0004 -#define PF_OPT_NOACTION 0x0008 -#define PF_OPT_QUIET 0x0010 -#define PF_OPT_CLRRULECTRS 0x0020 -#define PF_OPT_USEDNS 0x0040 -#define PF_OPT_VERBOSE2 0x0080 -#define PF_OPT_DUMMYACTION 0x0100 -#define PF_OPT_DEBUG 0x0200 -#define PF_OPT_SHOWALL 0x0400 -#define PF_OPT_OPTIMIZE 0x0800 -#define PF_OPT_NUMERIC 0x1000 -#define PF_OPT_MERGE 0x2000 -#define PF_OPT_RECURSE 0x4000 - -#define PF_TH_ALL 0xFF - -#define PF_NAT_PROXY_PORT_LOW 50001 -#define PF_NAT_PROXY_PORT_HIGH 65535 - -#define PF_OPTIMIZE_BASIC 0x0001 -#define PF_OPTIMIZE_PROFILE 0x0002 - -#define FCNT_NAMES { \ - "searches", \ - "inserts", \ - "removals", \ - NULL \ -} - -struct pfr_buffer; /* forward definition */ - - -struct pfctl { - int dev; - int opts; - int optimize; - int loadopt; - int asd; /* anchor stack depth */ - int bn; /* brace number */ - int brace; - int tdirty; /* kernel dirty */ -#define PFCTL_ANCHOR_STACK_DEPTH 64 - struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH]; - struct pfioc_pooladdr paddr; - struct pfioc_altq *paltq; - struct pfioc_queue *pqueue; - struct pfr_buffer *trans; - struct pf_anchor *anchor, *alast; - const char *ruleset; - - /* 'set foo' options */ - u_int32_t timeout[PFTM_MAX]; - u_int32_t limit[PF_LIMIT_MAX]; - u_int32_t debug; - u_int32_t hostid; - char *ifname; - - u_int8_t timeout_set[PFTM_MAX]; - u_int8_t limit_set[PF_LIMIT_MAX]; - u_int8_t debug_set; - u_int8_t hostid_set; - u_int8_t ifname_set; -}; - -struct node_if { - char ifname[IFNAMSIZ]; - u_int8_t not; - u_int8_t dynamic; /* antispoof */ - u_int ifa_flags; - struct node_if *next; - struct node_if *tail; -}; - -struct node_host { - struct pf_addr_wrap addr; - struct pf_addr bcast; - struct pf_addr peer; - sa_family_t af; - u_int8_t not; - u_int32_t ifindex; /* link-local IPv6 addrs */ - char *ifname; - u_int ifa_flags; - struct node_host *next; - struct node_host *tail; -}; - -struct node_os { - char *os; - pf_osfp_t fingerprint; - struct node_os *next; - struct node_os *tail; -}; - -struct node_queue_bw { - u_int32_t bw_absolute; - u_int16_t bw_percent; -}; - -struct node_hfsc_sc { - struct node_queue_bw m1; /* slope of 1st segment; bps */ - u_int d; /* x-projection of m1; msec */ - struct node_queue_bw m2; /* slope of 2nd segment; bps */ - u_int8_t used; -}; - -struct node_hfsc_opts { - struct node_hfsc_sc realtime; - struct node_hfsc_sc linkshare; - struct node_hfsc_sc upperlimit; - int flags; -}; - -struct node_queue_opt { - int qtype; - union { - struct cbq_opts cbq_opts; - struct priq_opts priq_opts; - struct node_hfsc_opts hfsc_opts; - } data; -}; - -#ifdef __FreeBSD__ -/* - * XXX - * Absolutely this is not correct location to define this. - * Should we use an another sperate header file? - */ -#define SIMPLEQ_HEAD STAILQ_HEAD -#define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER -#define SIMPLEQ_ENTRY STAILQ_ENTRY -#define SIMPLEQ_FIRST STAILQ_FIRST -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY STAILQ_EMPTY -#define SIMPLEQ_NEXT STAILQ_NEXT -/*#define SIMPLEQ_FOREACH STAILQ_FOREACH*/ -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) -#define SIMPLEQ_INIT STAILQ_INIT -#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD -#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL -#define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER -#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD -#endif -SIMPLEQ_HEAD(node_tinithead, node_tinit); -struct node_tinit { /* table initializer */ - SIMPLEQ_ENTRY(node_tinit) entries; - struct node_host *host; - char *file; -}; - - -/* optimizer created tables */ -struct pf_opt_tbl { - char pt_name[PF_TABLE_NAME_SIZE]; - int pt_rulecount; - int pt_generated; - struct node_tinithead pt_nodes; - struct pfr_buffer *pt_buf; -}; -#define PF_OPT_TABLE_PREFIX "__automatic_" - -/* optimizer pf_rule container */ -struct pf_opt_rule { - struct pf_rule por_rule; - struct pf_opt_tbl *por_src_tbl; - struct pf_opt_tbl *por_dst_tbl; - u_int64_t por_profile_count; - TAILQ_ENTRY(pf_opt_rule) por_entry; - TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT]; -}; - -TAILQ_HEAD(pf_opt_queue, pf_opt_rule); - -int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *); -int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *); - -int pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *); -int pfctl_add_altq(struct pfctl *, struct pf_altq *); -int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t); -void pfctl_move_pool(struct pf_pool *, struct pf_pool *); -void pfctl_clear_pool(struct pf_pool *); - -int pfctl_set_timeout(struct pfctl *, const char *, int, int); -int pfctl_set_optimization(struct pfctl *, const char *); -int pfctl_set_limit(struct pfctl *, const char *, unsigned int); -int pfctl_set_logif(struct pfctl *, char *); -int pfctl_set_hostid(struct pfctl *, u_int32_t); -int pfctl_set_debug(struct pfctl *, char *); -int pfctl_set_interface_flags(struct pfctl *, char *, int, int); - -int parse_config(char *, struct pfctl *); -int parse_flags(char *); -int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); - -void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int); -void print_src_node(struct pf_src_node *, int); -void print_rule(struct pf_rule *, const char *, int, int); -void print_tabledef(const char *, int, int, struct node_tinithead *); -void print_status(struct pf_status *, int); - -int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *, - struct node_queue_opt *); -int eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *, - struct node_queue_opt *); - -void print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *, - struct node_queue_opt *); -void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *, - int, struct node_queue_opt *); - -int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *, - u_int32_t); - -void pfctl_clear_fingerprints(int, int); -int pfctl_file_fingerprints(int, int, const char *); -pf_osfp_t pfctl_get_fingerprint(const char *); -int pfctl_load_fingerprints(int, int); -char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t); -void pfctl_show_fingerprints(int); - - -struct icmptypeent { - const char *name; - u_int8_t type; -}; - -struct icmpcodeent { - const char *name; - u_int8_t type; - u_int8_t code; -}; - -const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t); -const struct icmptypeent *geticmptypebyname(char *, u_int8_t); -const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t); -const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t); - -struct pf_timeout { - const char *name; - int timeout; -}; - -#define PFCTL_FLAG_FILTER 0x02 -#define PFCTL_FLAG_NAT 0x04 -#define PFCTL_FLAG_OPTION 0x08 -#define PFCTL_FLAG_ALTQ 0x10 -#define PFCTL_FLAG_TABLE 0x20 - -extern const struct pf_timeout pf_timeouts[]; - -void set_ipmask(struct node_host *, u_int8_t); -int check_netmask(struct node_host *, sa_family_t); -int unmask(struct pf_addr *, sa_family_t); -void ifa_load(void); -struct node_host *ifa_exists(const char *); -struct node_host *ifa_lookup(const char *, int); -struct node_host *host(const char *); - -int append_addr(struct pfr_buffer *, char *, int); -int append_addr_host(struct pfr_buffer *, - struct node_host *, int, int); - -#endif /* _PFCTL_PARSER_H_ */ diff --git a/contrib/pf/pfctl/pfctl_qstats.c b/contrib/pf/pfctl/pfctl_qstats.c deleted file mode 100644 index 95371e47af441..0000000000000 --- a/contrib/pf/pfctl/pfctl_qstats.c +++ /dev/null @@ -1,449 +0,0 @@ -/* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */ - -/* - * Copyright (c) Henning Brauer - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "pfctl.h" -#include "pfctl_parser.h" - -union class_stats { - class_stats_t cbq_stats; - struct priq_classstats priq_stats; - struct hfsc_classstats hfsc_stats; -}; - -#define AVGN_MAX 8 -#define STAT_INTERVAL 5 - -struct queue_stats { - union class_stats data; - int avgn; - double avg_bytes; - double avg_packets; - u_int64_t prev_bytes; - u_int64_t prev_packets; -}; - -struct pf_altq_node { - struct pf_altq altq; - struct pf_altq_node *next; - struct pf_altq_node *children; - struct queue_stats qstats; -}; - -int pfctl_update_qstats(int, struct pf_altq_node **); -void pfctl_insert_altq_node(struct pf_altq_node **, - const struct pf_altq, const struct queue_stats); -struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, - const char *, const char *); -void pfctl_print_altq_node(int, const struct pf_altq_node *, - unsigned, int); -void print_cbqstats(struct queue_stats); -void print_priqstats(struct queue_stats); -void print_hfscstats(struct queue_stats); -void pfctl_free_altq_node(struct pf_altq_node *); -void pfctl_print_altq_nodestat(int, - const struct pf_altq_node *); - -void update_avg(struct pf_altq_node *); - -int -pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) -{ - struct pf_altq_node *root = NULL, *node; - int nodes, dotitle = (opts & PF_OPT_SHOWALL); - -#ifdef __FreeBSD__ - if (!altqsupport) - return (-1); -#endif - - if ((nodes = pfctl_update_qstats(dev, &root)) < 0) - return (-1); - - if (nodes == 0) - printf("No queue in use\n"); - for (node = root; node != NULL; node = node->next) { - if (iface != NULL && strcmp(node->altq.ifname, iface)) - continue; - if (dotitle) { - pfctl_print_title("ALTQ:"); - dotitle = 0; - } - pfctl_print_altq_node(dev, node, 0, opts); - } - - while (verbose2 && nodes > 0) { - printf("\n"); - fflush(stdout); - sleep(STAT_INTERVAL); - if ((nodes = pfctl_update_qstats(dev, &root)) == -1) - return (-1); - for (node = root; node != NULL; node = node->next) { - if (iface != NULL && strcmp(node->altq.ifname, iface)) - continue; -#ifdef __FreeBSD__ - if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) - continue; -#endif - pfctl_print_altq_node(dev, node, 0, opts); - } - } - pfctl_free_altq_node(root); - return (0); -} - -int -pfctl_update_qstats(int dev, struct pf_altq_node **root) -{ - struct pf_altq_node *node; - struct pfioc_altq pa; - struct pfioc_qstats pq; - u_int32_t mnr, nr; - struct queue_stats qstats; - static u_int32_t last_ticket; - - memset(&pa, 0, sizeof(pa)); - memset(&pq, 0, sizeof(pq)); - memset(&qstats, 0, sizeof(qstats)); - if (ioctl(dev, DIOCGETALTQS, &pa)) { - warn("DIOCGETALTQS"); - return (-1); - } - - /* if a new set is found, start over */ - if (pa.ticket != last_ticket && *root != NULL) { - pfctl_free_altq_node(*root); - *root = NULL; - } - last_ticket = pa.ticket; - - mnr = pa.nr; - for (nr = 0; nr < mnr; ++nr) { - pa.nr = nr; - if (ioctl(dev, DIOCGETALTQ, &pa)) { - warn("DIOCGETALTQ"); - return (-1); - } -#ifdef __FreeBSD__ - if (pa.altq.qid > 0 && - !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { -#else - if (pa.altq.qid > 0) { -#endif - pq.nr = nr; - pq.ticket = pa.ticket; - pq.buf = &qstats.data; - pq.nbytes = sizeof(qstats.data); - if (ioctl(dev, DIOCGETQSTATS, &pq)) { - warn("DIOCGETQSTATS"); - return (-1); - } - if ((node = pfctl_find_altq_node(*root, pa.altq.qname, - pa.altq.ifname)) != NULL) { - memcpy(&node->qstats.data, &qstats.data, - sizeof(qstats.data)); - update_avg(node); - } else { - pfctl_insert_altq_node(root, pa.altq, qstats); - } - } -#ifdef __FreeBSD__ - else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) { - memset(&qstats.data, 0, sizeof(qstats.data)); - if ((node = pfctl_find_altq_node(*root, pa.altq.qname, - pa.altq.ifname)) != NULL) { - memcpy(&node->qstats.data, &qstats.data, - sizeof(qstats.data)); - update_avg(node); - } else { - pfctl_insert_altq_node(root, pa.altq, qstats); - } - } -#endif - } - return (mnr); -} - -void -pfctl_insert_altq_node(struct pf_altq_node **root, - const struct pf_altq altq, const struct queue_stats qstats) -{ - struct pf_altq_node *node; - - node = calloc(1, sizeof(struct pf_altq_node)); - if (node == NULL) - err(1, "pfctl_insert_altq_node: calloc"); - memcpy(&node->altq, &altq, sizeof(struct pf_altq)); - memcpy(&node->qstats, &qstats, sizeof(qstats)); - node->next = node->children = NULL; - - if (*root == NULL) - *root = node; - else if (!altq.parent[0]) { - struct pf_altq_node *prev = *root; - - while (prev->next != NULL) - prev = prev->next; - prev->next = node; - } else { - struct pf_altq_node *parent; - - parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); - if (parent == NULL) - errx(1, "parent %s not found", altq.parent); - if (parent->children == NULL) - parent->children = node; - else { - struct pf_altq_node *prev = parent->children; - - while (prev->next != NULL) - prev = prev->next; - prev->next = node; - } - } - update_avg(node); -} - -struct pf_altq_node * -pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, - const char *ifname) -{ - struct pf_altq_node *node, *child; - - for (node = root; node != NULL; node = node->next) { - if (!strcmp(node->altq.qname, qname) - && !(strcmp(node->altq.ifname, ifname))) - return (node); - if (node->children != NULL) { - child = pfctl_find_altq_node(node->children, qname, - ifname); - if (child != NULL) - return (child); - } - } - return (NULL); -} - -void -pfctl_print_altq_node(int dev, const struct pf_altq_node *node, - unsigned int level, int opts) -{ - const struct pf_altq_node *child; - - if (node == NULL) - return; - - print_altq(&node->altq, level, NULL, NULL); - - if (node->children != NULL) { - printf("{"); - for (child = node->children; child != NULL; - child = child->next) { - printf("%s", child->altq.qname); - if (child->next != NULL) - printf(", "); - } - printf("}"); - } - printf("\n"); - - if (opts & PF_OPT_VERBOSE) - pfctl_print_altq_nodestat(dev, node); - - if (opts & PF_OPT_DEBUG) - printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", - node->altq.qid, node->altq.ifname, - rate2str((double)(node->altq.ifbandwidth))); - - for (child = node->children; child != NULL; - child = child->next) - pfctl_print_altq_node(dev, child, level + 1, opts); -} - -void -pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) -{ - if (a->altq.qid == 0) - return; - -#ifdef __FreeBSD__ - if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) - return; -#endif - switch (a->altq.scheduler) { - case ALTQT_CBQ: - print_cbqstats(a->qstats); - break; - case ALTQT_PRIQ: - print_priqstats(a->qstats); - break; - case ALTQT_HFSC: - print_hfscstats(a->qstats); - break; - } -} - -void -print_cbqstats(struct queue_stats cur) -{ - printf(" [ pkts: %10llu bytes: %10llu " - "dropped pkts: %6llu bytes: %6llu ]\n", - (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, - (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, - (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, - (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); - printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", - cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, - cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); - - if (cur.avgn < 2) - return; - - printf(" [ measured: %7.1f packets/s, %s/s ]\n", - cur.avg_packets / STAT_INTERVAL, - rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); -} - -void -print_priqstats(struct queue_stats cur) -{ - printf(" [ pkts: %10llu bytes: %10llu " - "dropped pkts: %6llu bytes: %6llu ]\n", - (unsigned long long)cur.data.priq_stats.xmitcnt.packets, - (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, - (unsigned long long)cur.data.priq_stats.dropcnt.packets, - (unsigned long long)cur.data.priq_stats.dropcnt.bytes); - printf(" [ qlength: %3d/%3d ]\n", - cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); - - if (cur.avgn < 2) - return; - - printf(" [ measured: %7.1f packets/s, %s/s ]\n", - cur.avg_packets / STAT_INTERVAL, - rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); -} - -void -print_hfscstats(struct queue_stats cur) -{ - printf(" [ pkts: %10llu bytes: %10llu " - "dropped pkts: %6llu bytes: %6llu ]\n", - (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, - (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, - (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, - (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); - printf(" [ qlength: %3d/%3d ]\n", - cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); - - if (cur.avgn < 2) - return; - - printf(" [ measured: %7.1f packets/s, %s/s ]\n", - cur.avg_packets / STAT_INTERVAL, - rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); -} - -void -pfctl_free_altq_node(struct pf_altq_node *node) -{ - while (node != NULL) { - struct pf_altq_node *prev; - - if (node->children != NULL) - pfctl_free_altq_node(node->children); - prev = node; - node = node->next; - free(prev); - } -} - -void -update_avg(struct pf_altq_node *a) -{ - struct queue_stats *qs; - u_int64_t b, p; - int n; - - if (a->altq.qid == 0) - return; - - qs = &a->qstats; - n = qs->avgn; - - switch (a->altq.scheduler) { - case ALTQT_CBQ: - b = qs->data.cbq_stats.xmit_cnt.bytes; - p = qs->data.cbq_stats.xmit_cnt.packets; - break; - case ALTQT_PRIQ: - b = qs->data.priq_stats.xmitcnt.bytes; - p = qs->data.priq_stats.xmitcnt.packets; - break; - case ALTQT_HFSC: - b = qs->data.hfsc_stats.xmit_cnt.bytes; - p = qs->data.hfsc_stats.xmit_cnt.packets; - break; - default: - b = 0; - p = 0; - break; - } - - if (n == 0) { - qs->prev_bytes = b; - qs->prev_packets = p; - qs->avgn++; - return; - } - - if (b >= qs->prev_bytes) - qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + - (b - qs->prev_bytes)) / n; - - if (p >= qs->prev_packets) - qs->avg_packets = ((qs->avg_packets * (n - 1)) + - (p - qs->prev_packets)) / n; - - qs->prev_bytes = b; - qs->prev_packets = p; - if (n < AVGN_MAX) - qs->avgn++; -} diff --git a/contrib/pf/pfctl/pfctl_radix.c b/contrib/pf/pfctl/pfctl_radix.c deleted file mode 100644 index 38f16c4b72a97..0000000000000 --- a/contrib/pf/pfctl/pfctl_radix.c +++ /dev/null @@ -1,585 +0,0 @@ -/* $OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */ - -/* - * Copyright (c) 2002 Cedric Berger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "pfctl.h" - -#define BUF_SIZE 256 - -extern int dev; - -static int pfr_next_token(char buf[], FILE *); - - -int -pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) -{ - struct pfioc_table io; - - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - if (ioctl(dev, DIOCRCLRTABLES, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRADDTABLES, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - return (0); -} - -int -pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRDELTABLES, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, - int flags) -{ - struct pfioc_table io; - - if (size == NULL || *size < 0 || (*size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETTABLES, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, - int flags) -{ - struct pfioc_table io; - - if (size == NULL || *size < 0 || (*size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETTSTATS, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - if (ioctl(dev, DIOCRCLRADDRS, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nadd, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - if (ioctl(dev, DIOCRADDADDRS, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - return (0); -} - -int -pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *ndel, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - if (ioctl(dev, DIOCRDELADDRS, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); -} - -int -pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *size2, int *nadd, int *ndel, int *nchange, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - io.pfrio_size2 = (size2 != NULL) ? *size2 : 0; - if (ioctl(dev, DIOCRSETADDRS, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - if (ndel != NULL) - *ndel = io.pfrio_ndel; - if (nchange != NULL) - *nchange = io.pfrio_nchange; - if (size2 != NULL) - *size2 = io.pfrio_size2; - return (0); -} - -int -pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, - int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size == NULL || *size < 0 || - (*size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETADDRS, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, - int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size == NULL || *size < 0 || - (*size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETASTATS, &io)) - return (-1); - *size = io.pfrio_size; - return (0); -} - -int -pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && !tbl)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRCLRTSTATS, &io)) - return (-1); - if (nzero) - *nzero = io.pfrio_nzero; - return (0); -} - -int -pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nmatch, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - if (ioctl(dev, DIOCRTSTADDRS, &io)) - return (-1); - if (nmatch) - *nmatch = io.pfrio_nmatch; - return (0); -} - -int -pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nadd, int *naddr, int ticket, int flags) -{ - struct pfioc_table io; - - if (tbl == NULL || size < 0 || (size && addr == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - io.pfrio_buffer = addr; - io.pfrio_esize = sizeof(*addr); - io.pfrio_size = size; - io.pfrio_ticket = ticket; - if (ioctl(dev, DIOCRINADEFINE, &io)) - return (-1); - if (nadd != NULL) - *nadd = io.pfrio_nadd; - if (naddr != NULL) - *naddr = io.pfrio_naddr; - return (0); -} - -/* interface management code */ - -int -pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size) -{ - struct pfioc_iface io; - - if (size == NULL || *size < 0 || (*size && buf == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - if (filter != NULL) - if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >= - sizeof(io.pfiio_name)) { - errno = EINVAL; - return (-1); - } - io.pfiio_buffer = buf; - io.pfiio_esize = sizeof(*buf); - io.pfiio_size = *size; - if (ioctl(dev, DIOCIGETIFACES, &io)) - return (-1); - *size = io.pfiio_size; - return (0); -} - -/* buffer management code */ - -size_t buf_esize[PFRB_MAX] = { 0, - sizeof(struct pfr_table), sizeof(struct pfr_tstats), - sizeof(struct pfr_addr), sizeof(struct pfr_astats), - sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e) -}; - -/* - * add one element to the buffer - */ -int -pfr_buf_add(struct pfr_buffer *b, const void *e) -{ - size_t bs; - - if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX || - e == NULL) { - errno = EINVAL; - return (-1); - } - bs = buf_esize[b->pfrb_type]; - if (b->pfrb_size == b->pfrb_msize) - if (pfr_buf_grow(b, 0)) - return (-1); - memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs); - b->pfrb_size++; - return (0); -} - -/* - * return next element of the buffer (or first one if prev is NULL) - * see PFRB_FOREACH macro - */ -void * -pfr_buf_next(struct pfr_buffer *b, const void *prev) -{ - size_t bs; - - if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) - return (NULL); - if (b->pfrb_size == 0) - return (NULL); - if (prev == NULL) - return (b->pfrb_caddr); - bs = buf_esize[b->pfrb_type]; - if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1) - return (NULL); - return (((caddr_t)prev) + bs); -} - -/* - * minsize: - * 0: make the buffer somewhat bigger - * n: make room for "n" entries in the buffer - */ -int -pfr_buf_grow(struct pfr_buffer *b, int minsize) -{ - caddr_t p; - size_t bs; - - if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) { - errno = EINVAL; - return (-1); - } - if (minsize != 0 && minsize <= b->pfrb_msize) - return (0); - bs = buf_esize[b->pfrb_type]; - if (!b->pfrb_msize) { - if (minsize < 64) - minsize = 64; - b->pfrb_caddr = calloc(bs, minsize); - if (b->pfrb_caddr == NULL) - return (-1); - b->pfrb_msize = minsize; - } else { - if (minsize == 0) - minsize = b->pfrb_msize * 2; - if (minsize < 0 || minsize >= SIZE_T_MAX / bs) { - /* msize overflow */ - errno = ENOMEM; - return (-1); - } - p = realloc(b->pfrb_caddr, minsize * bs); - if (p == NULL) - return (-1); - bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs); - b->pfrb_caddr = p; - b->pfrb_msize = minsize; - } - return (0); -} - -/* - * reset buffer and free memory. - */ -void -pfr_buf_clear(struct pfr_buffer *b) -{ - if (b == NULL) - return; - if (b->pfrb_caddr != NULL) - free(b->pfrb_caddr); - b->pfrb_caddr = NULL; - b->pfrb_size = b->pfrb_msize = 0; -} - -int -pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, - int (*append_addr)(struct pfr_buffer *, char *, int)) -{ - FILE *fp; - char buf[BUF_SIZE]; - int rv; - - if (file == NULL) - return (0); - if (!strcmp(file, "-")) - fp = stdin; - else { - fp = pfctl_fopen(file, "r"); - if (fp == NULL) - return (-1); - } - while ((rv = pfr_next_token(buf, fp)) == 1) - if (append_addr(b, buf, nonetwork)) { - rv = -1; - break; - } - if (fp != stdin) - fclose(fp); - return (rv); -} - -int -pfr_next_token(char buf[BUF_SIZE], FILE *fp) -{ - static char next_ch = ' '; - int i = 0; - - for (;;) { - /* skip spaces */ - while (isspace(next_ch) && !feof(fp)) - next_ch = fgetc(fp); - /* remove from '#' until end of line */ - if (next_ch == '#') - while (!feof(fp)) { - next_ch = fgetc(fp); - if (next_ch == '\n') - break; - } - else - break; - } - if (feof(fp)) { - next_ch = ' '; - return (0); - } - do { - if (i < BUF_SIZE) - buf[i++] = next_ch; - next_ch = fgetc(fp); - } while (!feof(fp) && !isspace(next_ch)); - if (i >= BUF_SIZE) { - errno = EINVAL; - return (-1); - } - buf[i] = '\0'; - return (1); -} - -char * -pfr_strerror(int errnum) -{ - switch (errnum) { - case ESRCH: - return "Table does not exist"; - case ENOENT: - return "Anchor or Ruleset does not exist"; - default: - return strerror(errnum); - } -} diff --git a/contrib/pf/pfctl/pfctl_table.c b/contrib/pf/pfctl/pfctl_table.c deleted file mode 100644 index f3a1efdd06ba3..0000000000000 --- a/contrib/pf/pfctl/pfctl_table.c +++ /dev/null @@ -1,634 +0,0 @@ -/* $OpenBSD: pfctl_table.c,v 1.67 2008/06/10 20:55:02 mcbride Exp $ */ - -/* - * Copyright (c) 2002 Cedric Berger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pfctl_parser.h" -#include "pfctl.h" - -extern void usage(void); -static int pfctl_table(int, char *[], char *, const char *, char *, - const char *, int); -static void print_table(struct pfr_table *, int, int); -static void print_tstats(struct pfr_tstats *, int); -static int load_addr(struct pfr_buffer *, int, char *[], char *, int); -static void print_addrx(struct pfr_addr *, struct pfr_addr *, int); -static void print_astats(struct pfr_astats *, int); -static void radix_perror(void); -static void xprintf(int, const char *, ...); -static void print_iface(struct pfi_kif *, int); - -static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = { - { "In/Block:", "In/Pass:", "In/XPass:" }, - { "Out/Block:", "Out/Pass:", "Out/XPass:" } -}; - -static const char *istats_text[2][2][2] = { - { { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } }, - { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } } -}; - -#define RVTEST(fct) do { \ - if ((!(opts & PF_OPT_NOACTION) || \ - (opts & PF_OPT_DUMMYACTION)) && \ - (fct)) { \ - radix_perror(); \ - goto _error; \ - } \ - } while (0) - -#define CREATE_TABLE do { \ - table.pfrt_flags |= PFR_TFLAG_PERSIST; \ - if ((!(opts & PF_OPT_NOACTION) || \ - (opts & PF_OPT_DUMMYACTION)) && \ - (pfr_add_tables(&table, 1, &nadd, flags)) && \ - (errno != EPERM)) { \ - radix_perror(); \ - goto _error; \ - } \ - if (nadd) { \ - warn_namespace_collision(table.pfrt_name); \ - xprintf(opts, "%d table created", nadd); \ - if (opts & PF_OPT_NOACTION) \ - return (0); \ - } \ - table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \ - } while(0) - -int -pfctl_clear_tables(const char *anchor, int opts) -{ - return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts); -} - -int -pfctl_show_tables(const char *anchor, int opts) -{ - return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts); -} - -int -pfctl_command_tables(int argc, char *argv[], char *tname, - const char *command, char *file, const char *anchor, int opts) -{ - if (tname == NULL || command == NULL) - usage(); - return pfctl_table(argc, argv, tname, command, file, anchor, opts); -} - -int -pfctl_table(int argc, char *argv[], char *tname, const char *command, - char *file, const char *anchor, int opts) -{ - struct pfr_table table; - struct pfr_buffer b, b2; - struct pfr_addr *a, *a2; - int nadd = 0, ndel = 0, nchange = 0, nzero = 0; - int rv = 0, flags = 0, nmatch = 0; - void *p; - - if (command == NULL) - usage(); - if (opts & PF_OPT_NOACTION) - flags |= PFR_FLAG_DUMMY; - - bzero(&b, sizeof(b)); - bzero(&b2, sizeof(b2)); - bzero(&table, sizeof(table)); - if (tname != NULL) { - if (strlen(tname) >= PF_TABLE_NAME_SIZE) - usage(); - if (strlcpy(table.pfrt_name, tname, - sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name)) - errx(1, "pfctl_table: strlcpy"); - } - if (strlcpy(table.pfrt_anchor, anchor, - sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor)) - errx(1, "pfctl_table: strlcpy"); - - if (!strcmp(command, "-F")) { - if (argc || file != NULL) - usage(); - RVTEST(pfr_clr_tables(&table, &ndel, flags)); - xprintf(opts, "%d tables deleted", ndel); - } else if (!strcmp(command, "-s")) { - b.pfrb_type = (opts & PF_OPT_VERBOSE2) ? - PFRB_TSTATS : PFRB_TABLES; - if (argc || file != NULL) - usage(); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (opts & PF_OPT_VERBOSE2) - RVTEST(pfr_get_tstats(&table, - b.pfrb_caddr, &b.pfrb_size, flags)); - else - RVTEST(pfr_get_tables(&table, - b.pfrb_caddr, &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - - if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0) - pfctl_print_title("TABLES:"); - - PFRB_FOREACH(p, &b) - if (opts & PF_OPT_VERBOSE2) - print_tstats(p, opts & PF_OPT_DEBUG); - else - print_table(p, opts & PF_OPT_VERBOSE, - opts & PF_OPT_DEBUG); - } else if (!strcmp(command, "kill")) { - if (argc || file != NULL) - usage(); - RVTEST(pfr_del_tables(&table, 1, &ndel, flags)); - xprintf(opts, "%d table deleted", ndel); - } else if (!strcmp(command, "flush")) { - if (argc || file != NULL) - usage(); - RVTEST(pfr_clr_addrs(&table, &ndel, flags)); - xprintf(opts, "%d addresses deleted", ndel); - } else if (!strcmp(command, "add")) { - b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) - goto _error; - CREATE_TABLE; - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &nadd, flags)); - xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "delete")) { - b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) - goto _error; - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &ndel, flags)); - xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "replace")) { - b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) - goto _error; - CREATE_TABLE; - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - for (;;) { - int sz2 = b.pfrb_msize; - - RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &sz2, &nadd, &ndel, &nchange, flags)); - if (sz2 <= b.pfrb_msize) { - b.pfrb_size = sz2; - break; - } else - pfr_buf_grow(&b, sz2); - } - if (nadd) - xprintf(opts, "%d addresses added", nadd); - if (ndel) - xprintf(opts, "%d addresses deleted", ndel); - if (nchange) - xprintf(opts, "%d addresses changed", nchange); - if (!nadd && !ndel && !nchange) - xprintf(opts, "no changes"); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "expire")) { - const char *errstr; - u_int lifetime; - - b.pfrb_type = PFRB_ASTATS; - b2.pfrb_type = PFRB_ADDRS; - if (argc != 1 || file != NULL) - usage(); - lifetime = strtonum(*argv, 0, UINT_MAX, &errstr); - if (errstr) - errx(1, "expiry time: %s", errstr); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - RVTEST(pfr_get_astats(&table, b.pfrb_caddr, - &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - PFRB_FOREACH(p, &b) { - ((struct pfr_astats *)p)->pfras_a.pfra_fback = 0; - if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero > - lifetime) - if (pfr_buf_add(&b2, - &((struct pfr_astats *)p)->pfras_a)) - err(1, "duplicate buffer"); - } - - if (opts & PF_OPT_VERBOSE) - flags |= PFR_FLAG_FEEDBACK; - RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size, - &ndel, flags)); - xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size); - if (opts & PF_OPT_VERBOSE) - PFRB_FOREACH(a, &b2) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "show")) { - b.pfrb_type = (opts & PF_OPT_VERBOSE) ? - PFRB_ASTATS : PFRB_ADDRS; - if (argc || file != NULL) - usage(); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (opts & PF_OPT_VERBOSE) - RVTEST(pfr_get_astats(&table, b.pfrb_caddr, - &b.pfrb_size, flags)); - else - RVTEST(pfr_get_addrs(&table, b.pfrb_caddr, - &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - PFRB_FOREACH(p, &b) - if (opts & PF_OPT_VERBOSE) - print_astats(p, opts & PF_OPT_USEDNS); - else - print_addrx(p, NULL, opts & PF_OPT_USEDNS); - } else if (!strcmp(command, "test")) { - b.pfrb_type = PFRB_ADDRS; - b2.pfrb_type = PFRB_ADDRS; - - if (load_addr(&b, argc, argv, file, 1)) - goto _error; - if (opts & PF_OPT_VERBOSE2) { - flags |= PFR_FLAG_REPLACE; - PFRB_FOREACH(a, &b) - if (pfr_buf_add(&b2, a)) - err(1, "duplicate buffer"); - } - RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size, - &nmatch, flags)); - xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size); - if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2)) - PFRB_FOREACH(a, &b) - if (a->pfra_fback == PFR_FB_MATCH) - print_addrx(a, NULL, - opts & PF_OPT_USEDNS); - if (opts & PF_OPT_VERBOSE2) { - a2 = NULL; - PFRB_FOREACH(a, &b) { - a2 = pfr_buf_next(&b2, a2); - print_addrx(a2, a, opts & PF_OPT_USEDNS); - } - } - if (nmatch < b.pfrb_size) - rv = 2; - } else if (!strcmp(command, "zero")) { - if (argc || file != NULL) - usage(); - flags |= PFR_FLAG_ADDRSTOO; - RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags)); - xprintf(opts, "%d table/stats cleared", nzero); - } else - warnx("pfctl_table: unknown command '%s'", command); - goto _cleanup; - -_error: - rv = -1; -_cleanup: - pfr_buf_clear(&b); - pfr_buf_clear(&b2); - return (rv); -} - -void -print_table(struct pfr_table *ta, int verbose, int debug) -{ - if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE)) - return; - if (verbose) { - printf("%c%c%c%c%c%c%c\t%s", - (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-', - (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-', - (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-', - (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-', - (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-', - (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-', - (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-', - ta->pfrt_name); - if (ta->pfrt_anchor[0]) - printf("\t%s", ta->pfrt_anchor); - puts(""); - } else - puts(ta->pfrt_name); -} - -void -print_tstats(struct pfr_tstats *ts, int debug) -{ - time_t time = ts->pfrts_tzero; - int dir, op; - - if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE)) - return; - print_table(&ts->pfrts_t, 1, debug); - printf("\tAddresses: %d\n", ts->pfrts_cnt); - printf("\tCleared: %s", ctime(&time)); - printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n", - ts->pfrts_refcnt[PFR_REFCNT_ANCHOR], - ts->pfrts_refcnt[PFR_REFCNT_RULE]); - printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n", - (unsigned long long)ts->pfrts_nomatch, - (unsigned long long)ts->pfrts_match); - for (dir = 0; dir < PFR_DIR_MAX; dir++) - for (op = 0; op < PFR_OP_TABLE_MAX; op++) - printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", - stats_text[dir][op], - (unsigned long long)ts->pfrts_packets[dir][op], - (unsigned long long)ts->pfrts_bytes[dir][op]); -} - -int -load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file, - int nonetwork) -{ - while (argc--) - if (append_addr(b, *argv++, nonetwork)) { - if (errno) - warn("cannot decode %s", argv[-1]); - return (-1); - } - if (pfr_buf_load(b, file, nonetwork, append_addr)) { - warn("cannot load %s", file); - return (-1); - } - return (0); -} - -void -print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns) -{ - char ch, buf[256] = "{error}"; - char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' }; - unsigned int fback, hostnet; - - fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback; - ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?'; - hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32; - inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf)); - printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf); - if (ad->pfra_net < hostnet) - printf("/%d", ad->pfra_net); - if (rad != NULL && fback != PFR_FB_NONE) { - if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf)) - errx(1, "print_addrx: strlcpy"); - inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf)); - printf("\t%c%s", (rad->pfra_not?'!':' '), buf); - if (rad->pfra_net < hostnet) - printf("/%d", rad->pfra_net); - } - if (rad != NULL && fback == PFR_FB_NONE) - printf("\t nomatch"); - if (dns && ad->pfra_net == hostnet) { - char host[NI_MAXHOST]; - union sockaddr_union sa; - - strlcpy(host, "?", sizeof(host)); - bzero(&sa, sizeof(sa)); - sa.sa.sa_family = ad->pfra_af; - if (sa.sa.sa_family == AF_INET) { - sa.sa.sa_len = sizeof(sa.sin); - sa.sin.sin_addr = ad->pfra_ip4addr; - } else { - sa.sa.sa_len = sizeof(sa.sin6); - sa.sin6.sin6_addr = ad->pfra_ip6addr; - } - if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host), - NULL, 0, NI_NAMEREQD) == 0) - printf("\t(%s)", host); - } - printf("\n"); -} - -void -print_astats(struct pfr_astats *as, int dns) -{ - time_t time = as->pfras_tzero; - int dir, op; - - print_addrx(&as->pfras_a, NULL, dns); - printf("\tCleared: %s", ctime(&time)); - if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT) - return; - for (dir = 0; dir < PFR_DIR_MAX; dir++) - for (op = 0; op < PFR_OP_ADDR_MAX; op++) - printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", - stats_text[dir][op], - (unsigned long long)as->pfras_packets[dir][op], - (unsigned long long)as->pfras_bytes[dir][op]); -} - -void -radix_perror(void) -{ - extern char *__progname; - fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno)); -} - -int -pfctl_define_table(char *name, int flags, int addrs, const char *anchor, - struct pfr_buffer *ab, u_int32_t ticket) -{ - struct pfr_table tbl; - - bzero(&tbl, sizeof(tbl)); - if (strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)) >= - sizeof(tbl.pfrt_name) || strlcpy(tbl.pfrt_anchor, anchor, - sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor)) - errx(1, "pfctl_define_table: strlcpy"); - tbl.pfrt_flags = flags; - - return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL, - NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0); -} - -void -warn_namespace_collision(const char *filter) -{ - struct pfr_buffer b; - struct pfr_table *t; - const char *name = NULL, *lastcoll; - int coll = 0; - - bzero(&b, sizeof(b)); - b.pfrb_type = PFRB_TABLES; - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (pfr_get_tables(NULL, b.pfrb_caddr, - &b.pfrb_size, PFR_FLAG_ALLRSETS)) - err(1, "pfr_get_tables"); - if (b.pfrb_size <= b.pfrb_msize) - break; - } - PFRB_FOREACH(t, &b) { - if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE)) - continue; - if (filter != NULL && strcmp(filter, t->pfrt_name)) - continue; - if (!t->pfrt_anchor[0]) - name = t->pfrt_name; - else if (name != NULL && !strcmp(name, t->pfrt_name)) { - coll++; - lastcoll = name; - name = NULL; - } - } - if (coll == 1) - warnx("warning: namespace collision with <%s> global table.", - lastcoll); - else if (coll > 1) - warnx("warning: namespace collisions with %d global tables.", - coll); - pfr_buf_clear(&b); -} - -void -xprintf(int opts, const char *fmt, ...) -{ - va_list args; - - if (opts & PF_OPT_QUIET) - return; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - - if (opts & PF_OPT_DUMMYACTION) - fprintf(stderr, " (dummy).\n"); - else if (opts & PF_OPT_NOACTION) - fprintf(stderr, " (syntax only).\n"); - else - fprintf(stderr, ".\n"); -} - - -/* interface stuff */ - -int -pfctl_show_ifaces(const char *filter, int opts) -{ - struct pfr_buffer b; - struct pfi_kif *p; - int i = 0; - - bzero(&b, sizeof(b)); - b.pfrb_type = PFRB_IFACES; - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) { - radix_perror(); - return (1); - } - if (b.pfrb_size <= b.pfrb_msize) - break; - i++; - } - if (opts & PF_OPT_SHOWALL) - pfctl_print_title("INTERFACES:"); - PFRB_FOREACH(p, &b) - print_iface(p, opts); - return (0); -} - -void -print_iface(struct pfi_kif *p, int opts) -{ - time_t tzero = p->pfik_tzero; - int i, af, dir, act; - - printf("%s", p->pfik_name); - if (opts & PF_OPT_VERBOSE) { - if (p->pfik_flags & PFI_IFLAG_SKIP) - printf(" (skip)"); - } - printf("\n"); - - if (!(opts & PF_OPT_VERBOSE2)) - return; - printf("\tCleared: %s", ctime(&tzero)); - printf("\tReferences: %-18d\n", p->pfik_rulerefs); - for (i = 0; i < 8; i++) { - af = (i>>2) & 1; - dir = (i>>1) &1; - act = i & 1; - printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", - istats_text[af][dir][act], - (unsigned long long)p->pfik_packets[af][dir][act], - (unsigned long long)p->pfik_bytes[af][dir][act]); - } -} diff --git a/include/Makefile b/include/Makefile index d2f6d7f088537..3555831531efd 100644 --- a/include/Makefile +++ b/include/Makefile @@ -186,9 +186,6 @@ copies: ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 *.h \ ${DESTDIR}${INCLUDEDIR}/netinet .endif - cd ${.CURDIR}/../sys/contrib/pf/net; \ - ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 *.h \ - ${DESTDIR}${INCLUDEDIR}/net cd ${.CURDIR}/../sys/crypto; \ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 rijndael/rijndael.h \ ${DESTDIR}${INCLUDEDIR}/crypto @@ -272,11 +269,6 @@ symlinks: ${DESTDIR}${INCLUDEDIR}/netinet; \ done .endif - cd ${.CURDIR}/../sys/contrib/pf/net; \ - for h in *.h; do \ - ln -fs ../../../sys/contrib/pf/net/$$h \ - ${DESTDIR}${INCLUDEDIR}/net; \ - done cd ${.CURDIR}/../sys/crypto; \ for h in rijndael/rijndael.h; do \ ln -fs ../../../sys/crypto/$$h \ diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile index 2475baf94818a..26c2258a5dd4d 100644 --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -1,11 +1,10 @@ # $FreeBSD$ -.PATH: ${.CURDIR}/../../contrib/pf/pfctl -.PATH: ${.CURDIR}/../../sys/contrib/pf/net -.PATH: ${.CURDIR}/../../contrib/pf/man +# pf_ruleset.c is shared between kernel and pfctl +.PATH: ${.CURDIR}/../../sys/netpfil/pf PROG= pfctl -MAN= pfctl.8 pf.4 pflog.4 pfsync.4 pf.conf.5 pf.os.5 +MAN= pfctl.8 SRCS = pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c @@ -14,11 +13,8 @@ SRCS+= pf_ruleset.c WARNS?= 2 CFLAGS+= -Wall -Wmissing-prototypes -Wno-uninitialized -CFLAGS+= -Wstrict-prototypes -I${.CURDIR}/../../contrib/pf/pfctl - -# XXX ALTQ -CFLAGS+= -DENABLE_ALTQ -#CFLAGS+= -I${.CURDIR}/missing +CFLAGS+= -Wstrict-prototypes +CFLAGS+= -DENABLE_ALTQ -I${.CURDIR} YFLAGS= diff --git a/sbin/pfctl/missing/altq/altq.h b/sbin/pfctl/missing/altq/altq.h deleted file mode 100644 index c740ed359ad16..0000000000000 --- a/sbin/pfctl/missing/altq/altq.h +++ /dev/null @@ -1,204 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq.h,v 1.10 2003/07/10 12:07:47 kjc Exp $ */ - -/* - * Copyright (C) 1998-2003 - * Sony Computer Science Laboratories Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#ifndef _ALTQ_ALTQ_H_ -#define _ALTQ_ALTQ_H_ - -#if 0 -/* - * allow altq-3 (altqd(8) and /dev/altq) to coexist with the new pf-based altq. - * altq3 is mainly for research experiments. pf-based altq is for daily use. - */ -#define ALTQ3_COMPAT /* for compatibility with altq-3 */ -#define ALTQ3_CLFIER_COMPAT /* for compatibility with altq-3 classifier */ -#endif - -#ifdef ALTQ3_COMPAT -#include -#include -#include -#include - -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif -#endif /* ALTQ3_COMPAT */ - -/* altq discipline type */ -#define ALTQT_NONE 0 /* reserved */ -#define ALTQT_CBQ 1 /* cbq */ -#define ALTQT_WFQ 2 /* wfq */ -#define ALTQT_AFMAP 3 /* afmap */ -#define ALTQT_FIFOQ 4 /* fifoq */ -#define ALTQT_RED 5 /* red */ -#define ALTQT_RIO 6 /* rio */ -#define ALTQT_LOCALQ 7 /* local use */ -#define ALTQT_HFSC 8 /* hfsc */ -#define ALTQT_CDNR 9 /* traffic conditioner */ -#define ALTQT_BLUE 10 /* blue */ -#define ALTQT_PRIQ 11 /* priority queue */ -#define ALTQT_JOBS 12 /* JoBS */ -#define ALTQT_MAX 13 /* should be max discipline type + 1 */ - -#ifdef ALTQ3_COMPAT -struct altqreq { - char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ - u_long arg; /* request-specific argument */ -}; -#endif - -/* simple token backet meter profile */ -struct tb_profile { - u_int rate; /* rate in bit-per-sec */ - u_int depth; /* depth in bytes */ -}; - -#ifdef ALTQ3_COMPAT -struct tbrreq { - char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ - struct tb_profile tb_prof; /* token bucket profile */ -}; - -#ifdef ALTQ3_CLFIER_COMPAT -/* - * common network flow info structure - */ -struct flowinfo { - u_char fi_len; /* total length */ - u_char fi_family; /* address family */ - u_int8_t fi_data[46]; /* actually longer; address family - specific flow info. */ -}; - -/* - * flow info structure for internet protocol family. - * (currently this is the only protocol family supported) - */ -struct flowinfo_in { - u_char fi_len; /* sizeof(struct flowinfo_in) */ - u_char fi_family; /* AF_INET */ - u_int8_t fi_proto; /* IPPROTO_XXX */ - u_int8_t fi_tos; /* type-of-service */ - struct in_addr fi_dst; /* dest address */ - struct in_addr fi_src; /* src address */ - u_int16_t fi_dport; /* dest port */ - u_int16_t fi_sport; /* src port */ - u_int32_t fi_gpi; /* generalized port id for ipsec */ - u_int8_t _pad[28]; /* make the size equal to - flowinfo_in6 */ -}; - -#ifdef SIN6_LEN -struct flowinfo_in6 { - u_char fi6_len; /* sizeof(struct flowinfo_in6) */ - u_char fi6_family; /* AF_INET6 */ - u_int8_t fi6_proto; /* IPPROTO_XXX */ - u_int8_t fi6_tclass; /* traffic class */ - u_int32_t fi6_flowlabel; /* ipv6 flowlabel */ - u_int16_t fi6_dport; /* dest port */ - u_int16_t fi6_sport; /* src port */ - u_int32_t fi6_gpi; /* generalized port id */ - struct in6_addr fi6_dst; /* dest address */ - struct in6_addr fi6_src; /* src address */ -}; -#endif /* INET6 */ - -/* - * flow filters for AF_INET and AF_INET6 - */ -struct flow_filter { - int ff_ruleno; - struct flowinfo_in ff_flow; - struct { - struct in_addr mask_dst; - struct in_addr mask_src; - u_int8_t mask_tos; - u_int8_t _pad[3]; - } ff_mask; - u_int8_t _pad2[24]; /* make the size equal to flow_filter6 */ -}; - -#ifdef SIN6_LEN -struct flow_filter6 { - int ff_ruleno; - struct flowinfo_in6 ff_flow6; - struct { - struct in6_addr mask6_dst; - struct in6_addr mask6_src; - u_int8_t mask6_tclass; - u_int8_t _pad[3]; - } ff_mask6; -}; -#endif /* INET6 */ -#endif /* ALTQ3_CLFIER_COMPAT */ -#endif /* ALTQ3_COMPAT */ - -/* - * generic packet counter - */ -struct pktcntr { - u_int64_t packets; - u_int64_t bytes; -}; - -#define PKTCNTR_ADD(cntr, len) \ - do { (cntr)->packets++; (cntr)->bytes += len; } while (/*CONSTCOND*/ 0) - -#ifdef ALTQ3_COMPAT -/* - * altq related ioctls - */ -#define ALTQGTYPE _IOWR('q', 0, struct altqreq) /* get queue type */ -#if 0 -/* - * these ioctls are currently discipline-specific but could be shared - * in the future. - */ -#define ALTQATTACH _IOW('q', 1, struct altqreq) /* attach discipline */ -#define ALTQDETACH _IOW('q', 2, struct altqreq) /* detach discipline */ -#define ALTQENABLE _IOW('q', 3, struct altqreq) /* enable discipline */ -#define ALTQDISABLE _IOW('q', 4, struct altqreq) /* disable discipline*/ -#define ALTQCLEAR _IOW('q', 5, struct altqreq) /* (re)initialize */ -#define ALTQCONFIG _IOWR('q', 6, struct altqreq) /* set config params */ -#define ALTQADDCLASS _IOWR('q', 7, struct altqreq) /* add a class */ -#define ALTQMODCLASS _IOWR('q', 8, struct altqreq) /* modify a class */ -#define ALTQDELCLASS _IOWR('q', 9, struct altqreq) /* delete a class */ -#define ALTQADDFILTER _IOWR('q', 10, struct altqreq) /* add a filter */ -#define ALTQDELFILTER _IOWR('q', 11, struct altqreq) /* delete a filter */ -#define ALTQGETSTATS _IOWR('q', 12, struct altqreq) /* get statistics */ -#define ALTQGETCNTR _IOWR('q', 13, struct altqreq) /* get a pkt counter */ -#endif /* 0 */ -#define ALTQTBRSET _IOW('q', 14, struct tbrreq) /* set tb regulator */ -#define ALTQTBRGET _IOWR('q', 15, struct tbrreq) /* get tb regulator */ -#endif /* ALTQ3_COMPAT */ - -#ifdef _KERNEL -#include -#endif - -#endif /* _ALTQ_ALTQ_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_cbq.h b/sbin/pfctl/missing/altq/altq_cbq.h deleted file mode 100644 index 3c41c6bd836f5..0000000000000 --- a/sbin/pfctl/missing/altq/altq_cbq.h +++ /dev/null @@ -1,232 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_cbq.h,v 1.10 2003/08/20 23:30:23 itojun Exp $ */ - -/* - * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the SMCC Technology - * Development Group at Sun Microsystems, Inc. - * - * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE - * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is - * provided "as is" without express or implied warranty of any kind. - * - * These notices must be retained in any copies of any part of this software. - */ - -#ifndef _ALTQ_ALTQ_CBQ_H_ -#define _ALTQ_ALTQ_CBQ_H_ - -#include -#include -#include -#include - -/* #pragma ident "@(#)cbq.h 1.18 98/05/13 SMI" */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Define a well known class handles - */ -#define NULL_CLASS_HANDLE 0xffffffff -#define ROOT_CLASS_HANDLE 0xfffffffe -#define DEFAULT_CLASS_HANDLE 0xfffffffd -#ifdef ALTQ3_COMPAT -#define CTL_CLASS_HANDLE 0xfffffffc -#endif - -/* class flags shoud be same as class flags in rm_class.h */ -#define CBQCLF_RED 0x0001 /* use RED */ -#define CBQCLF_ECN 0x0002 /* use RED/ECN */ -#define CBQCLF_RIO 0x0004 /* use RIO */ -#define CBQCLF_FLOWVALVE 0x0008 /* use flowvalve (aka penalty-box) */ -#define CBQCLF_CLEARDSCP 0x0010 /* clear diffserv codepoint */ -#define CBQCLF_BORROW 0x0020 /* borrow from parent */ - -/* class flags only for root class */ -#define CBQCLF_WRR 0x0100 /* weighted-round robin */ -#define CBQCLF_EFFICIENT 0x0200 /* work-conserving */ - -/* class flags for special classes */ -#define CBQCLF_ROOTCLASS 0x1000 /* root class */ -#define CBQCLF_DEFCLASS 0x2000 /* default class */ -#ifdef ALTQ3_COMPAT -#define CBQCLF_CTLCLASS 0x4000 /* control class */ -#endif -#define CBQCLF_CLASSMASK 0xf000 /* class mask */ - -#define CBQ_MAXQSIZE 200 -#define CBQ_MAXPRI RM_MAXPRIO - -typedef struct _cbq_class_stats_ { - u_int32_t handle; - u_int depth; - - struct pktcntr xmit_cnt; /* packets sent in this class */ - struct pktcntr drop_cnt; /* dropped packets */ - u_int over; /* # times went over limit */ - u_int borrows; /* # times tried to borrow */ - u_int overactions; /* # times invoked overlimit action */ - u_int delays; /* # times invoked delay actions */ - - /* other static class parameters useful for debugging */ - int priority; - int maxidle; - int minidle; - int offtime; - int qmax; - int ns_per_byte; - int wrr_allot; - - int qcnt; /* # packets in queue */ - int avgidle; - - /* red and rio related info */ - int qtype; - struct redstats red[3]; -} class_stats_t; - -#ifdef ALTQ3_COMPAT -/* - * Define structures associated with IOCTLS for cbq. - */ - -/* - * Define the CBQ interface structure. This must be included in all - * IOCTL's such that the CBQ driver may find the appropriate CBQ module - * associated with the network interface to be affected. - */ -struct cbq_interface { - char cbq_ifacename[IFNAMSIZ]; -}; - -typedef struct cbq_class_spec { - u_int priority; - u_int nano_sec_per_byte; - u_int maxq; - u_int maxidle; - int minidle; - u_int offtime; - u_int32_t parent_class_handle; - u_int32_t borrow_class_handle; - - u_int pktsize; - int flags; -} cbq_class_spec_t; - -struct cbq_add_class { - struct cbq_interface cbq_iface; - - cbq_class_spec_t cbq_class; - u_int32_t cbq_class_handle; -}; - -struct cbq_delete_class { - struct cbq_interface cbq_iface; - u_int32_t cbq_class_handle; -}; - -struct cbq_modify_class { - struct cbq_interface cbq_iface; - - cbq_class_spec_t cbq_class; - u_int32_t cbq_class_handle; -}; - -struct cbq_add_filter { - struct cbq_interface cbq_iface; - u_int32_t cbq_class_handle; - struct flow_filter cbq_filter; - - u_long cbq_filter_handle; -}; - -struct cbq_delete_filter { - struct cbq_interface cbq_iface; - u_long cbq_filter_handle; -}; - -/* number of classes are returned in nclasses field */ -struct cbq_getstats { - struct cbq_interface iface; - int nclasses; - class_stats_t *stats; -}; - -/* - * Define IOCTLs for CBQ. - */ -#define CBQ_IF_ATTACH _IOW('Q', 1, struct cbq_interface) -#define CBQ_IF_DETACH _IOW('Q', 2, struct cbq_interface) -#define CBQ_ENABLE _IOW('Q', 3, struct cbq_interface) -#define CBQ_DISABLE _IOW('Q', 4, struct cbq_interface) -#define CBQ_CLEAR_HIERARCHY _IOW('Q', 5, struct cbq_interface) -#define CBQ_ADD_CLASS _IOWR('Q', 7, struct cbq_add_class) -#define CBQ_DEL_CLASS _IOW('Q', 8, struct cbq_delete_class) -#define CBQ_MODIFY_CLASS _IOWR('Q', 9, struct cbq_modify_class) -#define CBQ_ADD_FILTER _IOWR('Q', 10, struct cbq_add_filter) -#define CBQ_DEL_FILTER _IOW('Q', 11, struct cbq_delete_filter) -#define CBQ_GETSTATS _IOWR('Q', 12, struct cbq_getstats) -#endif /* ALTQ3_COMPAT */ - -#ifdef _KERNEL -/* - * Define macros only good for kernel drivers and modules. - */ -#define CBQ_WATCHDOG (hz / 20) -#define CBQ_TIMEOUT 10 -#define CBQ_LS_TIMEOUT (20 * hz / 1000) - -#define CBQ_MAX_CLASSES 256 - -#ifdef ALTQ3_COMPAT -#define CBQ_MAX_FILTERS 256 - -#define DISABLE 0x00 -#define ENABLE 0x01 -#endif /* ALTQ3_COMPAT */ - -/* - * Define State structures. - */ -typedef struct cbqstate { -#ifdef ALTQ3_COMPAT - struct cbqstate *cbq_next; -#endif - int cbq_qlen; /* # of packets in cbq */ - struct rm_class *cbq_class_tbl[CBQ_MAX_CLASSES]; - - struct rm_ifdat ifnp; - struct callout cbq_callout; /* for timeouts */ -#ifdef ALTQ3_CLFIER_COMPAT - struct acc_classifier cbq_classifier; -#endif -} cbq_state_t; - -#endif /* _KERNEL */ - -#ifdef __cplusplus -} -#endif - -#endif /* !_ALTQ_ALTQ_CBQ_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_classq.h b/sbin/pfctl/missing/altq/altq_classq.h deleted file mode 100644 index 43045b25a6303..0000000000000 --- a/sbin/pfctl/missing/altq/altq_classq.h +++ /dev/null @@ -1,203 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_classq.h,v 1.6 2003/01/07 07:33:38 kjc Exp $ */ - -/* - * Copyright (c) 1991-1997 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor of the Laboratory may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * class queue definitions extracted from rm_class.h. - */ -#ifndef _ALTQ_ALTQ_CLASSQ_H_ -#define _ALTQ_ALTQ_CLASSQ_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Packet Queue types: RED or DROPHEAD. - */ -#define Q_DROPHEAD 0x00 -#define Q_RED 0x01 -#define Q_RIO 0x02 -#define Q_DROPTAIL 0x03 - -#ifdef _KERNEL - -/* - * Packet Queue structures and macros to manipulate them. - */ -struct _class_queue_ { - struct mbuf *tail_; /* Tail of packet queue */ - int qlen_; /* Queue length (in number of packets) */ - int qlim_; /* Queue limit (in number of packets*) */ - int qtype_; /* Queue type */ -}; - -typedef struct _class_queue_ class_queue_t; - -#define qtype(q) (q)->qtype_ /* Get queue type */ -#define qlimit(q) (q)->qlim_ /* Max packets to be queued */ -#define qlen(q) (q)->qlen_ /* Current queue length. */ -#define qtail(q) (q)->tail_ /* Tail of the queue */ -#define qhead(q) ((q)->tail_ ? (q)->tail_->m_nextpkt : NULL) - -#define qempty(q) ((q)->qlen_ == 0) /* Is the queue empty?? */ -#define q_is_red(q) ((q)->qtype_ == Q_RED) /* Is the queue a red queue */ -#define q_is_rio(q) ((q)->qtype_ == Q_RIO) /* Is the queue a rio queue */ -#define q_is_red_or_rio(q) ((q)->qtype_ == Q_RED || (q)->qtype_ == Q_RIO) - -#if !defined(__GNUC__) || defined(ALTQ_DEBUG) - -extern void _addq(class_queue_t *, struct mbuf *); -extern struct mbuf *_getq(class_queue_t *); -extern struct mbuf *_getq_tail(class_queue_t *); -extern struct mbuf *_getq_random(class_queue_t *); -extern void _removeq(class_queue_t *, struct mbuf *); -extern void _flushq(class_queue_t *); - -#else /* __GNUC__ && !ALTQ_DEBUG */ -/* - * inlined versions - */ -static __inline void -_addq(class_queue_t *q, struct mbuf *m) -{ - struct mbuf *m0; - - if ((m0 = qtail(q)) != NULL) - m->m_nextpkt = m0->m_nextpkt; - else - m0 = m; - m0->m_nextpkt = m; - qtail(q) = m; - qlen(q)++; -} - -static __inline struct mbuf * -_getq(class_queue_t *q) -{ - struct mbuf *m, *m0; - - if ((m = qtail(q)) == NULL) - return (NULL); - if ((m0 = m->m_nextpkt) != m) - m->m_nextpkt = m0->m_nextpkt; - else - qtail(q) = NULL; - qlen(q)--; - m0->m_nextpkt = NULL; - return (m0); -} - -/* drop a packet at the tail of the queue */ -static __inline struct mbuf * -_getq_tail(class_queue_t *q) -{ - struct mbuf *m, *m0, *prev; - - if ((m = m0 = qtail(q)) == NULL) - return NULL; - do { - prev = m0; - m0 = m0->m_nextpkt; - } while (m0 != m); - prev->m_nextpkt = m->m_nextpkt; - if (prev == m) - qtail(q) = NULL; - else - qtail(q) = prev; - qlen(q)--; - m->m_nextpkt = NULL; - return (m); -} - -/* randomly select a packet in the queue */ -static __inline struct mbuf * -_getq_random(class_queue_t *q) -{ - struct mbuf *m; - int i, n; - - if ((m = qtail(q)) == NULL) - return NULL; - if (m->m_nextpkt == m) - qtail(q) = NULL; - else { - struct mbuf *prev = NULL; - - n = random() % qlen(q) + 1; - for (i = 0; i < n; i++) { - prev = m; - m = m->m_nextpkt; - } - prev->m_nextpkt = m->m_nextpkt; - if (m == qtail(q)) - qtail(q) = prev; - } - qlen(q)--; - m->m_nextpkt = NULL; - return (m); -} - -static __inline void -_removeq(class_queue_t *q, struct mbuf *m) -{ - struct mbuf *m0, *prev; - - m0 = qtail(q); - do { - prev = m0; - m0 = m0->m_nextpkt; - } while (m0 != m); - prev->m_nextpkt = m->m_nextpkt; - if (prev == m) - qtail(q) = NULL; - else if (qtail(q) == m) - qtail(q) = prev; - qlen(q)--; -} - -static __inline void -_flushq(class_queue_t *q) -{ - struct mbuf *m; - - while ((m = _getq(q)) != NULL) - m_freem(m); -} - -#endif /* __GNUC__ && !ALTQ_DEBUG */ - -#endif /* _KERNEL */ - -#ifdef __cplusplus -} -#endif - -#endif /* _ALTQ_ALTQ_CLASSQ_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_hfsc.h b/sbin/pfctl/missing/altq/altq_hfsc.h deleted file mode 100644 index ac242114c084c..0000000000000 --- a/sbin/pfctl/missing/altq/altq_hfsc.h +++ /dev/null @@ -1,325 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_hfsc.h,v 1.10 2003/07/10 12:07:48 kjc Exp $ */ - -/* - * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this software and - * its documentation is hereby granted (including for commercial or - * for-profit use), provided that both the copyright notice and this - * permission notice appear in all copies of the software, derivative - * works, or modified versions, and any portions thereof, and that - * both notices appear in supporting documentation, and that credit - * is given to Carnegie Mellon University in all publications reporting - * on direct or indirect use of this code or its derivatives. - * - * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF - * WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS - * SOFTWARE IN ITS ``AS IS'' CONDITION, AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * Carnegie Mellon encourages (but does not require) users of this - * software to return any improvements or extensions that they make, - * and to grant Carnegie Mellon the rights to redistribute these - * changes without encumbrance. - */ -#ifndef _ALTQ_ALTQ_HFSC_H_ -#define _ALTQ_ALTQ_HFSC_H_ - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct service_curve { - u_int m1; /* slope of the first segment in bits/sec */ - u_int d; /* the x-projection of the first segment in msec */ - u_int m2; /* slope of the second segment in bits/sec */ -}; - -/* special class handles */ -#define HFSC_NULLCLASS_HANDLE 0 -#define HFSC_ROOTCLASS_HANDLE 1 -#define HFSC_MAX_CLASSES 64 - -/* hfsc class flags */ -#define HFCF_RED 0x0001 /* use RED */ -#define HFCF_ECN 0x0002 /* use RED/ECN */ -#define HFCF_RIO 0x0004 /* use RIO */ -#define HFCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */ -#define HFCF_DEFAULTCLASS 0x1000 /* default class */ - -/* service curve types */ -#define HFSC_REALTIMESC 1 -#define HFSC_LINKSHARINGSC 2 -#define HFSC_UPPERLIMITSC 4 -#define HFSC_DEFAULTSC (HFSC_REALTIMESC|HFSC_LINKSHARINGSC) - -struct hfsc_classstats { - u_int class_id; - u_int32_t class_handle; - struct service_curve rsc; - struct service_curve fsc; - struct service_curve usc; /* upper limit service curve */ - - u_int64_t total; /* total work in bytes */ - u_int64_t cumul; /* cumulative work in bytes - done by real-time criteria */ - u_int64_t d; /* deadline */ - u_int64_t e; /* eligible time */ - u_int64_t vt; /* virtual time */ - u_int64_t f; /* fit time for upper-limit */ - - /* info helpful for debugging */ - u_int64_t initvt; /* init virtual time */ - u_int64_t vtoff; /* cl_vt_ipoff */ - u_int64_t cvtmax; /* cl_maxvt */ - u_int64_t myf; /* cl_myf */ - u_int64_t cfmin; /* cl_mincf */ - u_int64_t cvtmin; /* cl_mincvt */ - u_int64_t myfadj; /* cl_myfadj */ - u_int64_t vtadj; /* cl_vtadj */ - u_int64_t cur_time; - u_int32_t machclk_freq; - - u_int qlength; - u_int qlimit; - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; - u_int period; - - u_int vtperiod; /* vt period sequence no */ - u_int parentperiod; /* parent's vt period seqno */ - int nactive; /* number of active children */ - - /* red and rio related info */ - int qtype; - struct redstats red[3]; -}; - -#ifdef ALTQ3_COMPAT -struct hfsc_interface { - char hfsc_ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */ -}; - -struct hfsc_attach { - struct hfsc_interface iface; - u_int bandwidth; /* link bandwidth in bits/sec */ -}; - -struct hfsc_add_class { - struct hfsc_interface iface; - u_int32_t parent_handle; - struct service_curve service_curve; - int qlimit; - int flags; - - u_int32_t class_handle; /* return value */ -}; - -struct hfsc_delete_class { - struct hfsc_interface iface; - u_int32_t class_handle; -}; - -struct hfsc_modify_class { - struct hfsc_interface iface; - u_int32_t class_handle; - struct service_curve service_curve; - int sctype; -}; - -struct hfsc_add_filter { - struct hfsc_interface iface; - u_int32_t class_handle; - struct flow_filter filter; - - u_long filter_handle; /* return value */ -}; - -struct hfsc_delete_filter { - struct hfsc_interface iface; - u_long filter_handle; -}; - -struct hfsc_class_stats { - struct hfsc_interface iface; - int nskip; /* skip # of classes */ - int nclasses; /* # of class stats (WR) */ - u_int64_t cur_time; /* current time */ - u_int32_t machclk_freq; /* machine clock frequency */ - u_int hif_classes; /* # of classes in the tree */ - u_int hif_packets; /* # of packets in the tree */ - struct hfsc_classstats *stats; /* pointer to stats array */ -}; - -#define HFSC_IF_ATTACH _IOW('Q', 1, struct hfsc_attach) -#define HFSC_IF_DETACH _IOW('Q', 2, struct hfsc_interface) -#define HFSC_ENABLE _IOW('Q', 3, struct hfsc_interface) -#define HFSC_DISABLE _IOW('Q', 4, struct hfsc_interface) -#define HFSC_CLEAR_HIERARCHY _IOW('Q', 5, struct hfsc_interface) -#define HFSC_ADD_CLASS _IOWR('Q', 7, struct hfsc_add_class) -#define HFSC_DEL_CLASS _IOW('Q', 8, struct hfsc_delete_class) -#define HFSC_MOD_CLASS _IOW('Q', 9, struct hfsc_modify_class) -#define HFSC_ADD_FILTER _IOWR('Q', 10, struct hfsc_add_filter) -#define HFSC_DEL_FILTER _IOW('Q', 11, struct hfsc_delete_filter) -#define HFSC_GETSTATS _IOWR('Q', 12, struct hfsc_class_stats) -#endif /* ALTQ3_COMPAT */ - -#ifdef _KERNEL -/* - * kernel internal service curve representation - * coordinates are given by 64 bit unsigned integers. - * x-axis: unit is clock count. for the intel x86 architecture, - * the raw Pentium TSC (Timestamp Counter) value is used. - * virtual time is also calculated in this time scale. - * y-axis: unit is byte. - * - * the service curve parameters are converted to the internal - * representation. - * the slope values are scaled to avoid overflow. - * the inverse slope values as well as the y-projection of the 1st - * segment are kept in order to to avoid 64-bit divide operations - * that are expensive on 32-bit architectures. - * - * note: Intel Pentium TSC never wraps around in several thousands of years. - * x-axis doesn't wrap around for 1089 years with 1GHz clock. - * y-axis doesn't wrap around for 4358 years with 1Gbps bandwidth. - */ - -/* kernel internal representation of a service curve */ -struct internal_sc { - u_int64_t sm1; /* scaled slope of the 1st segment */ - u_int64_t ism1; /* scaled inverse-slope of the 1st segment */ - u_int64_t dx; /* the x-projection of the 1st segment */ - u_int64_t dy; /* the y-projection of the 1st segment */ - u_int64_t sm2; /* scaled slope of the 2nd segment */ - u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */ -}; - -/* runtime service curve */ -struct runtime_sc { - u_int64_t x; /* current starting position on x-axis */ - u_int64_t y; /* current starting position on x-axis */ - u_int64_t sm1; /* scaled slope of the 1st segment */ - u_int64_t ism1; /* scaled inverse-slope of the 1st segment */ - u_int64_t dx; /* the x-projection of the 1st segment */ - u_int64_t dy; /* the y-projection of the 1st segment */ - u_int64_t sm2; /* scaled slope of the 2nd segment */ - u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */ -}; - -/* for TAILQ based ellist and actlist implementation */ -struct hfsc_class; -typedef TAILQ_HEAD(_eligible, hfsc_class) ellist_t; -typedef TAILQ_ENTRY(hfsc_class) elentry_t; -typedef TAILQ_HEAD(_active, hfsc_class) actlist_t; -typedef TAILQ_ENTRY(hfsc_class) actentry_t; -#define ellist_first(s) TAILQ_FIRST(s) -#define actlist_first(s) TAILQ_FIRST(s) -#define actlist_last(s) TAILQ_LAST(s, _active) - -struct hfsc_class { - u_int cl_id; /* class id (just for debug) */ - u_int32_t cl_handle; /* class handle */ - struct hfsc_if *cl_hif; /* back pointer to struct hfsc_if */ - int cl_flags; /* misc flags */ - - struct hfsc_class *cl_parent; /* parent class */ - struct hfsc_class *cl_siblings; /* sibling classes */ - struct hfsc_class *cl_children; /* child classes */ - - class_queue_t *cl_q; /* class queue structure */ - struct red *cl_red; /* RED state */ - struct altq_pktattr *cl_pktattr; /* saved header used by ECN */ - - u_int64_t cl_total; /* total work in bytes */ - u_int64_t cl_cumul; /* cumulative work in bytes - done by real-time criteria */ - u_int64_t cl_d; /* deadline */ - u_int64_t cl_e; /* eligible time */ - u_int64_t cl_vt; /* virtual time */ - u_int64_t cl_f; /* time when this class will fit for - link-sharing, max(myf, cfmin) */ - u_int64_t cl_myf; /* my fit-time (as calculated from this - class's own upperlimit curve) */ - u_int64_t cl_myfadj; /* my fit-time adjustment - (to cancel history dependence) */ - u_int64_t cl_cfmin; /* earliest children's fit-time (used - with cl_myf to obtain cl_f) */ - u_int64_t cl_cvtmin; /* minimal virtual time among the - children fit for link-sharing - (monotonic within a period) */ - u_int64_t cl_vtadj; /* intra-period cumulative vt - adjustment */ - u_int64_t cl_vtoff; /* inter-period cumulative vt offset */ - u_int64_t cl_cvtmax; /* max child's vt in the last period */ - - u_int64_t cl_initvt; /* init virtual time (for debugging) */ - - struct internal_sc *cl_rsc; /* internal real-time service curve */ - struct internal_sc *cl_fsc; /* internal fair service curve */ - struct internal_sc *cl_usc; /* internal upperlimit service curve */ - struct runtime_sc cl_deadline; /* deadline curve */ - struct runtime_sc cl_eligible; /* eligible curve */ - struct runtime_sc cl_virtual; /* virtual curve */ - struct runtime_sc cl_ulimit; /* upperlimit curve */ - - u_int cl_vtperiod; /* vt period sequence no */ - u_int cl_parentperiod; /* parent's vt period seqno */ - int cl_nactive; /* number of active children */ - actlist_t *cl_actc; /* active children list */ - - actentry_t cl_actlist; /* active children list entry */ - elentry_t cl_ellist; /* eligible list entry */ - - struct { - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; - u_int period; - } cl_stats; -}; - -/* - * hfsc interface state - */ -struct hfsc_if { - struct hfsc_if *hif_next; /* interface state list */ - struct ifaltq *hif_ifq; /* backpointer to ifaltq */ - struct hfsc_class *hif_rootclass; /* root class */ - struct hfsc_class *hif_defaultclass; /* default class */ - struct hfsc_class *hif_class_tbl[HFSC_MAX_CLASSES]; - struct hfsc_class *hif_pollcache; /* cache for poll operation */ - - u_int hif_classes; /* # of classes in the tree */ - u_int hif_packets; /* # of packets in the tree */ - u_int hif_classid; /* class id sequence number */ - - ellist_t *hif_eligible; /* eligible list */ - -#ifdef ALTQ3_CLFIER_COMPAT - struct acc_classifier hif_classifier; -#endif -}; - -#endif /* _KERNEL */ - -#ifdef __cplusplus -} -#endif - -#endif /* _ALTQ_ALTQ_HFSC_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_priq.h b/sbin/pfctl/missing/altq/altq_priq.h deleted file mode 100644 index 526b09235e397..0000000000000 --- a/sbin/pfctl/missing/altq/altq_priq.h +++ /dev/null @@ -1,172 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_priq.h,v 1.5 2003/07/10 12:07:48 kjc Exp $ */ -/* - * Copyright (C) 2000-2003 - * Sony Computer Science Laboratories Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _ALTQ_ALTQ_PRIQ_H_ -#define _ALTQ_ALTQ_PRIQ_H_ - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define PRIQ_MAXPRI 16 /* upper limit of the number of priorities */ -#define PRIQ_MAXQID 256 /* upper limit of queues */ - -#ifdef ALTQ3_COMPAT -struct priq_interface { - char ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */ - u_long arg; /* request-specific argument */ -}; - -struct priq_add_class { - struct priq_interface iface; - int pri; /* priority (0 is the lowest) */ - int qlimit; /* queue size limit */ - int flags; /* misc flags (see below) */ - - u_int32_t class_handle; /* return value */ -}; -#endif /* ALTQ3_COMPAT */ - -/* priq class flags */ -#define PRCF_RED 0x0001 /* use RED */ -#define PRCF_ECN 0x0002 /* use RED/ECN */ -#define PRCF_RIO 0x0004 /* use RIO */ -#define PRCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */ -#define PRCF_DEFAULTCLASS 0x1000 /* default class */ - -/* special class handles */ -#define PRIQ_NULLCLASS_HANDLE 0 - -#ifdef ALTQ3_COMPAT -struct priq_delete_class { - struct priq_interface iface; - u_int32_t class_handle; -}; - -struct priq_modify_class { - struct priq_interface iface; - u_int32_t class_handle; - int pri; - int qlimit; - int flags; -}; - -struct priq_add_filter { - struct priq_interface iface; - u_int32_t class_handle; - struct flow_filter filter; - - u_long filter_handle; /* return value */ -}; - -struct priq_delete_filter { - struct priq_interface iface; - u_long filter_handle; -}; -#endif /* ALTQ3_COMPAT */ - -struct priq_classstats { - u_int32_t class_handle; - - u_int qlength; - u_int qlimit; - u_int period; - struct pktcntr xmitcnt; /* transmitted packet counter */ - struct pktcntr dropcnt; /* dropped packet counter */ - - /* red and rio related info */ - int qtype; - struct redstats red[3]; /* rio has 3 red stats */ -}; - -#ifdef ALTQ3_COMPAT -struct priq_class_stats { - struct priq_interface iface; - int maxpri; /* in/out */ - - struct priq_classstats *stats; /* pointer to stats array */ -}; - -#define PRIQ_IF_ATTACH _IOW('Q', 1, struct priq_interface) -#define PRIQ_IF_DETACH _IOW('Q', 2, struct priq_interface) -#define PRIQ_ENABLE _IOW('Q', 3, struct priq_interface) -#define PRIQ_DISABLE _IOW('Q', 4, struct priq_interface) -#define PRIQ_CLEAR _IOW('Q', 5, struct priq_interface) -#define PRIQ_ADD_CLASS _IOWR('Q', 7, struct priq_add_class) -#define PRIQ_DEL_CLASS _IOW('Q', 8, struct priq_delete_class) -#define PRIQ_MOD_CLASS _IOW('Q', 9, struct priq_modify_class) -#define PRIQ_ADD_FILTER _IOWR('Q', 10, struct priq_add_filter) -#define PRIQ_DEL_FILTER _IOW('Q', 11, struct priq_delete_filter) -#define PRIQ_GETSTATS _IOWR('Q', 12, struct priq_class_stats) - -#endif /* ALTQ3_COMPAT */ - -#ifdef _KERNEL - -struct priq_class { - u_int32_t cl_handle; /* class handle */ - class_queue_t *cl_q; /* class queue structure */ - struct red *cl_red; /* RED state */ - int cl_pri; /* priority */ - int cl_flags; /* class flags */ - struct priq_if *cl_pif; /* back pointer to pif */ - struct altq_pktattr *cl_pktattr; /* saved header used by ECN */ - - /* statistics */ - u_int cl_period; /* backlog period */ - struct pktcntr cl_xmitcnt; /* transmitted packet counter */ - struct pktcntr cl_dropcnt; /* dropped packet counter */ -}; - -/* - * priq interface state - */ -struct priq_if { - struct priq_if *pif_next; /* interface state list */ - struct ifaltq *pif_ifq; /* backpointer to ifaltq */ - u_int pif_bandwidth; /* link bandwidth in bps */ - int pif_maxpri; /* max priority in use */ - struct priq_class *pif_default; /* default class */ - struct priq_class *pif_classes[PRIQ_MAXPRI]; /* classes */ -#ifdef ALTQ3_CLFIER_COMPAT - struct acc_classifier pif_classifier; /* classifier */ -#endif -}; - -#endif /* _KERNEL */ - -#ifdef __cplusplus -} -#endif - -#endif /* _ALTQ_ALTQ_PRIQ_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_red.h b/sbin/pfctl/missing/altq/altq_red.h deleted file mode 100644 index 96e3fae94fc6a..0000000000000 --- a/sbin/pfctl/missing/altq/altq_red.h +++ /dev/null @@ -1,199 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_red.h,v 1.8 2003/07/10 12:07:49 kjc Exp $ */ - -/* - * Copyright (C) 1997-2003 - * Sony Computer Science Laboratories Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _ALTQ_ALTQ_RED_H_ -#define _ALTQ_ALTQ_RED_H_ - -#include - -#ifdef ALTQ3_COMPAT -struct red_interface { - char red_ifname[IFNAMSIZ]; -}; - -struct red_stats { - struct red_interface iface; - int q_len; - int q_avg; - - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; - u_int drop_forced; - u_int drop_unforced; - u_int marked_packets; - - /* static red parameters */ - int q_limit; - int weight; - int inv_pmax; - int th_min; - int th_max; - - /* flowvalve related stuff */ - u_int fv_flows; - u_int fv_pass; - u_int fv_predrop; - u_int fv_alloc; - u_int fv_escape; -}; - -struct red_conf { - struct red_interface iface; - int red_weight; /* weight for EWMA */ - int red_inv_pmax; /* inverse of max drop probability */ - int red_thmin; /* red min threshold */ - int red_thmax; /* red max threshold */ - int red_limit; /* max queue length */ - int red_pkttime; /* average packet time in usec */ - int red_flags; /* see below */ -}; -#endif /* ALTQ3_COMPAT */ - -/* red flags */ -#define REDF_ECN4 0x01 /* use packet marking for IPv4 packets */ -#define REDF_ECN6 0x02 /* use packet marking for IPv6 packets */ -#define REDF_ECN (REDF_ECN4 | REDF_ECN6) -#define REDF_FLOWVALVE 0x04 /* use flowvalve (aka penalty-box) */ - -/* - * simpler versions of red parameters and statistics used by other - * disciplines (e.g., CBQ) - */ -struct redparams { - int th_min; /* red min threshold */ - int th_max; /* red max threshold */ - int inv_pmax; /* inverse of max drop probability */ -}; - -struct redstats { - int q_avg; - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; - u_int drop_forced; - u_int drop_unforced; - u_int marked_packets; -}; - -#ifdef ALTQ3_COMPAT -/* - * IOCTLs for RED - */ -#define RED_IF_ATTACH _IOW('Q', 1, struct red_interface) -#define RED_IF_DETACH _IOW('Q', 2, struct red_interface) -#define RED_ENABLE _IOW('Q', 3, struct red_interface) -#define RED_DISABLE _IOW('Q', 4, struct red_interface) -#define RED_CONFIG _IOWR('Q', 6, struct red_conf) -#define RED_GETSTATS _IOWR('Q', 12, struct red_stats) -#define RED_SETDEFAULTS _IOW('Q', 30, struct redparams) -#endif /* ALTQ3_COMPAT */ - -#ifdef _KERNEL - -#ifdef ALTQ3_COMPAT -struct flowvalve; -#endif - -/* weight table structure for idle time calibration */ -struct wtab { - struct wtab *w_next; - int w_weight; - int w_param_max; - int w_refcount; - int32_t w_tab[32]; -}; - -typedef struct red { - int red_pkttime; /* average packet time in micro sec - used for idle calibration */ - int red_flags; /* red flags */ - - /* red parameters */ - int red_weight; /* weight for EWMA */ - int red_inv_pmax; /* inverse of max drop probability */ - int red_thmin; /* red min threshold */ - int red_thmax; /* red max threshold */ - - /* variables for internal use */ - int red_wshift; /* log(red_weight) */ - int red_thmin_s; /* th_min scaled by avgshift */ - int red_thmax_s; /* th_max scaled by avgshift */ - int red_probd; /* drop probability denominator */ - - int red_avg; /* queue len avg scaled by avgshift */ - int red_count; /* packet count since last dropped/ - marked packet */ - int red_idle; /* queue was empty */ - int red_old; /* avg is above th_min */ - struct wtab *red_wtab; /* weight table */ - struct timeval red_last; /* time when the queue becomes idle */ - -#ifdef ALTQ3_COMPAT - struct flowvalve *red_flowvalve; /* flowvalve state */ -#endif - - struct { - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; - u_int drop_forced; - u_int drop_unforced; - u_int marked_packets; - } red_stats; -} red_t; - -#ifdef ALTQ3_COMPAT -typedef struct red_queue { - struct red_queue *rq_next; /* next red_state in the list */ - struct ifaltq *rq_ifq; /* backpointer to ifaltq */ - - class_queue_t *rq_q; - - red_t *rq_red; -} red_queue_t; -#endif /* ALTQ3_COMPAT */ - -/* red drop types */ -#define DTYPE_NODROP 0 /* no drop */ -#define DTYPE_FORCED 1 /* a "forced" drop */ -#define DTYPE_EARLY 2 /* an "unforced" (early) drop */ - -extern red_t *red_alloc(int, int, int, int, int, int); -extern void red_destroy(red_t *); -extern void red_getstats(red_t *, struct redstats *); -extern int red_addq(red_t *, class_queue_t *, struct mbuf *, - struct altq_pktattr *); -extern struct mbuf *red_getq(red_t *, class_queue_t *); -extern int drop_early(int, int, int); -extern int mark_ecn(struct mbuf *, struct altq_pktattr *, int); -extern struct wtab *wtab_alloc(int); -extern int wtab_destroy(struct wtab *); -extern int32_t pow_w(struct wtab *, int); - -#endif /* _KERNEL */ - -#endif /* _ALTQ_ALTQ_RED_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_rio.h b/sbin/pfctl/missing/altq/altq_rio.h deleted file mode 100644 index eda338ae2f1dc..0000000000000 --- a/sbin/pfctl/missing/altq/altq_rio.h +++ /dev/null @@ -1,145 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_rio.h,v 1.9 2003/07/10 12:07:49 kjc Exp $ */ - -/* - * Copyright (C) 1998-2003 - * Sony Computer Science Laboratories Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _ALTQ_ALTQ_RIO_H_ -#define _ALTQ_ALTQ_RIO_H_ - -#include - -/* - * RIO: RED with IN/OUT bit - * (extended to support more than 2 drop precedence values) - */ -#define RIO_NDROPPREC 3 /* number of drop precedence values */ - -#ifdef ALTQ3_COMPAT -struct rio_interface { - char rio_ifname[IFNAMSIZ]; -}; - -struct rio_stats { - struct rio_interface iface; - int q_len[RIO_NDROPPREC]; - struct redstats q_stats[RIO_NDROPPREC]; - - /* static red parameters */ - int q_limit; - int weight; - int flags; - struct redparams q_params[RIO_NDROPPREC]; -}; - -struct rio_conf { - struct rio_interface iface; - struct redparams q_params[RIO_NDROPPREC]; - int rio_weight; /* weight for EWMA */ - int rio_limit; /* max queue length */ - int rio_pkttime; /* average packet time in usec */ - int rio_flags; /* see below */ -}; -#endif /* ALTQ3_COMPAT */ - -/* rio flags */ -#define RIOF_ECN4 0x01 /* use packet marking for IPv4 packets */ -#define RIOF_ECN6 0x02 /* use packet marking for IPv6 packets */ -#define RIOF_ECN (RIOF_ECN4 | RIOF_ECN6) -#define RIOF_CLEARDSCP 0x200 /* clear diffserv codepoint */ - -#ifdef ALTQ3_COMPAT -/* - * IOCTLs for RIO - */ -#define RIO_IF_ATTACH _IOW('Q', 1, struct rio_interface) -#define RIO_IF_DETACH _IOW('Q', 2, struct rio_interface) -#define RIO_ENABLE _IOW('Q', 3, struct rio_interface) -#define RIO_DISABLE _IOW('Q', 4, struct rio_interface) -#define RIO_CONFIG _IOWR('Q', 6, struct rio_conf) -#define RIO_GETSTATS _IOWR('Q', 12, struct rio_stats) -#define RIO_SETDEFAULTS _IOW('Q', 30, struct redparams[RIO_NDROPPREC]) -#endif /* ALTQ3_COMPAT */ - -#ifdef _KERNEL - -typedef struct rio { - /* per drop precedence structure */ - struct dropprec_state { - /* red parameters */ - int inv_pmax; /* inverse of max drop probability */ - int th_min; /* red min threshold */ - int th_max; /* red max threshold */ - - /* variables for internal use */ - int th_min_s; /* th_min scaled by avgshift */ - int th_max_s; /* th_max scaled by avgshift */ - int probd; /* drop probability denominator */ - - int qlen; /* queue length */ - int avg; /* (scaled) queue length average */ - int count; /* packet count since the last dropped/ - marked packet */ - int idle; /* queue was empty */ - int old; /* avg is above th_min */ - struct timeval last; /* timestamp when queue becomes idle */ - } rio_precstate[RIO_NDROPPREC]; - - int rio_wshift; /* log(red_weight) */ - int rio_weight; /* weight for EWMA */ - struct wtab *rio_wtab; /* weight table */ - - int rio_pkttime; /* average packet time in micro sec - used for idle calibration */ - int rio_flags; /* rio flags */ - - u_int8_t rio_codepoint; /* codepoint value to tag packets */ - u_int8_t rio_codepointmask; /* codepoint mask bits */ - - struct redstats q_stats[RIO_NDROPPREC]; /* statistics */ -} rio_t; - -#ifdef ALTQ3_COMPAT -typedef struct rio_queue { - struct rio_queue *rq_next; /* next red_state in the list */ - struct ifaltq *rq_ifq; /* backpointer to ifaltq */ - - class_queue_t *rq_q; - - rio_t *rq_rio; -} rio_queue_t; -#endif /* ALTQ3_COMPAT */ - -extern rio_t *rio_alloc(int, struct redparams *, int, int); -extern void rio_destroy(rio_t *); -extern void rio_getstats(rio_t *, struct redstats *); -extern int rio_addq(rio_t *, class_queue_t *, struct mbuf *, - struct altq_pktattr *); -extern struct mbuf *rio_getq(rio_t *, class_queue_t *); - -#endif /* _KERNEL */ - -#endif /* _ALTQ_ALTQ_RIO_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_rmclass.h b/sbin/pfctl/missing/altq/altq_rmclass.h deleted file mode 100644 index 87e3e630bb8a1..0000000000000 --- a/sbin/pfctl/missing/altq/altq_rmclass.h +++ /dev/null @@ -1,263 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_rmclass.h,v 1.10 2003/08/20 23:30:23 itojun Exp $ */ - -/* - * Copyright (c) 1991-1997 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor of the Laboratory may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _ALTQ_ALTQ_RMCLASS_H_ -#define _ALTQ_ALTQ_RMCLASS_H_ - -#include - -/* #pragma ident "@(#)rm_class.h 1.20 97/10/23 SMI" */ - -#ifdef __cplusplus -extern "C" { -#endif - -#define RM_MAXPRIO 8 /* Max priority */ - -#ifdef _KERNEL - -typedef struct mbuf mbuf_t; -typedef struct rm_ifdat rm_ifdat_t; -typedef struct rm_class rm_class_t; - -struct red; - -/* - * Macros for dealing with time values. We assume all times are - * 'timevals'. `microtime' is used to get the best available clock - * resolution. If `microtime' *doesn't* return a value that's about - * ten times smaller than the average packet time on the fastest - * link that will use these routines, a slightly different clock - * scheme than this one should be used. - * (Bias due to truncation error in this scheme will overestimate utilization - * and discriminate against high bandwidth classes. To remove this bias an - * integrator needs to be added. The simplest integrator uses a history of - * 10 * avg.packet.time / min.tick.time packet completion entries. This is - * straight forward to add but we don't want to pay the extra memory - * traffic to maintain it if it's not necessary (occasionally a vendor - * accidentally builds a workstation with a decent clock - e.g., Sun & HP).) - */ - -#define RM_GETTIME(now) microtime(&now) - -#define TV_LT(a, b) (((a)->tv_sec < (b)->tv_sec) || \ - (((a)->tv_usec < (b)->tv_usec) && ((a)->tv_sec <= (b)->tv_sec))) - -#define TV_DELTA(a, b, delta) { \ - register int xxs; \ - \ - delta = (a)->tv_usec - (b)->tv_usec; \ - if ((xxs = (a)->tv_sec - (b)->tv_sec)) { \ - switch (xxs) { \ - default: \ - /* if (xxs < 0) \ - printf("rm_class: bogus time values\n"); */ \ - delta = 0; \ - /* fall through */ \ - case 2: \ - delta += 1000000; \ - /* fall through */ \ - case 1: \ - delta += 1000000; \ - break; \ - } \ - } \ -} - -#define TV_ADD_DELTA(a, delta, res) { \ - register int xxus = (a)->tv_usec + (delta); \ - \ - (res)->tv_sec = (a)->tv_sec; \ - while (xxus >= 1000000) { \ - ++((res)->tv_sec); \ - xxus -= 1000000; \ - } \ - (res)->tv_usec = xxus; \ -} - -#define RM_TIMEOUT 2 /* 1 Clock tick. */ - -#if 1 -#define RM_MAXQUEUED 1 /* this isn't used in ALTQ/CBQ */ -#else -#define RM_MAXQUEUED 16 /* Max number of packets downstream of CBQ */ -#endif -#define RM_MAXQUEUE 64 /* Max queue length */ -#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ -#define RM_POWER (1 << RM_FILTER_GAIN) -#define RM_MAXDEPTH 32 -#define RM_NS_PER_SEC (1000000000) - -typedef struct _rm_class_stats_ { - u_int handle; - u_int depth; - - struct pktcntr xmit_cnt; /* packets sent in this class */ - struct pktcntr drop_cnt; /* dropped packets */ - u_int over; /* # times went over limit */ - u_int borrows; /* # times tried to borrow */ - u_int overactions; /* # times invoked overlimit action */ - u_int delays; /* # times invoked delay actions */ -} rm_class_stats_t; - -/* - * CBQ Class state structure - */ -struct rm_class { - class_queue_t *q_; /* Queue of packets */ - rm_ifdat_t *ifdat_; - int pri_; /* Class priority. */ - int depth_; /* Class depth */ - u_int ns_per_byte_; /* NanoSeconds per byte. */ - u_int maxrate_; /* Bytes per second for this class. */ - u_int allotment_; /* Fraction of link bandwidth. */ - u_int w_allotment_; /* Weighted allotment for WRR */ - int bytes_alloc_; /* Allocation for round of WRR */ - - int avgidle_; - int maxidle_; - int minidle_; - int offtime_; - int sleeping_; /* != 0 if delaying */ - int qthresh_; /* Queue threshold for formal link sharing */ - int leaf_; /* Note whether leaf class or not.*/ - - rm_class_t *children_; /* Children of this class */ - rm_class_t *next_; /* Next pointer, used if child */ - - rm_class_t *peer_; /* Peer class */ - rm_class_t *borrow_; /* Borrow class */ - rm_class_t *parent_; /* Parent class */ - - void (*overlimit)(struct rm_class *, struct rm_class *); - void (*drop)(struct rm_class *); /* Class drop action. */ - - struct red *red_; /* RED state pointer */ - struct altq_pktattr *pktattr_; /* saved hdr used by RED/ECN */ - int flags_; - - int last_pkttime_; /* saved pkt_time */ - struct timeval undertime_; /* time can next send */ - struct timeval last_; /* time last packet sent */ - struct timeval overtime_; - struct callout callout_; /* for timeout() calls */ - - rm_class_stats_t stats_; /* Class Statistics */ -}; - -/* - * CBQ Interface state - */ -struct rm_ifdat { - int queued_; /* # pkts queued downstream */ - int efficient_; /* Link Efficency bit */ - int wrr_; /* Enable Weighted Round-Robin */ - u_long ns_per_byte_; /* Link byte speed. */ - int maxqueued_; /* Max packets to queue */ - int maxpkt_; /* Max packet size. */ - int qi_; /* In/out pointers for downstream */ - int qo_; /* packets */ - - /* - * Active class state and WRR state. - */ - rm_class_t *active_[RM_MAXPRIO]; /* Active cl's in each pri */ - int na_[RM_MAXPRIO]; /* # of active cl's in a pri */ - int num_[RM_MAXPRIO]; /* # of cl's per pri */ - int alloc_[RM_MAXPRIO]; /* Byte Allocation */ - u_long M_[RM_MAXPRIO]; /* WRR weights. */ - - /* - * Network Interface/Solaris Queue state pointer. - */ - struct ifaltq *ifq_; - rm_class_t *default_; /* Default Pkt class, BE */ - rm_class_t *root_; /* Root Link class. */ - rm_class_t *ctl_; /* Control Traffic class. */ - void (*restart)(struct ifaltq *); /* Restart routine. */ - - /* - * Current packet downstream packet state and dynamic state. - */ - rm_class_t *borrowed_[RM_MAXQUEUED]; /* Class borrowed last */ - rm_class_t *class_[RM_MAXQUEUED]; /* class sending */ - int curlen_[RM_MAXQUEUED]; /* Current pktlen */ - struct timeval now_[RM_MAXQUEUED]; /* Current packet time. */ - int is_overlimit_[RM_MAXQUEUED];/* Current packet time. */ - - int cutoff_; /* Cut-off depth for borrowing */ - - struct timeval ifnow_; /* expected xmit completion time */ -#if 1 /* ALTQ4PPP */ - int maxiftime_; /* max delay inside interface */ -#endif - rm_class_t *pollcache_; /* cached rm_class by poll operation */ -}; - -/* flags for rmc_init and rmc_newclass */ -/* class flags */ -#define RMCF_RED 0x0001 -#define RMCF_ECN 0x0002 -#define RMCF_RIO 0x0004 -#define RMCF_FLOWVALVE 0x0008 /* use flowvalve (aka penalty-box) */ -#define RMCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */ - -/* flags for rmc_init */ -#define RMCF_WRR 0x0100 -#define RMCF_EFFICIENT 0x0200 - -#define is_a_parent_class(cl) ((cl)->children_ != NULL) - -extern rm_class_t *rmc_newclass(int, struct rm_ifdat *, u_int, - void (*)(struct rm_class *, struct rm_class *), - int, struct rm_class *, struct rm_class *, - u_int, int, u_int, int, int); -extern void rmc_delete_class(struct rm_ifdat *, struct rm_class *); -extern int rmc_modclass(struct rm_class *, u_int, int, - u_int, int, u_int, int); -extern void rmc_init(struct ifaltq *, struct rm_ifdat *, u_int, - void (*)(struct ifaltq *), - int, int, u_int, int, u_int, int); -extern int rmc_queue_packet(struct rm_class *, mbuf_t *); -extern mbuf_t *rmc_dequeue_next(struct rm_ifdat *, int); -extern void rmc_update_class_util(struct rm_ifdat *); -extern void rmc_delay_action(struct rm_class *, struct rm_class *); -extern void rmc_dropall(struct rm_class *); -extern int rmc_get_weight(struct rm_ifdat *, int); - -#endif /* _KERNEL */ - -#ifdef __cplusplus -} -#endif - -#endif /* _ALTQ_ALTQ_RMCLASS_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_rmclass_debug.h b/sbin/pfctl/missing/altq/altq_rmclass_debug.h deleted file mode 100644 index 7fcc86ba129be..0000000000000 --- a/sbin/pfctl/missing/altq/altq_rmclass_debug.h +++ /dev/null @@ -1,113 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_rmclass_debug.h,v 1.3 2002/11/29 04:36:24 kjc Exp $ */ - -/* - * Copyright (c) Sun Microsystems, Inc. 1998 All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the SMCC Technology - * Development Group at Sun Microsystems, Inc. - * - * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE - * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is - * provided "as is" without express or implied warranty of any kind. - * - * These notices must be retained in any copies of any part of this software. - */ - -#ifndef _ALTQ_ALTQ_RMCLASS_DEBUG_H_ -#define _ALTQ_ALTQ_RMCLASS_DEBUG_H_ - -/* #pragma ident "@(#)rm_class_debug.h 1.7 98/05/04 SMI" */ - -/* - * Cbq debugging macros - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef CBQ_TRACE -#ifndef NCBQTRACE -#define NCBQTRACE (16 * 1024) -#endif - -/* - * To view the trace output, using adb, type: - * adb -k /dev/ksyms /dev/mem , then type - * cbqtrace_count/D to get the count, then type - * cbqtrace_buffer,0tcount/Dp4C" "Xn - * This will dump the trace buffer from 0 to count. - */ -/* - * in ALTQ, "call cbqtrace_dump(N)" from DDB to display 20 events - * from Nth event in the circular buffer. - */ - -struct cbqtrace { - int count; - int function; /* address of function */ - int trace_action; /* descriptive 4 characters */ - int object; /* object operated on */ -}; - -extern struct cbqtrace cbqtrace_buffer[]; -extern struct cbqtrace *cbqtrace_ptr; -extern int cbqtrace_count; - -#define CBQTRACEINIT() { \ - if (cbqtrace_ptr == NULL) \ - cbqtrace_ptr = cbqtrace_buffer; \ - else { \ - cbqtrace_ptr = cbqtrace_buffer; \ - bzero((void *)cbqtrace_ptr, sizeof(cbqtrace_buffer)); \ - cbqtrace_count = 0; \ - } \ -} - -#define LOCK_TRACE() splimp() -#define UNLOCK_TRACE(x) splx(x) - -#define CBQTRACE(func, act, obj) { \ - int __s = LOCK_TRACE(); \ - int *_p = &cbqtrace_ptr->count; \ - *_p++ = ++cbqtrace_count; \ - *_p++ = (int)(func); \ - *_p++ = (int)(act); \ - *_p++ = (int)(obj); \ - if ((struct cbqtrace *)(void *)_p >= &cbqtrace_buffer[NCBQTRACE])\ - cbqtrace_ptr = cbqtrace_buffer; \ - else \ - cbqtrace_ptr = (struct cbqtrace *)(void *)_p; \ - UNLOCK_TRACE(__s); \ - } -#else - -/* If no tracing, define no-ops */ -#define CBQTRACEINIT() -#define CBQTRACE(a, b, c) - -#endif /* !CBQ_TRACE */ - -#ifdef __cplusplus -} -#endif - -#endif /* _ALTQ_ALTQ_RMCLASS_DEBUG_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_var.h b/sbin/pfctl/missing/altq/altq_var.h deleted file mode 100644 index 165b5afe32045..0000000000000 --- a/sbin/pfctl/missing/altq/altq_var.h +++ /dev/null @@ -1,267 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_var.h,v 1.15 2003/07/10 12:07:49 kjc Exp $ */ - -/* - * Copyright (C) 1998-2003 - * Sony Computer Science Laboratories Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#ifndef _ALTQ_ALTQ_VAR_H_ -#define _ALTQ_ALTQ_VAR_H_ - -#ifdef _KERNEL - -#include -#include -#include - -#ifdef ALTQ3_CLFIER_COMPAT -/* - * filter structure for altq common classifier - */ -struct acc_filter { - LIST_ENTRY(acc_filter) f_chain; - void *f_class; /* pointer to the class */ - u_long f_handle; /* filter id */ - u_int32_t f_fbmask; /* filter bitmask */ - struct flow_filter f_filter; /* filter value */ -}; - -/* - * XXX ACC_FILTER_TABLESIZE can't be larger than 2048 unless we fix - * the handle assignment. - */ -#define ACC_FILTER_TABLESIZE (256+1) -#define ACC_FILTER_MASK (ACC_FILTER_TABLESIZE - 2) -#define ACC_WILDCARD_INDEX (ACC_FILTER_TABLESIZE - 1) -#ifdef __GNUC__ -#define ACC_GET_HASH_INDEX(addr) \ - ({int x = (addr) + ((addr) >> 16); (x + (x >> 8)) & ACC_FILTER_MASK;}) -#else -#define ACC_GET_HASH_INDEX(addr) \ - (((addr) + ((addr) >> 8) + ((addr) >> 16) + ((addr) >> 24)) \ - & ACC_FILTER_MASK) -#endif -#define ACC_GET_HINDEX(handle) ((handle) >> 20) - -#if (__FreeBSD_version > 500000) -#define ACC_LOCK_INIT(ac) mtx_init(&(ac)->acc_mtx, "classifier", MTX_DEF) -#define ACC_LOCK_DESTROY(ac) mtx_destroy(&(ac)->acc_mtx) -#define ACC_LOCK(ac) mtx_lock(&(ac)->acc_mtx) -#define ACC_UNLOCK(ac) mtx_unlock(&(ac)->acc_mtx) -#else -#define ACC_LOCK_INIT(ac) -#define ACC_LOCK_DESTROY(ac) -#define ACC_LOCK(ac) -#define ACC_UNLOCK(ac) -#endif - -struct acc_classifier { - u_int32_t acc_fbmask; - LIST_HEAD(filt, acc_filter) acc_filters[ACC_FILTER_TABLESIZE]; - -#if (__FreeBSD_version > 500000) - struct mtx acc_mtx; -#endif -}; - -/* - * flowinfo mask bits used by classifier - */ -/* for ipv4 */ -#define FIMB4_PROTO 0x0001 -#define FIMB4_TOS 0x0002 -#define FIMB4_DADDR 0x0004 -#define FIMB4_SADDR 0x0008 -#define FIMB4_DPORT 0x0010 -#define FIMB4_SPORT 0x0020 -#define FIMB4_GPI 0x0040 -#define FIMB4_ALL 0x007f -/* for ipv6 */ -#define FIMB6_PROTO 0x0100 -#define FIMB6_TCLASS 0x0200 -#define FIMB6_DADDR 0x0400 -#define FIMB6_SADDR 0x0800 -#define FIMB6_DPORT 0x1000 -#define FIMB6_SPORT 0x2000 -#define FIMB6_GPI 0x4000 -#define FIMB6_FLABEL 0x8000 -#define FIMB6_ALL 0xff00 - -#define FIMB_ALL (FIMB4_ALL|FIMB6_ALL) - -#define FIMB4_PORTS (FIMB4_DPORT|FIMB4_SPORT|FIMB4_GPI) -#define FIMB6_PORTS (FIMB6_DPORT|FIMB6_SPORT|FIMB6_GPI) -#endif /* ALTQ3_CLFIER_COMPAT */ - -/* - * machine dependent clock - * a 64bit high resolution time counter. - */ -extern int machclk_usepcc; -extern u_int32_t machclk_freq; -extern u_int32_t machclk_per_tick; -extern void init_machclk(void); -extern u_int64_t read_machclk(void); - -/* - * debug support - */ -#ifdef ALTQ_DEBUG -#ifdef __STDC__ -#define ASSERT(e) ((e) ? (void)0 : altq_assert(__FILE__, __LINE__, #e)) -#else /* PCC */ -#define ASSERT(e) ((e) ? (void)0 : altq_assert(__FILE__, __LINE__, "e")) -#endif -#else -#define ASSERT(e) ((void)0) -#endif - -/* - * misc stuff for compatibility - */ -/* ioctl cmd type */ -#if defined(__FreeBSD__) && (__FreeBSD__ < 3) -typedef int ioctlcmd_t; -#else -typedef u_long ioctlcmd_t; -#endif - -/* - * queue macros: - * the interface of TAILQ_LAST macro changed after the introduction - * of softupdate. redefine it here to make it work with pre-2.2.7. - */ -#undef TAILQ_LAST -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) - -#ifndef TAILQ_EMPTY -#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) -#endif -#ifndef TAILQ_FOREACH -#define TAILQ_FOREACH(var, head, field) \ - for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field)) -#endif - -/* macro for timeout/untimeout */ -#if (__FreeBSD_version > 300000) || defined(__NetBSD__) -/* use callout */ -#include - -#if (__FreeBSD_version > 500000) -#define CALLOUT_INIT(c) callout_init((c), 0) -#else -#define CALLOUT_INIT(c) callout_init((c)) -#endif -#define CALLOUT_RESET(c,t,f,a) callout_reset((c),(t),(f),(a)) -#define CALLOUT_STOP(c) callout_stop((c)) -#ifndef CALLOUT_INITIALIZER -#define CALLOUT_INITIALIZER { { { NULL } }, 0, NULL, NULL, 0 } -#endif -#elif defined(__OpenBSD__) -#include -/* callout structure as a wrapper of struct timeout */ -struct callout { - struct timeout c_to; -}; -#define CALLOUT_INIT(c) do { bzero((c), sizeof(*(c))); } while (/*CONSTCOND*/ 0) -#define CALLOUT_RESET(c,t,f,a) do { if (!timeout_initialized(&(c)->c_to)) \ - timeout_set(&(c)->c_to, (f), (a)); \ - timeout_add(&(c)->c_to, (t)); } while (/*CONSTCOND*/ 0) -#define CALLOUT_STOP(c) timeout_del(&(c)->c_to) -#define CALLOUT_INITIALIZER { { { NULL }, NULL, NULL, 0, 0 } } -#else -/* use old-style timeout/untimeout */ -/* dummy callout structure */ -struct callout { - void *c_arg; /* function argument */ - void (*c_func)(void *); /* functiuon to call */ -}; -#define CALLOUT_INIT(c) do { bzero((c), sizeof(*(c))); } while (/*CONSTCOND*/ 0) -#define CALLOUT_RESET(c,t,f,a) do { (c)->c_arg = (a); \ - (c)->c_func = (f); \ - timeout((f),(a),(t)); } while (/*CONSTCOND*/ 0) -#define CALLOUT_STOP(c) untimeout((c)->c_func,(c)->c_arg) -#define CALLOUT_INITIALIZER { NULL, NULL } -#endif -#if !defined(__FreeBSD__) -typedef void (timeout_t)(void *); -#endif - -#define m_pktlen(m) ((m)->m_pkthdr.len) - -extern int pfaltq_running; - -struct ifnet; struct mbuf; -struct pf_altq; -#ifdef ALTQ3_CLFIER_COMPAT -struct flowinfo; -#endif - -void *altq_lookup(char *, int); -#ifdef ALTQ3_CLFIER_COMPAT -int altq_extractflow(struct mbuf *, int, struct flowinfo *, u_int32_t); -int acc_add_filter(struct acc_classifier *, struct flow_filter *, - void *, u_long *); -int acc_delete_filter(struct acc_classifier *, u_long); -int acc_discard_filters(struct acc_classifier *, void *, int); -void *acc_classify(void *, struct mbuf *, int); -#endif -u_int8_t read_dsfield(struct mbuf *, struct altq_pktattr *); -void write_dsfield(struct mbuf *, struct altq_pktattr *, u_int8_t); -void altq_assert(const char *, int, const char *); -int tbr_set(struct ifaltq *, struct tb_profile *); -int tbr_get(struct ifaltq *, struct tb_profile *); - -int altq_pfattach(struct pf_altq *); -int altq_pfdetach(struct pf_altq *); -int altq_add(struct pf_altq *); -int altq_remove(struct pf_altq *); -int altq_add_queue(struct pf_altq *); -int altq_remove_queue(struct pf_altq *); -int altq_getqstats(struct pf_altq *, void *, int *); - -int cbq_pfattach(struct pf_altq *); -int cbq_add_altq(struct pf_altq *); -int cbq_remove_altq(struct pf_altq *); -int cbq_add_queue(struct pf_altq *); -int cbq_remove_queue(struct pf_altq *); -int cbq_getqstats(struct pf_altq *, void *, int *); - -int priq_pfattach(struct pf_altq *); -int priq_add_altq(struct pf_altq *); -int priq_remove_altq(struct pf_altq *); -int priq_add_queue(struct pf_altq *); -int priq_remove_queue(struct pf_altq *); -int priq_getqstats(struct pf_altq *, void *, int *); - -int hfsc_pfattach(struct pf_altq *); -int hfsc_add_altq(struct pf_altq *); -int hfsc_remove_altq(struct pf_altq *); -int hfsc_add_queue(struct pf_altq *); -int hfsc_remove_queue(struct pf_altq *); -int hfsc_getqstats(struct pf_altq *, void *, int *); - -#endif /* _KERNEL */ -#endif /* _ALTQ_ALTQ_VAR_H_ */ diff --git a/sbin/pfctl/missing/altq/altq_wfq.h b/sbin/pfctl/missing/altq/altq_wfq.h deleted file mode 100644 index ca163cf3652dc..0000000000000 --- a/sbin/pfctl/missing/altq/altq_wfq.h +++ /dev/null @@ -1,129 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: altq_wfq.h,v 1.8 2003/07/10 12:07:49 kjc Exp $ */ - -/* - * Copyright (C) 1997-2002 - * Sony Computer Science Laboratories Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * March 27, 1997. Written by Hiroshi Kyusojin of Keio University - * (kyu@mt.cs.keio.ac.jp). - */ - -#ifndef _ALTQ_ALTQ_WFQ_H_ -#define _ALTQ_ALTQ_WFQ_H_ - -#include - -#define DEFAULT_QSIZE 256 -#define MAX_QSIZE 2048 - -struct wfq_interface{ - char wfq_ifacename[IFNAMSIZ]; -}; - -struct wfq_getqid{ - struct wfq_interface iface; -#ifdef ALTQ3_CLFIER_COMPAT - struct flowinfo flow; -#endif - u_long qid; -}; - -struct wfq_setweight { - struct wfq_interface iface; - int qid; - int weight; -}; - -typedef struct each_queue_stats { - int bytes; /* bytes in this queue */ - int weight; /* weight in percent */ - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; -} queue_stats; - -struct wfq_getstats { - struct wfq_interface iface; - int qid; - queue_stats stats; -}; - -struct wfq_conf { - struct wfq_interface iface; - int hash_policy; /* hash policy */ - int nqueues; /* number of queues */ - int qlimit; /* queue size in bytes */ -}; - -#define WFQ_HASH_DSTADDR 0 /* hash by dst address */ -#define WFQ_HASH_SRCPORT 1 /* hash by src port */ -#define WFQ_HASH_FULL 2 /* hash by all fields */ - -#define WFQ_IF_ATTACH _IOW('Q', 1, struct wfq_interface) -#define WFQ_IF_DETACH _IOW('Q', 2, struct wfq_interface) -#define WFQ_ENABLE _IOW('Q', 3, struct wfq_interface) -#define WFQ_DISABLE _IOW('Q', 4, struct wfq_interface) -#define WFQ_CONFIG _IOWR('Q', 6, struct wfq_conf) -#define WFQ_GET_STATS _IOWR('Q', 12, struct wfq_getstats) -#define WFQ_GET_QID _IOWR('Q', 30, struct wfq_getqid) -#define WFQ_SET_WEIGHT _IOWR('Q', 31, struct wfq_setweight) - -#ifdef _KERNEL - -#define HWM (64 * 1024) -#define WFQ_QUOTA 512 /* quota bytes to send at a time */ -#define WFQ_ADDQUOTA(q) ((q)->quota += WFQ_QUOTA * (q)->weight / 100) -#define ENABLE 0 -#define DISABLE 1 - -typedef struct weighted_fair_queue{ - struct weighted_fair_queue *next, *prev; - struct mbuf *head, *tail; - int bytes; /* bytes in this queue */ - int quota; /* bytes sent in this round */ - int weight; /* weight in percent */ - - struct pktcntr xmit_cnt; - struct pktcntr drop_cnt; -} wfq; - - -typedef struct wfqstate { - struct wfqstate *next; /* for wfqstate list */ - struct ifaltq *ifq; - int nums; /* number of queues */ - int hwm; /* high water mark */ - int bytes; /* total bytes in all the queues */ - wfq *rrp; /* round robin pointer */ - wfq *queue; /* pointer to queue list */ -#ifdef ALTQ3_CLFIER_COMPAT - u_long (*hash_func)(struct flowinfo *, int); -#endif - u_int32_t fbmask; /* filter bitmask */ -} wfq_state_t; - -#endif /* _KERNEL */ - -#endif /* _ALTQ_ALTQ_WFQ_H */ diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y new file mode 100644 index 0000000000000..99c26c0a33dfc --- /dev/null +++ b/sbin/pfctl/parse.y @@ -0,0 +1,6038 @@ +/* $OpenBSD: parse.y,v 1.554 2008/10/17 12:59:53 henning Exp $ */ + +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * Copyright (c) 2002,2003 Henning Brauer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +%{ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +static struct pfctl *pf = NULL; +static int debug = 0; +static int rulestate = 0; +static u_int16_t returnicmpdefault = + (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT; +static u_int16_t returnicmp6default = + (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT; +static int blockpolicy = PFRULE_DROP; +static int require_order = 1; +static int default_statelock; + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file; +struct file *pushfile(const char *, int); +int popfile(void); +int check_file_secrecy(int, const char *); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; +int symset(const char *, const char *, int); +char *symget(const char *); + +int atoul(char *, u_long *); + +enum { + PFCTL_STATE_NONE, + PFCTL_STATE_OPTION, + PFCTL_STATE_SCRUB, + PFCTL_STATE_QUEUE, + PFCTL_STATE_NAT, + PFCTL_STATE_FILTER +}; + +struct node_proto { + u_int8_t proto; + struct node_proto *next; + struct node_proto *tail; +}; + +struct node_port { + u_int16_t port[2]; + u_int8_t op; + struct node_port *next; + struct node_port *tail; +}; + +struct node_uid { + uid_t uid[2]; + u_int8_t op; + struct node_uid *next; + struct node_uid *tail; +}; + +struct node_gid { + gid_t gid[2]; + u_int8_t op; + struct node_gid *next; + struct node_gid *tail; +}; + +struct node_icmp { + u_int8_t code; + u_int8_t type; + u_int8_t proto; + struct node_icmp *next; + struct node_icmp *tail; +}; + +enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, + PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, + PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, + PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, + PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; + +enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; + +struct node_state_opt { + int type; + union { + u_int32_t max_states; + u_int32_t max_src_states; + u_int32_t max_src_conn; + struct { + u_int32_t limit; + u_int32_t seconds; + } max_src_conn_rate; + struct { + u_int8_t flush; + char tblname[PF_TABLE_NAME_SIZE]; + } overload; + u_int32_t max_src_nodes; + u_int8_t src_track; + u_int32_t statelock; + struct { + int number; + u_int32_t seconds; + } timeout; + } data; + struct node_state_opt *next; + struct node_state_opt *tail; +}; + +struct peer { + struct node_host *host; + struct node_port *port; +}; + +struct node_queue { + char queue[PF_QNAME_SIZE]; + char parent[PF_QNAME_SIZE]; + char ifname[IFNAMSIZ]; + int scheduler; + struct node_queue *next; + struct node_queue *tail; +} *queues = NULL; + +struct node_qassign { + char *qname; + char *pqname; +}; + +struct filter_opts { + int marker; +#define FOM_FLAGS 0x01 +#define FOM_ICMP 0x02 +#define FOM_TOS 0x04 +#define FOM_KEEP 0x08 +#define FOM_SRCTRACK 0x10 + struct node_uid *uid; + struct node_gid *gid; + struct { + u_int8_t b1; + u_int8_t b2; + u_int16_t w; + u_int16_t w2; + } flags; + struct node_icmp *icmpspec; + u_int32_t tos; + u_int32_t prob; + struct { + int action; + struct node_state_opt *options; + } keep; + int fragment; + int allowopts; + char *label; + struct node_qassign queues; + char *tag; + char *match_tag; + u_int8_t match_tag_not; + u_int rtableid; + struct { + struct node_host *addr; + u_int16_t port; + } divert; +} filter_opts; + +struct antispoof_opts { + char *label; + u_int rtableid; +} antispoof_opts; + +struct scrub_opts { + int marker; +#define SOM_MINTTL 0x01 +#define SOM_MAXMSS 0x02 +#define SOM_FRAGCACHE 0x04 +#define SOM_SETTOS 0x08 + int nodf; + int minttl; + int maxmss; + int settos; + int fragcache; + int randomid; + int reassemble_tcp; + char *match_tag; + u_int8_t match_tag_not; + u_int rtableid; +} scrub_opts; + +struct queue_opts { + int marker; +#define QOM_BWSPEC 0x01 +#define QOM_SCHEDULER 0x02 +#define QOM_PRIORITY 0x04 +#define QOM_TBRSIZE 0x08 +#define QOM_QLIMIT 0x10 + struct node_queue_bw queue_bwspec; + struct node_queue_opt scheduler; + int priority; + int tbrsize; + int qlimit; +} queue_opts; + +struct table_opts { + int flags; + int init_addr; + struct node_tinithead init_nodes; +} table_opts; + +struct pool_opts { + int marker; +#define POM_TYPE 0x01 +#define POM_STICKYADDRESS 0x02 + u_int8_t opts; + int type; + int staticport; + struct pf_poolhashkey *key; + +} pool_opts; + + +struct node_hfsc_opts hfsc_opts; +struct node_state_opt *keep_state_defaults = NULL; + +int disallow_table(struct node_host *, const char *); +int disallow_urpf_failed(struct node_host *, const char *); +int disallow_alias(struct node_host *, const char *); +int rule_consistent(struct pf_rule *, int); +int filter_consistent(struct pf_rule *, int); +int nat_consistent(struct pf_rule *); +int rdr_consistent(struct pf_rule *); +int process_tabledef(char *, struct table_opts *); +void expand_label_str(char *, size_t, const char *, const char *); +void expand_label_if(const char *, char *, size_t, const char *); +void expand_label_addr(const char *, char *, size_t, u_int8_t, + struct node_host *); +void expand_label_port(const char *, char *, size_t, + struct node_port *); +void expand_label_proto(const char *, char *, size_t, u_int8_t); +void expand_label_nr(const char *, char *, size_t); +void expand_label(char *, size_t, const char *, u_int8_t, + struct node_host *, struct node_port *, struct node_host *, + struct node_port *, u_int8_t); +void expand_rule(struct pf_rule *, struct node_if *, + struct node_host *, struct node_proto *, struct node_os *, + struct node_host *, struct node_port *, struct node_host *, + struct node_port *, struct node_uid *, struct node_gid *, + struct node_icmp *, const char *); +int expand_altq(struct pf_altq *, struct node_if *, + struct node_queue *, struct node_queue_bw bwspec, + struct node_queue_opt *); +int expand_queue(struct pf_altq *, struct node_if *, + struct node_queue *, struct node_queue_bw, + struct node_queue_opt *); +int expand_skip_interface(struct node_if *); + +int check_rulestate(int); +int getservice(char *); +int rule_label(struct pf_rule *, char *); +int rt_tableid_max(void); + +void mv_rules(struct pf_ruleset *, struct pf_ruleset *); +void decide_address_family(struct node_host *, sa_family_t *); +void remove_invalid_hosts(struct node_host **, sa_family_t *); +int invalid_redirect(struct node_host *, sa_family_t); +u_int16_t parseicmpspec(char *, sa_family_t); + +TAILQ_HEAD(loadanchorshead, loadanchors) + loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead); + +struct loadanchors { + TAILQ_ENTRY(loadanchors) entries; + char *anchorname; + char *filename; +}; + +typedef struct { + union { + int64_t number; + double probability; + int i; + char *string; + u_int rtableid; + struct { + u_int8_t b1; + u_int8_t b2; + u_int16_t w; + u_int16_t w2; + } b; + struct range { + int a; + int b; + int t; + } range; + struct node_if *interface; + struct node_proto *proto; + struct node_icmp *icmp; + struct node_host *host; + struct node_os *os; + struct node_port *port; + struct node_uid *uid; + struct node_gid *gid; + struct node_state_opt *state_opt; + struct peer peer; + struct { + struct peer src, dst; + struct node_os *src_os; + } fromto; + struct { + struct node_host *host; + u_int8_t rt; + u_int8_t pool_opts; + sa_family_t af; + struct pf_poolhashkey *key; + } route; + struct redirection { + struct node_host *host; + struct range rport; + } *redirection; + struct { + int action; + struct node_state_opt *options; + } keep_state; + struct { + u_int8_t log; + u_int8_t logif; + u_int8_t quick; + } logquick; + struct { + int neg; + char *name; + } tagged; + struct pf_poolhashkey *hashkey; + struct node_queue *queue; + struct node_queue_opt queue_options; + struct node_queue_bw queue_bwspec; + struct node_qassign qassign; + struct filter_opts filter_opts; + struct antispoof_opts antispoof_opts; + struct queue_opts queue_opts; + struct scrub_opts scrub_opts; + struct table_opts table_opts; + struct pool_opts pool_opts; + struct node_hfsc_opts hfsc_opts; + } v; + int lineno; +} YYSTYPE; + +#define PPORT_RANGE 1 +#define PPORT_STAR 2 +int parseport(char *, struct range *r, int); + +#define DYNIF_MULTIADDR(addr) ((addr).type == PF_ADDR_DYNIFTL && \ + (!((addr).iflags & PFI_AFLAG_NOALIAS) || \ + !isdigit((addr).v.ifname[strlen((addr).v.ifname)-1]))) + +%} + +%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS +%token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE +%token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF +%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL +%token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE +%token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR +%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID +%token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID +%token ANTISPOOF FOR INCLUDE +%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY +%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT +%token QUEUE PRIORITY QLIMIT RTABLE +%token LOAD RULESET_OPTIMIZATION +%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE +%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY +%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS +%token DIVERTTO DIVERTREPLY +%token STRING +%token NUMBER +%token PORTBINARY +%type interface if_list if_item_not if_item +%type number icmptype icmp6type uid gid +%type tos not yesno +%type probability +%type no dir af fragcache optimizer +%type sourcetrack flush unaryop statelock +%type action nataction natpasslog scrubaction +%type flags flag blockspec +%type portplain portstar portrange +%type hashkey +%type proto proto_list proto_item +%type protoval +%type icmpspec +%type icmp_list icmp_item +%type icmp6_list icmp6_item +%type reticmpspec reticmp6spec +%type fromto +%type ipportspec from to +%type ipspec toipspec xhost host dynaddr host_list +%type redir_host_list redirspec +%type route_host route_host_list routespec +%type os xos os_list +%type portspec port_list port_item +%type uids uid_list uid_item +%type gids gid_list gid_item +%type route +%type redirection redirpool +%type label stringall tag anchorname +%type string varstring numberstring +%type keep +%type state_opt_spec state_opt_list state_opt_item +%type logquick quick log logopts logopt +%type antispoof_ifspc antispoof_iflst antispoof_if +%type qname +%type qassign qassign_list qassign_item +%type scheduler +%type cbqflags_list cbqflags_item +%type priqflags_list priqflags_item +%type hfscopts_list hfscopts_item hfsc_opts +%type bandwidth +%type filter_opts filter_opt filter_opts_l +%type antispoof_opts antispoof_opt antispoof_opts_l +%type queue_opts queue_opt queue_opts_l +%type scrub_opts scrub_opt scrub_opts_l +%type table_opts table_opt table_opts_l +%type pool_opts pool_opt pool_opts_l +%type tagged +%type rtable +%% + +ruleset : /* empty */ + | ruleset include '\n' + | ruleset '\n' + | ruleset option '\n' + | ruleset scrubrule '\n' + | ruleset natrule '\n' + | ruleset binatrule '\n' + | ruleset pfrule '\n' + | ruleset anchorrule '\n' + | ruleset loadrule '\n' + | ruleset altqif '\n' + | ruleset queuespec '\n' + | ruleset varset '\n' + | ruleset antispoof '\n' + | ruleset tabledef '\n' + | '{' fakeanchor '}' '\n'; + | ruleset error '\n' { file->errors++; } + ; + +include : INCLUDE STRING { + struct file *nfile; + + if ((nfile = pushfile($2, 0)) == NULL) { + yyerror("failed to include file %s", $2); + free($2); + YYERROR; + } + free($2); + + file = nfile; + lungetc('\n'); + } + ; + +/* + * apply to previouslys specified rule: must be careful to note + * what that is: pf or nat or binat or rdr + */ +fakeanchor : fakeanchor '\n' + | fakeanchor anchorrule '\n' + | fakeanchor binatrule '\n' + | fakeanchor natrule '\n' + | fakeanchor pfrule '\n' + | fakeanchor error '\n' + ; + +optimizer : string { + if (!strcmp($1, "none")) + $$ = 0; + else if (!strcmp($1, "basic")) + $$ = PF_OPTIMIZE_BASIC; + else if (!strcmp($1, "profile")) + $$ = PF_OPTIMIZE_BASIC | PF_OPTIMIZE_PROFILE; + else { + yyerror("unknown ruleset-optimization %s", $1); + YYERROR; + } + } + ; + +option : SET OPTIMIZATION STRING { + if (check_rulestate(PFCTL_STATE_OPTION)) { + free($3); + YYERROR; + } + if (pfctl_set_optimization(pf, $3) != 0) { + yyerror("unknown optimization %s", $3); + free($3); + YYERROR; + } + free($3); + } + | SET RULESET_OPTIMIZATION optimizer { + if (!(pf->opts & PF_OPT_OPTIMIZE)) { + pf->opts |= PF_OPT_OPTIMIZE; + pf->optimize = $3; + } + } + | SET TIMEOUT timeout_spec + | SET TIMEOUT '{' optnl timeout_list '}' + | SET LIMIT limit_spec + | SET LIMIT '{' optnl limit_list '}' + | SET LOGINTERFACE stringall { + if (check_rulestate(PFCTL_STATE_OPTION)) { + free($3); + YYERROR; + } + if (pfctl_set_logif(pf, $3) != 0) { + yyerror("error setting loginterface %s", $3); + free($3); + YYERROR; + } + free($3); + } + | SET HOSTID number { + if ($3 == 0 || $3 > UINT_MAX) { + yyerror("hostid must be non-zero"); + YYERROR; + } + if (pfctl_set_hostid(pf, $3) != 0) { + yyerror("error setting hostid %08x", $3); + YYERROR; + } + } + | SET BLOCKPOLICY DROP { + if (pf->opts & PF_OPT_VERBOSE) + printf("set block-policy drop\n"); + if (check_rulestate(PFCTL_STATE_OPTION)) + YYERROR; + blockpolicy = PFRULE_DROP; + } + | SET BLOCKPOLICY RETURN { + if (pf->opts & PF_OPT_VERBOSE) + printf("set block-policy return\n"); + if (check_rulestate(PFCTL_STATE_OPTION)) + YYERROR; + blockpolicy = PFRULE_RETURN; + } + | SET REQUIREORDER yesno { + if (pf->opts & PF_OPT_VERBOSE) + printf("set require-order %s\n", + $3 == 1 ? "yes" : "no"); + require_order = $3; + } + | SET FINGERPRINTS STRING { + if (pf->opts & PF_OPT_VERBOSE) + printf("set fingerprints \"%s\"\n", $3); + if (check_rulestate(PFCTL_STATE_OPTION)) { + free($3); + YYERROR; + } + if (!pf->anchor->name[0]) { + if (pfctl_file_fingerprints(pf->dev, + pf->opts, $3)) { + yyerror("error loading " + "fingerprints %s", $3); + free($3); + YYERROR; + } + } + free($3); + } + | SET STATEPOLICY statelock { + if (pf->opts & PF_OPT_VERBOSE) + switch ($3) { + case 0: + printf("set state-policy floating\n"); + break; + case PFRULE_IFBOUND: + printf("set state-policy if-bound\n"); + break; + } + default_statelock = $3; + } + | SET DEBUG STRING { + if (check_rulestate(PFCTL_STATE_OPTION)) { + free($3); + YYERROR; + } + if (pfctl_set_debug(pf, $3) != 0) { + yyerror("error setting debuglevel %s", $3); + free($3); + YYERROR; + } + free($3); + } + | SET SKIP interface { + if (expand_skip_interface($3) != 0) { + yyerror("error setting skip interface(s)"); + YYERROR; + } + } + | SET STATEDEFAULTS state_opt_list { + if (keep_state_defaults != NULL) { + yyerror("cannot redefine state-defaults"); + YYERROR; + } + keep_state_defaults = $3; + } + ; + +stringall : STRING { $$ = $1; } + | ALL { + if (($$ = strdup("all")) == NULL) { + err(1, "stringall: strdup"); + } + } + ; + +string : STRING string { + if (asprintf(&$$, "%s %s", $1, $2) == -1) + err(1, "string: asprintf"); + free($1); + free($2); + } + | STRING + ; + +varstring : numberstring varstring { + if (asprintf(&$$, "%s %s", $1, $2) == -1) + err(1, "string: asprintf"); + free($1); + free($2); + } + | numberstring + ; + +numberstring : NUMBER { + char *s; + if (asprintf(&s, "%lld", (long long)$1) == -1) { + yyerror("string: asprintf"); + YYERROR; + } + $$ = s; + } + | STRING + ; + +varset : STRING '=' varstring { + if (pf->opts & PF_OPT_VERBOSE) + printf("%s = \"%s\"\n", $1, $3); + if (symset($1, $3, 0) == -1) + err(1, "cannot store variable %s", $1); + free($1); + free($3); + } + ; + +anchorname : STRING { $$ = $1; } + | /* empty */ { $$ = NULL; } + ; + +pfa_anchorlist : /* empty */ + | pfa_anchorlist '\n' + | pfa_anchorlist pfrule '\n' + | pfa_anchorlist anchorrule '\n' + ; + +pfa_anchor : '{' + { + char ta[PF_ANCHOR_NAME_SIZE]; + struct pf_ruleset *rs; + + /* steping into a brace anchor */ + pf->asd++; + pf->bn++; + pf->brace = 1; + + /* create a holding ruleset in the root */ + snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn); + rs = pf_find_or_create_ruleset(ta); + if (rs == NULL) + err(1, "pfa_anchor: pf_find_or_create_ruleset"); + pf->astack[pf->asd] = rs->anchor; + pf->anchor = rs->anchor; + } '\n' pfa_anchorlist '}' + { + pf->alast = pf->anchor; + pf->asd--; + pf->anchor = pf->astack[pf->asd]; + } + | /* empty */ + ; + +anchorrule : ANCHOR anchorname dir quick interface af proto fromto + filter_opts pfa_anchor + { + struct pf_rule r; + struct node_proto *proto; + + if (check_rulestate(PFCTL_STATE_FILTER)) { + if ($2) + free($2); + YYERROR; + } + + if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { + free($2); + yyerror("anchor names beginning with '_' " + "are reserved for internal use"); + YYERROR; + } + + memset(&r, 0, sizeof(r)); + if (pf->astack[pf->asd + 1]) { + /* move inline rules into relative location */ + pf_anchor_setup(&r, + &pf->astack[pf->asd]->ruleset, + $2 ? $2 : pf->alast->name); + + if (r.anchor == NULL) + err(1, "anchorrule: unable to " + "create ruleset"); + + if (pf->alast != r.anchor) { + if (r.anchor->match) { + yyerror("inline anchor '%s' " + "already exists", + r.anchor->name); + YYERROR; + } + mv_rules(&pf->alast->ruleset, + &r.anchor->ruleset); + } + pf_remove_if_empty_ruleset(&pf->alast->ruleset); + pf->alast = r.anchor; + } else { + if (!$2) { + yyerror("anchors without explicit " + "rules must specify a name"); + YYERROR; + } + } + r.direction = $3; + r.quick = $4.quick; + r.af = $6; + r.prob = $9.prob; + r.rtableid = $9.rtableid; + + if ($9.tag) + if (strlcpy(r.tagname, $9.tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + if ($9.match_tag) + if (strlcpy(r.match_tagname, $9.match_tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + r.match_tag_not = $9.match_tag_not; + if (rule_label(&r, $9.label)) + YYERROR; + free($9.label); + r.flags = $9.flags.b1; + r.flagset = $9.flags.b2; + if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { + yyerror("flags always false"); + YYERROR; + } + if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { + for (proto = $7; proto != NULL && + proto->proto != IPPROTO_TCP; + proto = proto->next) + ; /* nothing */ + if (proto == NULL && $7 != NULL) { + if ($9.flags.b1 || $9.flags.b2) + yyerror( + "flags only apply to tcp"); + if ($8.src_os) + yyerror( + "OS fingerprinting only " + "applies to tcp"); + YYERROR; + } + } + + r.tos = $9.tos; + + if ($9.keep.action) { + yyerror("cannot specify state handling " + "on anchors"); + YYERROR; + } + + if ($9.match_tag) + if (strlcpy(r.match_tagname, $9.match_tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + r.match_tag_not = $9.match_tag_not; + + decide_address_family($8.src.host, &r.af); + decide_address_family($8.dst.host, &r.af); + + expand_rule(&r, $5, NULL, $7, $8.src_os, + $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, + $9.uid, $9.gid, $9.icmpspec, + pf->astack[pf->asd + 1] ? pf->alast->name : $2); + free($2); + pf->astack[pf->asd + 1] = NULL; + } + | NATANCHOR string interface af proto fromto rtable { + struct pf_rule r; + + if (check_rulestate(PFCTL_STATE_NAT)) { + free($2); + YYERROR; + } + + memset(&r, 0, sizeof(r)); + r.action = PF_NAT; + r.af = $4; + r.rtableid = $7; + + decide_address_family($6.src.host, &r.af); + decide_address_family($6.dst.host, &r.af); + + expand_rule(&r, $3, NULL, $5, $6.src_os, + $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, + 0, 0, 0, $2); + free($2); + } + | RDRANCHOR string interface af proto fromto rtable { + struct pf_rule r; + + if (check_rulestate(PFCTL_STATE_NAT)) { + free($2); + YYERROR; + } + + memset(&r, 0, sizeof(r)); + r.action = PF_RDR; + r.af = $4; + r.rtableid = $7; + + decide_address_family($6.src.host, &r.af); + decide_address_family($6.dst.host, &r.af); + + if ($6.src.port != NULL) { + yyerror("source port parameter not supported" + " in rdr-anchor"); + YYERROR; + } + if ($6.dst.port != NULL) { + if ($6.dst.port->next != NULL) { + yyerror("destination port list " + "expansion not supported in " + "rdr-anchor"); + YYERROR; + } else if ($6.dst.port->op != PF_OP_EQ) { + yyerror("destination port operators" + " not supported in rdr-anchor"); + YYERROR; + } + r.dst.port[0] = $6.dst.port->port[0]; + r.dst.port[1] = $6.dst.port->port[1]; + r.dst.port_op = $6.dst.port->op; + } + + expand_rule(&r, $3, NULL, $5, $6.src_os, + $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, + 0, 0, 0, $2); + free($2); + } + | BINATANCHOR string interface af proto fromto rtable { + struct pf_rule r; + + if (check_rulestate(PFCTL_STATE_NAT)) { + free($2); + YYERROR; + } + + memset(&r, 0, sizeof(r)); + r.action = PF_BINAT; + r.af = $4; + r.rtableid = $7; + if ($5 != NULL) { + if ($5->next != NULL) { + yyerror("proto list expansion" + " not supported in binat-anchor"); + YYERROR; + } + r.proto = $5->proto; + free($5); + } + + if ($6.src.host != NULL || $6.src.port != NULL || + $6.dst.host != NULL || $6.dst.port != NULL) { + yyerror("fromto parameter not supported" + " in binat-anchor"); + YYERROR; + } + + decide_address_family($6.src.host, &r.af); + decide_address_family($6.dst.host, &r.af); + + pfctl_add_rule(pf, &r, $2); + free($2); + } + ; + +loadrule : LOAD ANCHOR string FROM string { + struct loadanchors *loadanchor; + + if (strlen(pf->anchor->name) + 1 + + strlen($3) >= MAXPATHLEN) { + yyerror("anchorname %s too long, max %u\n", + $3, MAXPATHLEN - 1); + free($3); + YYERROR; + } + loadanchor = calloc(1, sizeof(struct loadanchors)); + if (loadanchor == NULL) + err(1, "loadrule: calloc"); + if ((loadanchor->anchorname = malloc(MAXPATHLEN)) == + NULL) + err(1, "loadrule: malloc"); + if (pf->anchor->name[0]) + snprintf(loadanchor->anchorname, MAXPATHLEN, + "%s/%s", pf->anchor->name, $3); + else + strlcpy(loadanchor->anchorname, $3, MAXPATHLEN); + if ((loadanchor->filename = strdup($5)) == NULL) + err(1, "loadrule: strdup"); + + TAILQ_INSERT_TAIL(&loadanchorshead, loadanchor, + entries); + + free($3); + free($5); + }; + +scrubaction : no SCRUB { + $$.b2 = $$.w = 0; + if ($1) + $$.b1 = PF_NOSCRUB; + else + $$.b1 = PF_SCRUB; + } + ; + +scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts + { + struct pf_rule r; + + if (check_rulestate(PFCTL_STATE_SCRUB)) + YYERROR; + + memset(&r, 0, sizeof(r)); + + r.action = $1.b1; + r.direction = $2; + + r.log = $3.log; + r.logif = $3.logif; + if ($3.quick) { + yyerror("scrub rules do not support 'quick'"); + YYERROR; + } + + r.af = $5; + if ($8.nodf) + r.rule_flag |= PFRULE_NODF; + if ($8.randomid) + r.rule_flag |= PFRULE_RANDOMID; + if ($8.reassemble_tcp) { + if (r.direction != PF_INOUT) { + yyerror("reassemble tcp rules can not " + "specify direction"); + YYERROR; + } + r.rule_flag |= PFRULE_REASSEMBLE_TCP; + } + if ($8.minttl) + r.min_ttl = $8.minttl; + if ($8.maxmss) + r.max_mss = $8.maxmss; + if ($8.marker & SOM_SETTOS) { + r.rule_flag |= PFRULE_SET_TOS; + r.set_tos = $8.settos; + } + if ($8.fragcache) + r.rule_flag |= $8.fragcache; + if ($8.match_tag) + if (strlcpy(r.match_tagname, $8.match_tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + r.match_tag_not = $8.match_tag_not; + r.rtableid = $8.rtableid; + + expand_rule(&r, $4, NULL, $6, $7.src_os, + $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, + NULL, NULL, NULL, ""); + } + ; + +scrub_opts : { + bzero(&scrub_opts, sizeof scrub_opts); + scrub_opts.rtableid = -1; + } + scrub_opts_l + { $$ = scrub_opts; } + | /* empty */ { + bzero(&scrub_opts, sizeof scrub_opts); + scrub_opts.rtableid = -1; + $$ = scrub_opts; + } + ; + +scrub_opts_l : scrub_opts_l scrub_opt + | scrub_opt + ; + +scrub_opt : NODF { + if (scrub_opts.nodf) { + yyerror("no-df cannot be respecified"); + YYERROR; + } + scrub_opts.nodf = 1; + } + | MINTTL NUMBER { + if (scrub_opts.marker & SOM_MINTTL) { + yyerror("min-ttl cannot be respecified"); + YYERROR; + } + if ($2 < 0 || $2 > 255) { + yyerror("illegal min-ttl value %d", $2); + YYERROR; + } + scrub_opts.marker |= SOM_MINTTL; + scrub_opts.minttl = $2; + } + | MAXMSS NUMBER { + if (scrub_opts.marker & SOM_MAXMSS) { + yyerror("max-mss cannot be respecified"); + YYERROR; + } + if ($2 < 0 || $2 > 65535) { + yyerror("illegal max-mss value %d", $2); + YYERROR; + } + scrub_opts.marker |= SOM_MAXMSS; + scrub_opts.maxmss = $2; + } + | SETTOS tos { + if (scrub_opts.marker & SOM_SETTOS) { + yyerror("set-tos cannot be respecified"); + YYERROR; + } + scrub_opts.marker |= SOM_SETTOS; + scrub_opts.settos = $2; + } + | fragcache { + if (scrub_opts.marker & SOM_FRAGCACHE) { + yyerror("fragcache cannot be respecified"); + YYERROR; + } + scrub_opts.marker |= SOM_FRAGCACHE; + scrub_opts.fragcache = $1; + } + | REASSEMBLE STRING { + if (strcasecmp($2, "tcp") != 0) { + yyerror("scrub reassemble supports only tcp, " + "not '%s'", $2); + free($2); + YYERROR; + } + free($2); + if (scrub_opts.reassemble_tcp) { + yyerror("reassemble tcp cannot be respecified"); + YYERROR; + } + scrub_opts.reassemble_tcp = 1; + } + | RANDOMID { + if (scrub_opts.randomid) { + yyerror("random-id cannot be respecified"); + YYERROR; + } + scrub_opts.randomid = 1; + } + | RTABLE NUMBER { + if ($2 < 0 || $2 > rt_tableid_max()) { + yyerror("invalid rtable id"); + YYERROR; + } + scrub_opts.rtableid = $2; + } + | not TAGGED string { + scrub_opts.match_tag = $3; + scrub_opts.match_tag_not = $1; + } + ; + +fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ } + | FRAGMENT FRAGCROP { $$ = PFRULE_FRAGCROP; } + | FRAGMENT FRAGDROP { $$ = PFRULE_FRAGDROP; } + ; + +antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { + struct pf_rule r; + struct node_host *h = NULL, *hh; + struct node_if *i, *j; + + if (check_rulestate(PFCTL_STATE_FILTER)) + YYERROR; + + for (i = $3; i; i = i->next) { + bzero(&r, sizeof(r)); + + r.action = PF_DROP; + r.direction = PF_IN; + r.log = $2.log; + r.logif = $2.logif; + r.quick = $2.quick; + r.af = $4; + if (rule_label(&r, $5.label)) + YYERROR; + r.rtableid = $5.rtableid; + j = calloc(1, sizeof(struct node_if)); + if (j == NULL) + err(1, "antispoof: calloc"); + if (strlcpy(j->ifname, i->ifname, + sizeof(j->ifname)) >= sizeof(j->ifname)) { + free(j); + yyerror("interface name too long"); + YYERROR; + } + j->not = 1; + if (i->dynamic) { + h = calloc(1, sizeof(*h)); + if (h == NULL) + err(1, "address: calloc"); + h->addr.type = PF_ADDR_DYNIFTL; + set_ipmask(h, 128); + if (strlcpy(h->addr.v.ifname, i->ifname, + sizeof(h->addr.v.ifname)) >= + sizeof(h->addr.v.ifname)) { + free(h); + yyerror( + "interface name too long"); + YYERROR; + } + hh = malloc(sizeof(*hh)); + if (hh == NULL) + err(1, "address: malloc"); + bcopy(h, hh, sizeof(*hh)); + h->addr.iflags = PFI_AFLAG_NETWORK; + } else { + h = ifa_lookup(j->ifname, + PFI_AFLAG_NETWORK); + hh = NULL; + } + + if (h != NULL) + expand_rule(&r, j, NULL, NULL, NULL, h, + NULL, NULL, NULL, NULL, NULL, + NULL, ""); + + if ((i->ifa_flags & IFF_LOOPBACK) == 0) { + bzero(&r, sizeof(r)); + + r.action = PF_DROP; + r.direction = PF_IN; + r.log = $2.log; + r.logif = $2.logif; + r.quick = $2.quick; + r.af = $4; + if (rule_label(&r, $5.label)) + YYERROR; + r.rtableid = $5.rtableid; + if (hh != NULL) + h = hh; + else + h = ifa_lookup(i->ifname, 0); + if (h != NULL) + expand_rule(&r, NULL, NULL, + NULL, NULL, h, NULL, NULL, + NULL, NULL, NULL, NULL, ""); + } else + free(hh); + } + free($5.label); + } + ; + +antispoof_ifspc : FOR antispoof_if { $$ = $2; } + | FOR '{' optnl antispoof_iflst '}' { $$ = $4; } + ; + +antispoof_iflst : antispoof_if optnl { $$ = $1; } + | antispoof_iflst comma antispoof_if optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +antispoof_if : if_item { $$ = $1; } + | '(' if_item ')' { + $2->dynamic = 1; + $$ = $2; + } + ; + +antispoof_opts : { + bzero(&antispoof_opts, sizeof antispoof_opts); + antispoof_opts.rtableid = -1; + } + antispoof_opts_l + { $$ = antispoof_opts; } + | /* empty */ { + bzero(&antispoof_opts, sizeof antispoof_opts); + antispoof_opts.rtableid = -1; + $$ = antispoof_opts; + } + ; + +antispoof_opts_l : antispoof_opts_l antispoof_opt + | antispoof_opt + ; + +antispoof_opt : label { + if (antispoof_opts.label) { + yyerror("label cannot be redefined"); + YYERROR; + } + antispoof_opts.label = $1; + } + | RTABLE NUMBER { + if ($2 < 0 || $2 > rt_tableid_max()) { + yyerror("invalid rtable id"); + YYERROR; + } + antispoof_opts.rtableid = $2; + } + ; + +not : '!' { $$ = 1; } + | /* empty */ { $$ = 0; } + ; + +tabledef : TABLE '<' STRING '>' table_opts { + struct node_host *h, *nh; + struct node_tinit *ti, *nti; + + if (strlen($3) >= PF_TABLE_NAME_SIZE) { + yyerror("table name too long, max %d chars", + PF_TABLE_NAME_SIZE - 1); + free($3); + YYERROR; + } + if (pf->loadopt & PFCTL_FLAG_TABLE) + if (process_tabledef($3, &$5)) { + free($3); + YYERROR; + } + free($3); + for (ti = SIMPLEQ_FIRST(&$5.init_nodes); + ti != SIMPLEQ_END(&$5.init_nodes); ti = nti) { + if (ti->file) + free(ti->file); + for (h = ti->host; h != NULL; h = nh) { + nh = h->next; + free(h); + } + nti = SIMPLEQ_NEXT(ti, entries); + free(ti); + } + } + ; + +table_opts : { + bzero(&table_opts, sizeof table_opts); + SIMPLEQ_INIT(&table_opts.init_nodes); + } + table_opts_l + { $$ = table_opts; } + | /* empty */ + { + bzero(&table_opts, sizeof table_opts); + SIMPLEQ_INIT(&table_opts.init_nodes); + $$ = table_opts; + } + ; + +table_opts_l : table_opts_l table_opt + | table_opt + ; + +table_opt : STRING { + if (!strcmp($1, "const")) + table_opts.flags |= PFR_TFLAG_CONST; + else if (!strcmp($1, "persist")) + table_opts.flags |= PFR_TFLAG_PERSIST; + else if (!strcmp($1, "counters")) + table_opts.flags |= PFR_TFLAG_COUNTERS; + else { + yyerror("invalid table option '%s'", $1); + free($1); + YYERROR; + } + free($1); + } + | '{' optnl '}' { table_opts.init_addr = 1; } + | '{' optnl host_list '}' { + struct node_host *n; + struct node_tinit *ti; + + for (n = $3; n != NULL; n = n->next) { + switch (n->addr.type) { + case PF_ADDR_ADDRMASK: + continue; /* ok */ + case PF_ADDR_RANGE: + yyerror("address ranges are not " + "permitted inside tables"); + break; + case PF_ADDR_DYNIFTL: + yyerror("dynamic addresses are not " + "permitted inside tables"); + break; + case PF_ADDR_TABLE: + yyerror("tables cannot contain tables"); + break; + case PF_ADDR_NOROUTE: + yyerror("\"no-route\" is not permitted " + "inside tables"); + break; + case PF_ADDR_URPFFAILED: + yyerror("\"urpf-failed\" is not " + "permitted inside tables"); + break; + default: + yyerror("unknown address type %d", + n->addr.type); + } + YYERROR; + } + if (!(ti = calloc(1, sizeof(*ti)))) + err(1, "table_opt: calloc"); + ti->host = $3; + SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti, + entries); + table_opts.init_addr = 1; + } + | FILENAME STRING { + struct node_tinit *ti; + + if (!(ti = calloc(1, sizeof(*ti)))) + err(1, "table_opt: calloc"); + ti->file = $2; + SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti, + entries); + table_opts.init_addr = 1; + } + ; + +altqif : ALTQ interface queue_opts QUEUE qassign { + struct pf_altq a; + + if (check_rulestate(PFCTL_STATE_QUEUE)) + YYERROR; + + memset(&a, 0, sizeof(a)); + if ($3.scheduler.qtype == ALTQT_NONE) { + yyerror("no scheduler specified!"); + YYERROR; + } + a.scheduler = $3.scheduler.qtype; + a.qlimit = $3.qlimit; + a.tbrsize = $3.tbrsize; + if ($5 == NULL) { + yyerror("no child queues specified"); + YYERROR; + } + if (expand_altq(&a, $2, $5, $3.queue_bwspec, + &$3.scheduler)) + YYERROR; + } + ; + +queuespec : QUEUE STRING interface queue_opts qassign { + struct pf_altq a; + + if (check_rulestate(PFCTL_STATE_QUEUE)) { + free($2); + YYERROR; + } + + memset(&a, 0, sizeof(a)); + + if (strlcpy(a.qname, $2, sizeof(a.qname)) >= + sizeof(a.qname)) { + yyerror("queue name too long (max " + "%d chars)", PF_QNAME_SIZE-1); + free($2); + YYERROR; + } + free($2); + if ($4.tbrsize) { + yyerror("cannot specify tbrsize for queue"); + YYERROR; + } + if ($4.priority > 255) { + yyerror("priority out of range: max 255"); + YYERROR; + } + a.priority = $4.priority; + a.qlimit = $4.qlimit; + a.scheduler = $4.scheduler.qtype; + if (expand_queue(&a, $3, $5, $4.queue_bwspec, + &$4.scheduler)) { + yyerror("errors in queue definition"); + YYERROR; + } + } + ; + +queue_opts : { + bzero(&queue_opts, sizeof queue_opts); + queue_opts.priority = DEFAULT_PRIORITY; + queue_opts.qlimit = DEFAULT_QLIMIT; + queue_opts.scheduler.qtype = ALTQT_NONE; + queue_opts.queue_bwspec.bw_percent = 100; + } + queue_opts_l + { $$ = queue_opts; } + | /* empty */ { + bzero(&queue_opts, sizeof queue_opts); + queue_opts.priority = DEFAULT_PRIORITY; + queue_opts.qlimit = DEFAULT_QLIMIT; + queue_opts.scheduler.qtype = ALTQT_NONE; + queue_opts.queue_bwspec.bw_percent = 100; + $$ = queue_opts; + } + ; + +queue_opts_l : queue_opts_l queue_opt + | queue_opt + ; + +queue_opt : BANDWIDTH bandwidth { + if (queue_opts.marker & QOM_BWSPEC) { + yyerror("bandwidth cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_BWSPEC; + queue_opts.queue_bwspec = $2; + } + | PRIORITY NUMBER { + if (queue_opts.marker & QOM_PRIORITY) { + yyerror("priority cannot be respecified"); + YYERROR; + } + if ($2 < 0 || $2 > 255) { + yyerror("priority out of range: max 255"); + YYERROR; + } + queue_opts.marker |= QOM_PRIORITY; + queue_opts.priority = $2; + } + | QLIMIT NUMBER { + if (queue_opts.marker & QOM_QLIMIT) { + yyerror("qlimit cannot be respecified"); + YYERROR; + } + if ($2 < 0 || $2 > 65535) { + yyerror("qlimit out of range: max 65535"); + YYERROR; + } + queue_opts.marker |= QOM_QLIMIT; + queue_opts.qlimit = $2; + } + | scheduler { + if (queue_opts.marker & QOM_SCHEDULER) { + yyerror("scheduler cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_SCHEDULER; + queue_opts.scheduler = $1; + } + | TBRSIZE NUMBER { + if (queue_opts.marker & QOM_TBRSIZE) { + yyerror("tbrsize cannot be respecified"); + YYERROR; + } + if ($2 < 0 || $2 > 65535) { + yyerror("tbrsize too big: max 65535"); + YYERROR; + } + queue_opts.marker |= QOM_TBRSIZE; + queue_opts.tbrsize = $2; + } + ; + +bandwidth : STRING { + double bps; + char *cp; + + $$.bw_percent = 0; + + bps = strtod($1, &cp); + if (cp != NULL) { + if (!strcmp(cp, "b")) + ; /* nothing */ + else if (!strcmp(cp, "Kb")) + bps *= 1000; + else if (!strcmp(cp, "Mb")) + bps *= 1000 * 1000; + else if (!strcmp(cp, "Gb")) + bps *= 1000 * 1000 * 1000; + else if (!strcmp(cp, "%")) { + if (bps < 0 || bps > 100) { + yyerror("bandwidth spec " + "out of range"); + free($1); + YYERROR; + } + $$.bw_percent = bps; + bps = 0; + } else { + yyerror("unknown unit %s", cp); + free($1); + YYERROR; + } + } + free($1); + $$.bw_absolute = (u_int32_t)bps; + } + | NUMBER { + if ($1 < 0 || $1 > UINT_MAX) { + yyerror("bandwidth number too big"); + YYERROR; + } + $$.bw_percent = 0; + $$.bw_absolute = $1; + } + ; + +scheduler : CBQ { + $$.qtype = ALTQT_CBQ; + $$.data.cbq_opts.flags = 0; + } + | CBQ '(' cbqflags_list ')' { + $$.qtype = ALTQT_CBQ; + $$.data.cbq_opts.flags = $3; + } + | PRIQ { + $$.qtype = ALTQT_PRIQ; + $$.data.priq_opts.flags = 0; + } + | PRIQ '(' priqflags_list ')' { + $$.qtype = ALTQT_PRIQ; + $$.data.priq_opts.flags = $3; + } + | HFSC { + $$.qtype = ALTQT_HFSC; + bzero(&$$.data.hfsc_opts, + sizeof(struct node_hfsc_opts)); + } + | HFSC '(' hfsc_opts ')' { + $$.qtype = ALTQT_HFSC; + $$.data.hfsc_opts = $3; + } + ; + +cbqflags_list : cbqflags_item { $$ |= $1; } + | cbqflags_list comma cbqflags_item { $$ |= $3; } + ; + +cbqflags_item : STRING { + if (!strcmp($1, "default")) + $$ = CBQCLF_DEFCLASS; + else if (!strcmp($1, "borrow")) + $$ = CBQCLF_BORROW; + else if (!strcmp($1, "red")) + $$ = CBQCLF_RED; + else if (!strcmp($1, "ecn")) + $$ = CBQCLF_RED|CBQCLF_ECN; + else if (!strcmp($1, "rio")) + $$ = CBQCLF_RIO; + else { + yyerror("unknown cbq flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + ; + +priqflags_list : priqflags_item { $$ |= $1; } + | priqflags_list comma priqflags_item { $$ |= $3; } + ; + +priqflags_item : STRING { + if (!strcmp($1, "default")) + $$ = PRCF_DEFAULTCLASS; + else if (!strcmp($1, "red")) + $$ = PRCF_RED; + else if (!strcmp($1, "ecn")) + $$ = PRCF_RED|PRCF_ECN; + else if (!strcmp($1, "rio")) + $$ = PRCF_RIO; + else { + yyerror("unknown priq flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + ; + +hfsc_opts : { + bzero(&hfsc_opts, + sizeof(struct node_hfsc_opts)); + } + hfscopts_list { + $$ = hfsc_opts; + } + ; + +hfscopts_list : hfscopts_item + | hfscopts_list comma hfscopts_item + ; + +hfscopts_item : LINKSHARE bandwidth { + if (hfsc_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + hfsc_opts.linkshare.m2 = $2; + hfsc_opts.linkshare.used = 1; + } + | LINKSHARE '(' bandwidth comma NUMBER comma bandwidth ')' + { + if ($5 < 0 || $5 > INT_MAX) { + yyerror("timing in curve out of range"); + YYERROR; + } + if (hfsc_opts.linkshare.used) { + yyerror("linkshare already specified"); + YYERROR; + } + hfsc_opts.linkshare.m1 = $3; + hfsc_opts.linkshare.d = $5; + hfsc_opts.linkshare.m2 = $7; + hfsc_opts.linkshare.used = 1; + } + | REALTIME bandwidth { + if (hfsc_opts.realtime.used) { + yyerror("realtime already specified"); + YYERROR; + } + hfsc_opts.realtime.m2 = $2; + hfsc_opts.realtime.used = 1; + } + | REALTIME '(' bandwidth comma NUMBER comma bandwidth ')' + { + if ($5 < 0 || $5 > INT_MAX) { + yyerror("timing in curve out of range"); + YYERROR; + } + if (hfsc_opts.realtime.used) { + yyerror("realtime already specified"); + YYERROR; + } + hfsc_opts.realtime.m1 = $3; + hfsc_opts.realtime.d = $5; + hfsc_opts.realtime.m2 = $7; + hfsc_opts.realtime.used = 1; + } + | UPPERLIMIT bandwidth { + if (hfsc_opts.upperlimit.used) { + yyerror("upperlimit already specified"); + YYERROR; + } + hfsc_opts.upperlimit.m2 = $2; + hfsc_opts.upperlimit.used = 1; + } + | UPPERLIMIT '(' bandwidth comma NUMBER comma bandwidth ')' + { + if ($5 < 0 || $5 > INT_MAX) { + yyerror("timing in curve out of range"); + YYERROR; + } + if (hfsc_opts.upperlimit.used) { + yyerror("upperlimit already specified"); + YYERROR; + } + hfsc_opts.upperlimit.m1 = $3; + hfsc_opts.upperlimit.d = $5; + hfsc_opts.upperlimit.m2 = $7; + hfsc_opts.upperlimit.used = 1; + } + | STRING { + if (!strcmp($1, "default")) + hfsc_opts.flags |= HFCF_DEFAULTCLASS; + else if (!strcmp($1, "red")) + hfsc_opts.flags |= HFCF_RED; + else if (!strcmp($1, "ecn")) + hfsc_opts.flags |= HFCF_RED|HFCF_ECN; + else if (!strcmp($1, "rio")) + hfsc_opts.flags |= HFCF_RIO; + else { + yyerror("unknown hfsc flag \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + } + ; + +qassign : /* empty */ { $$ = NULL; } + | qassign_item { $$ = $1; } + | '{' optnl qassign_list '}' { $$ = $3; } + ; + +qassign_list : qassign_item optnl { $$ = $1; } + | qassign_list comma qassign_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +qassign_item : STRING { + $$ = calloc(1, sizeof(struct node_queue)); + if ($$ == NULL) + err(1, "qassign_item: calloc"); + if (strlcpy($$->queue, $1, sizeof($$->queue)) >= + sizeof($$->queue)) { + yyerror("queue name '%s' too long (max " + "%d chars)", $1, sizeof($$->queue)-1); + free($1); + free($$); + YYERROR; + } + free($1); + $$->next = NULL; + $$->tail = $$; + } + ; + +pfrule : action dir logquick interface route af proto fromto + filter_opts + { + struct pf_rule r; + struct node_state_opt *o; + struct node_proto *proto; + int srctrack = 0; + int statelock = 0; + int adaptive = 0; + int defaults = 0; + + if (check_rulestate(PFCTL_STATE_FILTER)) + YYERROR; + + memset(&r, 0, sizeof(r)); + + r.action = $1.b1; + switch ($1.b2) { + case PFRULE_RETURNRST: + r.rule_flag |= PFRULE_RETURNRST; + r.return_ttl = $1.w; + break; + case PFRULE_RETURNICMP: + r.rule_flag |= PFRULE_RETURNICMP; + r.return_icmp = $1.w; + r.return_icmp6 = $1.w2; + break; + case PFRULE_RETURN: + r.rule_flag |= PFRULE_RETURN; + r.return_icmp = $1.w; + r.return_icmp6 = $1.w2; + break; + } + r.direction = $2; + r.log = $3.log; + r.logif = $3.logif; + r.quick = $3.quick; + r.prob = $9.prob; + r.rtableid = $9.rtableid; + + r.af = $6; + if ($9.tag) + if (strlcpy(r.tagname, $9.tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + if ($9.match_tag) + if (strlcpy(r.match_tagname, $9.match_tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + r.match_tag_not = $9.match_tag_not; + if (rule_label(&r, $9.label)) + YYERROR; + free($9.label); + r.flags = $9.flags.b1; + r.flagset = $9.flags.b2; + if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { + yyerror("flags always false"); + YYERROR; + } + if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { + for (proto = $7; proto != NULL && + proto->proto != IPPROTO_TCP; + proto = proto->next) + ; /* nothing */ + if (proto == NULL && $7 != NULL) { + if ($9.flags.b1 || $9.flags.b2) + yyerror( + "flags only apply to tcp"); + if ($8.src_os) + yyerror( + "OS fingerprinting only " + "apply to tcp"); + YYERROR; + } +#if 0 + if (($9.flags.b1 & parse_flags("S")) == 0 && + $8.src_os) { + yyerror("OS fingerprinting requires " + "the SYN TCP flag (flags S/SA)"); + YYERROR; + } +#endif + } + + r.tos = $9.tos; + r.keep_state = $9.keep.action; + o = $9.keep.options; + + /* 'keep state' by default on pass rules. */ + if (!r.keep_state && !r.action && + !($9.marker & FOM_KEEP)) { + r.keep_state = PF_STATE_NORMAL; + o = keep_state_defaults; + defaults = 1; + } + + while (o) { + struct node_state_opt *p = o; + + switch (o->type) { + case PF_STATE_OPT_MAX: + if (r.max_states) { + yyerror("state option 'max' " + "multiple definitions"); + YYERROR; + } + r.max_states = o->data.max_states; + break; + case PF_STATE_OPT_NOSYNC: + if (r.rule_flag & PFRULE_NOSYNC) { + yyerror("state option 'sync' " + "multiple definitions"); + YYERROR; + } + r.rule_flag |= PFRULE_NOSYNC; + break; + case PF_STATE_OPT_SRCTRACK: + if (srctrack) { + yyerror("state option " + "'source-track' " + "multiple definitions"); + YYERROR; + } + srctrack = o->data.src_track; + r.rule_flag |= PFRULE_SRCTRACK; + break; + case PF_STATE_OPT_MAX_SRC_STATES: + if (r.max_src_states) { + yyerror("state option " + "'max-src-states' " + "multiple definitions"); + YYERROR; + } + if (o->data.max_src_states == 0) { + yyerror("'max-src-states' must " + "be > 0"); + YYERROR; + } + r.max_src_states = + o->data.max_src_states; + r.rule_flag |= PFRULE_SRCTRACK; + break; + case PF_STATE_OPT_OVERLOAD: + if (r.overload_tblname[0]) { + yyerror("multiple 'overload' " + "table definitions"); + YYERROR; + } + if (strlcpy(r.overload_tblname, + o->data.overload.tblname, + PF_TABLE_NAME_SIZE) >= + PF_TABLE_NAME_SIZE) { + yyerror("state option: " + "strlcpy"); + YYERROR; + } + r.flush = o->data.overload.flush; + break; + case PF_STATE_OPT_MAX_SRC_CONN: + if (r.max_src_conn) { + yyerror("state option " + "'max-src-conn' " + "multiple definitions"); + YYERROR; + } + if (o->data.max_src_conn == 0) { + yyerror("'max-src-conn' " + "must be > 0"); + YYERROR; + } + r.max_src_conn = + o->data.max_src_conn; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; + case PF_STATE_OPT_MAX_SRC_CONN_RATE: + if (r.max_src_conn_rate.limit) { + yyerror("state option " + "'max-src-conn-rate' " + "multiple definitions"); + YYERROR; + } + if (!o->data.max_src_conn_rate.limit || + !o->data.max_src_conn_rate.seconds) { + yyerror("'max-src-conn-rate' " + "values must be > 0"); + YYERROR; + } + if (o->data.max_src_conn_rate.limit > + PF_THRESHOLD_MAX) { + yyerror("'max-src-conn-rate' " + "maximum rate must be < %u", + PF_THRESHOLD_MAX); + YYERROR; + } + r.max_src_conn_rate.limit = + o->data.max_src_conn_rate.limit; + r.max_src_conn_rate.seconds = + o->data.max_src_conn_rate.seconds; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; + case PF_STATE_OPT_MAX_SRC_NODES: + if (r.max_src_nodes) { + yyerror("state option " + "'max-src-nodes' " + "multiple definitions"); + YYERROR; + } + if (o->data.max_src_nodes == 0) { + yyerror("'max-src-nodes' must " + "be > 0"); + YYERROR; + } + r.max_src_nodes = + o->data.max_src_nodes; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; + case PF_STATE_OPT_STATELOCK: + if (statelock) { + yyerror("state locking option: " + "multiple definitions"); + YYERROR; + } + statelock = 1; + r.rule_flag |= o->data.statelock; + break; + case PF_STATE_OPT_SLOPPY: + if (r.rule_flag & PFRULE_STATESLOPPY) { + yyerror("state sloppy option: " + "multiple definitions"); + YYERROR; + } + r.rule_flag |= PFRULE_STATESLOPPY; + break; + case PF_STATE_OPT_TIMEOUT: + if (o->data.timeout.number == + PFTM_ADAPTIVE_START || + o->data.timeout.number == + PFTM_ADAPTIVE_END) + adaptive = 1; + if (r.timeout[o->data.timeout.number]) { + yyerror("state timeout %s " + "multiple definitions", + pf_timeouts[o->data. + timeout.number].name); + YYERROR; + } + r.timeout[o->data.timeout.number] = + o->data.timeout.seconds; + } + o = o->next; + if (!defaults) + free(p); + } + + /* 'flags S/SA' by default on stateful rules */ + if (!r.action && !r.flags && !r.flagset && + !$9.fragment && !($9.marker & FOM_FLAGS) && + r.keep_state) { + r.flags = parse_flags("S"); + r.flagset = parse_flags("SA"); + } + if (!adaptive && r.max_states) { + r.timeout[PFTM_ADAPTIVE_START] = + (r.max_states / 10) * 6; + r.timeout[PFTM_ADAPTIVE_END] = + (r.max_states / 10) * 12; + } + if (r.rule_flag & PFRULE_SRCTRACK) { + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_nodes) { + yyerror("'max-src-nodes' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_conn) { + yyerror("'max-src-conn' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_conn_rate.seconds) { + yyerror("'max-src-conn-rate' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (r.timeout[PFTM_SRC_NODE] < + r.max_src_conn_rate.seconds) + r.timeout[PFTM_SRC_NODE] = + r.max_src_conn_rate.seconds; + r.rule_flag |= PFRULE_SRCTRACK; + if (srctrack == PF_SRCTRACK_RULE) + r.rule_flag |= PFRULE_RULESRCTRACK; + } + if (r.keep_state && !statelock) + r.rule_flag |= default_statelock; + + if ($9.fragment) + r.rule_flag |= PFRULE_FRAGMENT; + r.allow_opts = $9.allowopts; + + decide_address_family($8.src.host, &r.af); + decide_address_family($8.dst.host, &r.af); + + if ($5.rt) { + if (!r.direction) { + yyerror("direction must be explicit " + "with rules that specify routing"); + YYERROR; + } + r.rt = $5.rt; + r.rpool.opts = $5.pool_opts; + if ($5.key != NULL) + memcpy(&r.rpool.key, $5.key, + sizeof(struct pf_poolhashkey)); + } + if (r.rt && r.rt != PF_FASTROUTE) { + decide_address_family($5.host, &r.af); + remove_invalid_hosts(&$5.host, &r.af); + if ($5.host == NULL) { + yyerror("no routing address with " + "matching address family found."); + YYERROR; + } + if ((r.rpool.opts & PF_POOL_TYPEMASK) == + PF_POOL_NONE && ($5.host->next != NULL || + $5.host->addr.type == PF_ADDR_TABLE || + DYNIF_MULTIADDR($5.host->addr))) + r.rpool.opts |= PF_POOL_ROUNDROBIN; + if ((r.rpool.opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN && + disallow_table($5.host, "tables are only " + "supported in round-robin routing pools")) + YYERROR; + if ((r.rpool.opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN && + disallow_alias($5.host, "interface (%s) " + "is only supported in round-robin " + "routing pools")) + YYERROR; + if ($5.host->next != NULL) { + if ((r.rpool.opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) { + yyerror("r.rpool.opts must " + "be PF_POOL_ROUNDROBIN"); + YYERROR; + } + } + } + if ($9.queues.qname != NULL) { + if (strlcpy(r.qname, $9.queues.qname, + sizeof(r.qname)) >= sizeof(r.qname)) { + yyerror("rule qname too long (max " + "%d chars)", sizeof(r.qname)-1); + YYERROR; + } + free($9.queues.qname); + } + if ($9.queues.pqname != NULL) { + if (strlcpy(r.pqname, $9.queues.pqname, + sizeof(r.pqname)) >= sizeof(r.pqname)) { + yyerror("rule pqname too long (max " + "%d chars)", sizeof(r.pqname)-1); + YYERROR; + } + free($9.queues.pqname); + } +#ifdef __FreeBSD__ + r.divert.port = $9.divert.port; +#else + if ((r.divert.port = $9.divert.port)) { + if (r.direction == PF_OUT) { + if ($9.divert.addr) { + yyerror("address specified " + "for outgoing divert"); + YYERROR; + } + bzero(&r.divert.addr, + sizeof(r.divert.addr)); + } else { + if (!$9.divert.addr) { + yyerror("no address specified " + "for incoming divert"); + YYERROR; + } + if ($9.divert.addr->af != r.af) { + yyerror("address family " + "mismatch for divert"); + YYERROR; + } + r.divert.addr = + $9.divert.addr->addr.v.a.addr; + } + } +#endif + + expand_rule(&r, $4, $5.host, $7, $8.src_os, + $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, + $9.uid, $9.gid, $9.icmpspec, ""); + } + ; + +filter_opts : { + bzero(&filter_opts, sizeof filter_opts); + filter_opts.rtableid = -1; + } + filter_opts_l + { $$ = filter_opts; } + | /* empty */ { + bzero(&filter_opts, sizeof filter_opts); + filter_opts.rtableid = -1; + $$ = filter_opts; + } + ; + +filter_opts_l : filter_opts_l filter_opt + | filter_opt + ; + +filter_opt : USER uids { + if (filter_opts.uid) + $2->tail->next = filter_opts.uid; + filter_opts.uid = $2; + } + | GROUP gids { + if (filter_opts.gid) + $2->tail->next = filter_opts.gid; + filter_opts.gid = $2; + } + | flags { + if (filter_opts.marker & FOM_FLAGS) { + yyerror("flags cannot be redefined"); + YYERROR; + } + filter_opts.marker |= FOM_FLAGS; + filter_opts.flags.b1 |= $1.b1; + filter_opts.flags.b2 |= $1.b2; + filter_opts.flags.w |= $1.w; + filter_opts.flags.w2 |= $1.w2; + } + | icmpspec { + if (filter_opts.marker & FOM_ICMP) { + yyerror("icmp-type cannot be redefined"); + YYERROR; + } + filter_opts.marker |= FOM_ICMP; + filter_opts.icmpspec = $1; + } + | TOS tos { + if (filter_opts.marker & FOM_TOS) { + yyerror("tos cannot be redefined"); + YYERROR; + } + filter_opts.marker |= FOM_TOS; + filter_opts.tos = $2; + } + | keep { + if (filter_opts.marker & FOM_KEEP) { + yyerror("modulate or keep cannot be redefined"); + YYERROR; + } + filter_opts.marker |= FOM_KEEP; + filter_opts.keep.action = $1.action; + filter_opts.keep.options = $1.options; + } + | FRAGMENT { + filter_opts.fragment = 1; + } + | ALLOWOPTS { + filter_opts.allowopts = 1; + } + | label { + if (filter_opts.label) { + yyerror("label cannot be redefined"); + YYERROR; + } + filter_opts.label = $1; + } + | qname { + if (filter_opts.queues.qname) { + yyerror("queue cannot be redefined"); + YYERROR; + } + filter_opts.queues = $1; + } + | TAG string { + filter_opts.tag = $2; + } + | not TAGGED string { + filter_opts.match_tag = $3; + filter_opts.match_tag_not = $1; + } + | PROBABILITY probability { + double p; + + p = floor($2 * UINT_MAX + 0.5); + if (p < 0.0 || p > UINT_MAX) { + yyerror("invalid probability: %lf", p); + YYERROR; + } + filter_opts.prob = (u_int32_t)p; + if (filter_opts.prob == 0) + filter_opts.prob = 1; + } + | RTABLE NUMBER { + if ($2 < 0 || $2 > rt_tableid_max()) { + yyerror("invalid rtable id"); + YYERROR; + } + filter_opts.rtableid = $2; + } + | DIVERTTO portplain { +#ifdef __FreeBSD__ + filter_opts.divert.port = $2.a; + if (!filter_opts.divert.port) { + yyerror("invalid divert port: %u", ntohs($2.a)); + YYERROR; + } +#endif + } + | DIVERTTO STRING PORT portplain { +#ifndef __FreeBSD__ + if ((filter_opts.divert.addr = host($2)) == NULL) { + yyerror("could not parse divert address: %s", + $2); + free($2); + YYERROR; + } +#else + if ($2) +#endif + free($2); + filter_opts.divert.port = $4.a; + if (!filter_opts.divert.port) { + yyerror("invalid divert port: %u", ntohs($4.a)); + YYERROR; + } + } + | DIVERTREPLY { +#ifdef __FreeBSD__ + yyerror("divert-reply has no meaning in FreeBSD pf(4)"); + YYERROR; +#else + filter_opts.divert.port = 1; /* some random value */ +#endif + } + ; + +probability : STRING { + char *e; + double p = strtod($1, &e); + + if (*e == '%') { + p *= 0.01; + e++; + } + if (*e) { + yyerror("invalid probability: %s", $1); + free($1); + YYERROR; + } + free($1); + $$ = p; + } + | NUMBER { + $$ = (double)$1; + } + ; + + +action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; } + | BLOCK blockspec { $$ = $2; $$.b1 = PF_DROP; } + ; + +blockspec : /* empty */ { + $$.b2 = blockpolicy; + $$.w = returnicmpdefault; + $$.w2 = returnicmp6default; + } + | DROP { + $$.b2 = PFRULE_DROP; + $$.w = 0; + $$.w2 = 0; + } + | RETURNRST { + $$.b2 = PFRULE_RETURNRST; + $$.w = 0; + $$.w2 = 0; + } + | RETURNRST '(' TTL NUMBER ')' { + if ($4 < 0 || $4 > 255) { + yyerror("illegal ttl value %d", $4); + YYERROR; + } + $$.b2 = PFRULE_RETURNRST; + $$.w = $4; + $$.w2 = 0; + } + | RETURNICMP { + $$.b2 = PFRULE_RETURNICMP; + $$.w = returnicmpdefault; + $$.w2 = returnicmp6default; + } + | RETURNICMP6 { + $$.b2 = PFRULE_RETURNICMP; + $$.w = returnicmpdefault; + $$.w2 = returnicmp6default; + } + | RETURNICMP '(' reticmpspec ')' { + $$.b2 = PFRULE_RETURNICMP; + $$.w = $3; + $$.w2 = returnicmpdefault; + } + | RETURNICMP6 '(' reticmp6spec ')' { + $$.b2 = PFRULE_RETURNICMP; + $$.w = returnicmpdefault; + $$.w2 = $3; + } + | RETURNICMP '(' reticmpspec comma reticmp6spec ')' { + $$.b2 = PFRULE_RETURNICMP; + $$.w = $3; + $$.w2 = $5; + } + | RETURN { + $$.b2 = PFRULE_RETURN; + $$.w = returnicmpdefault; + $$.w2 = returnicmp6default; + } + ; + +reticmpspec : STRING { + if (!($$ = parseicmpspec($1, AF_INET))) { + free($1); + YYERROR; + } + free($1); + } + | NUMBER { + u_int8_t icmptype; + + if ($1 < 0 || $1 > 255) { + yyerror("invalid icmp code %lu", $1); + YYERROR; + } + icmptype = returnicmpdefault >> 8; + $$ = (icmptype << 8 | $1); + } + ; + +reticmp6spec : STRING { + if (!($$ = parseicmpspec($1, AF_INET6))) { + free($1); + YYERROR; + } + free($1); + } + | NUMBER { + u_int8_t icmptype; + + if ($1 < 0 || $1 > 255) { + yyerror("invalid icmp code %lu", $1); + YYERROR; + } + icmptype = returnicmp6default >> 8; + $$ = (icmptype << 8 | $1); + } + ; + +dir : /* empty */ { $$ = PF_INOUT; } + | IN { $$ = PF_IN; } + | OUT { $$ = PF_OUT; } + ; + +quick : /* empty */ { $$.quick = 0; } + | QUICK { $$.quick = 1; } + ; + +logquick : /* empty */ { $$.log = 0; $$.quick = 0; $$.logif = 0; } + | log { $$ = $1; $$.quick = 0; } + | QUICK { $$.quick = 1; $$.log = 0; $$.logif = 0; } + | log QUICK { $$ = $1; $$.quick = 1; } + | QUICK log { $$ = $2; $$.quick = 1; } + ; + +log : LOG { $$.log = PF_LOG; $$.logif = 0; } + | LOG '(' logopts ')' { + $$.log = PF_LOG | $3.log; + $$.logif = $3.logif; + } + ; + +logopts : logopt { $$ = $1; } + | logopts comma logopt { + $$.log = $1.log | $3.log; + $$.logif = $3.logif; + if ($$.logif == 0) + $$.logif = $1.logif; + } + ; + +logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; } + | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } + | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } + | TO string { + const char *errstr; + u_int i; + + $$.log = 0; + if (strncmp($2, "pflog", 5)) { + yyerror("%s: should be a pflog interface", $2); + free($2); + YYERROR; + } + i = strtonum($2 + 5, 0, 255, &errstr); + if (errstr) { + yyerror("%s: %s", $2, errstr); + free($2); + YYERROR; + } + free($2); + $$.logif = i; + } + ; + +interface : /* empty */ { $$ = NULL; } + | ON if_item_not { $$ = $2; } + | ON '{' optnl if_list '}' { $$ = $4; } + ; + +if_list : if_item_not optnl { $$ = $1; } + | if_list comma if_item_not optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +if_item_not : not if_item { $$ = $2; $$->not = $1; } + ; + +if_item : STRING { + struct node_host *n; + + $$ = calloc(1, sizeof(struct node_if)); + if ($$ == NULL) + err(1, "if_item: calloc"); + if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >= + sizeof($$->ifname)) { + free($1); + free($$); + yyerror("interface name too long"); + YYERROR; + } + + if ((n = ifa_exists($1)) != NULL) + $$->ifa_flags = n->ifa_flags; + + free($1); + $$->not = 0; + $$->next = NULL; + $$->tail = $$; + } + ; + +af : /* empty */ { $$ = 0; } + | INET { $$ = AF_INET; } + | INET6 { $$ = AF_INET6; } + ; + +proto : /* empty */ { $$ = NULL; } + | PROTO proto_item { $$ = $2; } + | PROTO '{' optnl proto_list '}' { $$ = $4; } + ; + +proto_list : proto_item optnl { $$ = $1; } + | proto_list comma proto_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +proto_item : protoval { + u_int8_t pr; + + pr = (u_int8_t)$1; + if (pr == 0) { + yyerror("proto 0 cannot be used"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_proto)); + if ($$ == NULL) + err(1, "proto_item: calloc"); + $$->proto = pr; + $$->next = NULL; + $$->tail = $$; + } + ; + +protoval : STRING { + struct protoent *p; + + p = getprotobyname($1); + if (p == NULL) { + yyerror("unknown protocol %s", $1); + free($1); + YYERROR; + } + $$ = p->p_proto; + free($1); + } + | NUMBER { + if ($1 < 0 || $1 > 255) { + yyerror("protocol outside range"); + YYERROR; + } + } + ; + +fromto : ALL { + $$.src.host = NULL; + $$.src.port = NULL; + $$.dst.host = NULL; + $$.dst.port = NULL; + $$.src_os = NULL; + } + | from os to { + $$.src = $1; + $$.src_os = $2; + $$.dst = $3; + } + ; + +os : /* empty */ { $$ = NULL; } + | OS xos { $$ = $2; } + | OS '{' optnl os_list '}' { $$ = $4; } + ; + +xos : STRING { + $$ = calloc(1, sizeof(struct node_os)); + if ($$ == NULL) + err(1, "os: calloc"); + $$->os = $1; + $$->tail = $$; + } + ; + +os_list : xos optnl { $$ = $1; } + | os_list comma xos optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +from : /* empty */ { + $$.host = NULL; + $$.port = NULL; + } + | FROM ipportspec { + $$ = $2; + } + ; + +to : /* empty */ { + $$.host = NULL; + $$.port = NULL; + } + | TO ipportspec { + if (disallow_urpf_failed($2.host, "\"urpf-failed\" is " + "not permitted in a destination address")) + YYERROR; + $$ = $2; + } + ; + +ipportspec : ipspec { + $$.host = $1; + $$.port = NULL; + } + | ipspec PORT portspec { + $$.host = $1; + $$.port = $3; + } + | PORT portspec { + $$.host = NULL; + $$.port = $2; + } + ; + +optnl : '\n' optnl + | + ; + +ipspec : ANY { $$ = NULL; } + | xhost { $$ = $1; } + | '{' optnl host_list '}' { $$ = $3; } + ; + +toipspec : TO ipspec { $$ = $2; } + | /* empty */ { $$ = NULL; } + ; + +host_list : ipspec optnl { $$ = $1; } + | host_list comma ipspec optnl { + if ($3 == NULL) + $$ = $1; + else if ($1 == NULL) + $$ = $3; + else { + $1->tail->next = $3; + $1->tail = $3->tail; + $$ = $1; + } + } + ; + +xhost : not host { + struct node_host *n; + + for (n = $2; n != NULL; n = n->next) + n->not = $1; + $$ = $2; + } + | not NOROUTE { + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) + err(1, "xhost: calloc"); + $$->addr.type = PF_ADDR_NOROUTE; + $$->next = NULL; + $$->not = $1; + $$->tail = $$; + } + | not URPFFAILED { + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) + err(1, "xhost: calloc"); + $$->addr.type = PF_ADDR_URPFFAILED; + $$->next = NULL; + $$->not = $1; + $$->tail = $$; + } + ; + +host : STRING { + if (($$ = host($1)) == NULL) { + /* error. "any" is handled elsewhere */ + free($1); + yyerror("could not parse host specification"); + YYERROR; + } + free($1); + + } + | STRING '-' STRING { + struct node_host *b, *e; + + if ((b = host($1)) == NULL || (e = host($3)) == NULL) { + free($1); + free($3); + yyerror("could not parse host specification"); + YYERROR; + } + if (b->af != e->af || + b->addr.type != PF_ADDR_ADDRMASK || + e->addr.type != PF_ADDR_ADDRMASK || + unmask(&b->addr.v.a.mask, b->af) != + (b->af == AF_INET ? 32 : 128) || + unmask(&e->addr.v.a.mask, e->af) != + (e->af == AF_INET ? 32 : 128) || + b->next != NULL || b->not || + e->next != NULL || e->not) { + free(b); + free(e); + free($1); + free($3); + yyerror("invalid address range"); + YYERROR; + } + memcpy(&b->addr.v.a.mask, &e->addr.v.a.addr, + sizeof(b->addr.v.a.mask)); + b->addr.type = PF_ADDR_RANGE; + $$ = b; + free(e); + free($1); + free($3); + } + | STRING '/' NUMBER { + char *buf; + + if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1) + err(1, "host: asprintf"); + free($1); + if (($$ = host(buf)) == NULL) { + /* error. "any" is handled elsewhere */ + free(buf); + yyerror("could not parse host specification"); + YYERROR; + } + free(buf); + } + | NUMBER '/' NUMBER { + char *buf; + + /* ie. for 10/8 parsing */ +#ifdef __FreeBSD__ + if (asprintf(&buf, "%lld/%lld", (long long)$1, (long long)$3) == -1) +#else + if (asprintf(&buf, "%lld/%lld", $1, $3) == -1) +#endif + err(1, "host: asprintf"); + if (($$ = host(buf)) == NULL) { + /* error. "any" is handled elsewhere */ + free(buf); + yyerror("could not parse host specification"); + YYERROR; + } + free(buf); + } + | dynaddr + | dynaddr '/' NUMBER { + struct node_host *n; + + if ($3 < 0 || $3 > 128) { + yyerror("bit number too big"); + YYERROR; + } + $$ = $1; + for (n = $1; n != NULL; n = n->next) + set_ipmask(n, $3); + } + | '<' STRING '>' { + if (strlen($2) >= PF_TABLE_NAME_SIZE) { + yyerror("table name '%s' too long", $2); + free($2); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) + err(1, "host: calloc"); + $$->addr.type = PF_ADDR_TABLE; + if (strlcpy($$->addr.v.tblname, $2, + sizeof($$->addr.v.tblname)) >= + sizeof($$->addr.v.tblname)) + errx(1, "host: strlcpy"); + free($2); + $$->next = NULL; + $$->tail = $$; + } + ; + +number : NUMBER + | STRING { + u_long ulval; + + if (atoul($1, &ulval) == -1) { + yyerror("%s is not a number", $1); + free($1); + YYERROR; + } else + $$ = ulval; + free($1); + } + ; + +dynaddr : '(' STRING ')' { + int flags = 0; + char *p, *op; + + op = $2; + if (!isalpha(op[0])) { + yyerror("invalid interface name '%s'", op); + free(op); + YYERROR; + } + while ((p = strrchr($2, ':')) != NULL) { + if (!strcmp(p+1, "network")) + flags |= PFI_AFLAG_NETWORK; + else if (!strcmp(p+1, "broadcast")) + flags |= PFI_AFLAG_BROADCAST; + else if (!strcmp(p+1, "peer")) + flags |= PFI_AFLAG_PEER; + else if (!strcmp(p+1, "0")) + flags |= PFI_AFLAG_NOALIAS; + else { + yyerror("interface %s has bad modifier", + $2); + free(op); + YYERROR; + } + *p = '\0'; + } + if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { + free(op); + yyerror("illegal combination of " + "interface modifiers"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) + err(1, "address: calloc"); + $$->af = 0; + set_ipmask($$, 128); + $$->addr.type = PF_ADDR_DYNIFTL; + $$->addr.iflags = flags; + if (strlcpy($$->addr.v.ifname, $2, + sizeof($$->addr.v.ifname)) >= + sizeof($$->addr.v.ifname)) { + free(op); + free($$); + yyerror("interface name too long"); + YYERROR; + } + free(op); + $$->next = NULL; + $$->tail = $$; + } + ; + +portspec : port_item { $$ = $1; } + | '{' optnl port_list '}' { $$ = $3; } + ; + +port_list : port_item optnl { $$ = $1; } + | port_list comma port_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +port_item : portrange { + $$ = calloc(1, sizeof(struct node_port)); + if ($$ == NULL) + err(1, "port_item: calloc"); + $$->port[0] = $1.a; + $$->port[1] = $1.b; + if ($1.t) + $$->op = PF_OP_RRG; + else + $$->op = PF_OP_EQ; + $$->next = NULL; + $$->tail = $$; + } + | unaryop portrange { + if ($2.t) { + yyerror("':' cannot be used with an other " + "port operator"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_port)); + if ($$ == NULL) + err(1, "port_item: calloc"); + $$->port[0] = $2.a; + $$->port[1] = $2.b; + $$->op = $1; + $$->next = NULL; + $$->tail = $$; + } + | portrange PORTBINARY portrange { + if ($1.t || $3.t) { + yyerror("':' cannot be used with an other " + "port operator"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_port)); + if ($$ == NULL) + err(1, "port_item: calloc"); + $$->port[0] = $1.a; + $$->port[1] = $3.a; + $$->op = $2; + $$->next = NULL; + $$->tail = $$; + } + ; + +portplain : numberstring { + if (parseport($1, &$$, 0) == -1) { + free($1); + YYERROR; + } + free($1); + } + ; + +portrange : numberstring { + if (parseport($1, &$$, PPORT_RANGE) == -1) { + free($1); + YYERROR; + } + free($1); + } + ; + +uids : uid_item { $$ = $1; } + | '{' optnl uid_list '}' { $$ = $3; } + ; + +uid_list : uid_item optnl { $$ = $1; } + | uid_list comma uid_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +uid_item : uid { + $$ = calloc(1, sizeof(struct node_uid)); + if ($$ == NULL) + err(1, "uid_item: calloc"); + $$->uid[0] = $1; + $$->uid[1] = $1; + $$->op = PF_OP_EQ; + $$->next = NULL; + $$->tail = $$; + } + | unaryop uid { + if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { + yyerror("user unknown requires operator = or " + "!="); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_uid)); + if ($$ == NULL) + err(1, "uid_item: calloc"); + $$->uid[0] = $2; + $$->uid[1] = $2; + $$->op = $1; + $$->next = NULL; + $$->tail = $$; + } + | uid PORTBINARY uid { + if ($1 == UID_MAX || $3 == UID_MAX) { + yyerror("user unknown requires operator = or " + "!="); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_uid)); + if ($$ == NULL) + err(1, "uid_item: calloc"); + $$->uid[0] = $1; + $$->uid[1] = $3; + $$->op = $2; + $$->next = NULL; + $$->tail = $$; + } + ; + +uid : STRING { + if (!strcmp($1, "unknown")) + $$ = UID_MAX; + else { + struct passwd *pw; + + if ((pw = getpwnam($1)) == NULL) { + yyerror("unknown user %s", $1); + free($1); + YYERROR; + } + $$ = pw->pw_uid; + } + free($1); + } + | NUMBER { + if ($1 < 0 || $1 >= UID_MAX) { + yyerror("illegal uid value %lu", $1); + YYERROR; + } + $$ = $1; + } + ; + +gids : gid_item { $$ = $1; } + | '{' optnl gid_list '}' { $$ = $3; } + ; + +gid_list : gid_item optnl { $$ = $1; } + | gid_list comma gid_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +gid_item : gid { + $$ = calloc(1, sizeof(struct node_gid)); + if ($$ == NULL) + err(1, "gid_item: calloc"); + $$->gid[0] = $1; + $$->gid[1] = $1; + $$->op = PF_OP_EQ; + $$->next = NULL; + $$->tail = $$; + } + | unaryop gid { + if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { + yyerror("group unknown requires operator = or " + "!="); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_gid)); + if ($$ == NULL) + err(1, "gid_item: calloc"); + $$->gid[0] = $2; + $$->gid[1] = $2; + $$->op = $1; + $$->next = NULL; + $$->tail = $$; + } + | gid PORTBINARY gid { + if ($1 == GID_MAX || $3 == GID_MAX) { + yyerror("group unknown requires operator = or " + "!="); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_gid)); + if ($$ == NULL) + err(1, "gid_item: calloc"); + $$->gid[0] = $1; + $$->gid[1] = $3; + $$->op = $2; + $$->next = NULL; + $$->tail = $$; + } + ; + +gid : STRING { + if (!strcmp($1, "unknown")) + $$ = GID_MAX; + else { + struct group *grp; + + if ((grp = getgrnam($1)) == NULL) { + yyerror("unknown group %s", $1); + free($1); + YYERROR; + } + $$ = grp->gr_gid; + } + free($1); + } + | NUMBER { + if ($1 < 0 || $1 >= GID_MAX) { + yyerror("illegal gid value %lu", $1); + YYERROR; + } + $$ = $1; + } + ; + +flag : STRING { + int f; + + if ((f = parse_flags($1)) < 0) { + yyerror("bad flags %s", $1); + free($1); + YYERROR; + } + free($1); + $$.b1 = f; + } + ; + +flags : FLAGS flag '/' flag { $$.b1 = $2.b1; $$.b2 = $4.b1; } + | FLAGS '/' flag { $$.b1 = 0; $$.b2 = $3.b1; } + | FLAGS ANY { $$.b1 = 0; $$.b2 = 0; } + ; + +icmpspec : ICMPTYPE icmp_item { $$ = $2; } + | ICMPTYPE '{' optnl icmp_list '}' { $$ = $4; } + | ICMP6TYPE icmp6_item { $$ = $2; } + | ICMP6TYPE '{' optnl icmp6_list '}' { $$ = $4; } + ; + +icmp_list : icmp_item optnl { $$ = $1; } + | icmp_list comma icmp_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +icmp6_list : icmp6_item optnl { $$ = $1; } + | icmp6_list comma icmp6_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +icmp_item : icmptype { + $$ = calloc(1, sizeof(struct node_icmp)); + if ($$ == NULL) + err(1, "icmp_item: calloc"); + $$->type = $1; + $$->code = 0; + $$->proto = IPPROTO_ICMP; + $$->next = NULL; + $$->tail = $$; + } + | icmptype CODE STRING { + const struct icmpcodeent *p; + + if ((p = geticmpcodebyname($1-1, $3, AF_INET)) == NULL) { + yyerror("unknown icmp-code %s", $3); + free($3); + YYERROR; + } + + free($3); + $$ = calloc(1, sizeof(struct node_icmp)); + if ($$ == NULL) + err(1, "icmp_item: calloc"); + $$->type = $1; + $$->code = p->code + 1; + $$->proto = IPPROTO_ICMP; + $$->next = NULL; + $$->tail = $$; + } + | icmptype CODE NUMBER { + if ($3 < 0 || $3 > 255) { + yyerror("illegal icmp-code %lu", $3); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_icmp)); + if ($$ == NULL) + err(1, "icmp_item: calloc"); + $$->type = $1; + $$->code = $3 + 1; + $$->proto = IPPROTO_ICMP; + $$->next = NULL; + $$->tail = $$; + } + ; + +icmp6_item : icmp6type { + $$ = calloc(1, sizeof(struct node_icmp)); + if ($$ == NULL) + err(1, "icmp_item: calloc"); + $$->type = $1; + $$->code = 0; + $$->proto = IPPROTO_ICMPV6; + $$->next = NULL; + $$->tail = $$; + } + | icmp6type CODE STRING { + const struct icmpcodeent *p; + + if ((p = geticmpcodebyname($1-1, $3, AF_INET6)) == NULL) { + yyerror("unknown icmp6-code %s", $3); + free($3); + YYERROR; + } + free($3); + + $$ = calloc(1, sizeof(struct node_icmp)); + if ($$ == NULL) + err(1, "icmp_item: calloc"); + $$->type = $1; + $$->code = p->code + 1; + $$->proto = IPPROTO_ICMPV6; + $$->next = NULL; + $$->tail = $$; + } + | icmp6type CODE NUMBER { + if ($3 < 0 || $3 > 255) { + yyerror("illegal icmp-code %lu", $3); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_icmp)); + if ($$ == NULL) + err(1, "icmp_item: calloc"); + $$->type = $1; + $$->code = $3 + 1; + $$->proto = IPPROTO_ICMPV6; + $$->next = NULL; + $$->tail = $$; + } + ; + +icmptype : STRING { + const struct icmptypeent *p; + + if ((p = geticmptypebyname($1, AF_INET)) == NULL) { + yyerror("unknown icmp-type %s", $1); + free($1); + YYERROR; + } + $$ = p->type + 1; + free($1); + } + | NUMBER { + if ($1 < 0 || $1 > 255) { + yyerror("illegal icmp-type %lu", $1); + YYERROR; + } + $$ = $1 + 1; + } + ; + +icmp6type : STRING { + const struct icmptypeent *p; + + if ((p = geticmptypebyname($1, AF_INET6)) == + NULL) { + yyerror("unknown icmp6-type %s", $1); + free($1); + YYERROR; + } + $$ = p->type + 1; + free($1); + } + | NUMBER { + if ($1 < 0 || $1 > 255) { + yyerror("illegal icmp6-type %lu", $1); + YYERROR; + } + $$ = $1 + 1; + } + ; + +tos : STRING { + if (!strcmp($1, "lowdelay")) + $$ = IPTOS_LOWDELAY; + else if (!strcmp($1, "throughput")) + $$ = IPTOS_THROUGHPUT; + else if (!strcmp($1, "reliability")) + $$ = IPTOS_RELIABILITY; + else if ($1[0] == '0' && $1[1] == 'x') + $$ = strtoul($1, NULL, 16); + else + $$ = 0; /* flag bad argument */ + if (!$$ || $$ > 255) { + yyerror("illegal tos value %s", $1); + free($1); + YYERROR; + } + free($1); + } + | NUMBER { + $$ = $1; + if (!$$ || $$ > 255) { + yyerror("illegal tos value %s", $1); + YYERROR; + } + } + ; + +sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; } + | SOURCETRACK GLOBAL { $$ = PF_SRCTRACK_GLOBAL; } + | SOURCETRACK RULE { $$ = PF_SRCTRACK_RULE; } + ; + +statelock : IFBOUND { + $$ = PFRULE_IFBOUND; + } + | FLOATING { + $$ = 0; + } + ; + +keep : NO STATE { + $$.action = 0; + $$.options = NULL; + } + | KEEP STATE state_opt_spec { + $$.action = PF_STATE_NORMAL; + $$.options = $3; + } + | MODULATE STATE state_opt_spec { + $$.action = PF_STATE_MODULATE; + $$.options = $3; + } + | SYNPROXY STATE state_opt_spec { + $$.action = PF_STATE_SYNPROXY; + $$.options = $3; + } + ; + +flush : /* empty */ { $$ = 0; } + | FLUSH { $$ = PF_FLUSH; } + | FLUSH GLOBAL { + $$ = PF_FLUSH | PF_FLUSH_GLOBAL; + } + ; + +state_opt_spec : '(' state_opt_list ')' { $$ = $2; } + | /* empty */ { $$ = NULL; } + ; + +state_opt_list : state_opt_item { $$ = $1; } + | state_opt_list comma state_opt_item { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +state_opt_item : MAXIMUM NUMBER { + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX; + $$->data.max_states = $2; + $$->next = NULL; + $$->tail = $$; + } + | NOSYNC { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_NOSYNC; + $$->next = NULL; + $$->tail = $$; + } + | MAXSRCSTATES NUMBER { + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_STATES; + $$->data.max_src_states = $2; + $$->next = NULL; + $$->tail = $$; + } + | MAXSRCCONN NUMBER { + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_CONN; + $$->data.max_src_conn = $2; + $$->next = NULL; + $$->tail = $$; + } + | MAXSRCCONNRATE NUMBER '/' NUMBER { + if ($2 < 0 || $2 > UINT_MAX || + $4 < 0 || $4 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE; + $$->data.max_src_conn_rate.limit = $2; + $$->data.max_src_conn_rate.seconds = $4; + $$->next = NULL; + $$->tail = $$; + } + | OVERLOAD '<' STRING '>' flush { + if (strlen($3) >= PF_TABLE_NAME_SIZE) { + yyerror("table name '%s' too long", $3); + free($3); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + if (strlcpy($$->data.overload.tblname, $3, + PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE) + errx(1, "state_opt_item: strlcpy"); + free($3); + $$->type = PF_STATE_OPT_OVERLOAD; + $$->data.overload.flush = $5; + $$->next = NULL; + $$->tail = $$; + } + | MAXSRCNODES NUMBER { + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_NODES; + $$->data.max_src_nodes = $2; + $$->next = NULL; + $$->tail = $$; + } + | sourcetrack { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_SRCTRACK; + $$->data.src_track = $1; + $$->next = NULL; + $$->tail = $$; + } + | statelock { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_STATELOCK; + $$->data.statelock = $1; + $$->next = NULL; + $$->tail = $$; + } + | SLOPPY { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_SLOPPY; + $$->next = NULL; + $$->tail = $$; + } + | STRING NUMBER { + int i; + + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + for (i = 0; pf_timeouts[i].name && + strcmp(pf_timeouts[i].name, $1); ++i) + ; /* nothing */ + if (!pf_timeouts[i].name) { + yyerror("illegal timeout name %s", $1); + free($1); + YYERROR; + } + if (strchr(pf_timeouts[i].name, '.') == NULL) { + yyerror("illegal state timeout %s", $1); + free($1); + YYERROR; + } + free($1); + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_TIMEOUT; + $$->data.timeout.number = pf_timeouts[i].timeout; + $$->data.timeout.seconds = $2; + $$->next = NULL; + $$->tail = $$; + } + ; + +label : LABEL STRING { + $$ = $2; + } + ; + +qname : QUEUE STRING { + $$.qname = $2; + $$.pqname = NULL; + } + | QUEUE '(' STRING ')' { + $$.qname = $3; + $$.pqname = NULL; + } + | QUEUE '(' STRING comma STRING ')' { + $$.qname = $3; + $$.pqname = $5; + } + ; + +no : /* empty */ { $$ = 0; } + | NO { $$ = 1; } + ; + +portstar : numberstring { + if (parseport($1, &$$, PPORT_RANGE|PPORT_STAR) == -1) { + free($1); + YYERROR; + } + free($1); + } + ; + +redirspec : host { $$ = $1; } + | '{' optnl redir_host_list '}' { $$ = $3; } + ; + +redir_host_list : host optnl { $$ = $1; } + | redir_host_list comma host optnl { + $1->tail->next = $3; + $1->tail = $3->tail; + $$ = $1; + } + ; + +redirpool : /* empty */ { $$ = NULL; } + | ARROW redirspec { + $$ = calloc(1, sizeof(struct redirection)); + if ($$ == NULL) + err(1, "redirection: calloc"); + $$->host = $2; + $$->rport.a = $$->rport.b = $$->rport.t = 0; + } + | ARROW redirspec PORT portstar { + $$ = calloc(1, sizeof(struct redirection)); + if ($$ == NULL) + err(1, "redirection: calloc"); + $$->host = $2; + $$->rport = $4; + } + ; + +hashkey : /* empty */ + { + $$ = calloc(1, sizeof(struct pf_poolhashkey)); + if ($$ == NULL) + err(1, "hashkey: calloc"); + $$->key32[0] = arc4random(); + $$->key32[1] = arc4random(); + $$->key32[2] = arc4random(); + $$->key32[3] = arc4random(); + } + | string + { + if (!strncmp($1, "0x", 2)) { + if (strlen($1) != 34) { + free($1); + yyerror("hex key must be 128 bits " + "(32 hex digits) long"); + YYERROR; + } + $$ = calloc(1, sizeof(struct pf_poolhashkey)); + if ($$ == NULL) + err(1, "hashkey: calloc"); + + if (sscanf($1, "0x%8x%8x%8x%8x", + &$$->key32[0], &$$->key32[1], + &$$->key32[2], &$$->key32[3]) != 4) { + free($$); + free($1); + yyerror("invalid hex key"); + YYERROR; + } + } else { + MD5_CTX context; + + $$ = calloc(1, sizeof(struct pf_poolhashkey)); + if ($$ == NULL) + err(1, "hashkey: calloc"); + MD5Init(&context); + MD5Update(&context, (unsigned char *)$1, + strlen($1)); + MD5Final((unsigned char *)$$, &context); + HTONL($$->key32[0]); + HTONL($$->key32[1]); + HTONL($$->key32[2]); + HTONL($$->key32[3]); + } + free($1); + } + ; + +pool_opts : { bzero(&pool_opts, sizeof pool_opts); } + pool_opts_l + { $$ = pool_opts; } + | /* empty */ { + bzero(&pool_opts, sizeof pool_opts); + $$ = pool_opts; + } + ; + +pool_opts_l : pool_opts_l pool_opt + | pool_opt + ; + +pool_opt : BITMASK { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_BITMASK; + } + | RANDOM { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_RANDOM; + } + | SOURCEHASH hashkey { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_SRCHASH; + pool_opts.key = $2; + } + | ROUNDROBIN { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_ROUNDROBIN; + } + | STATICPORT { + if (pool_opts.staticport) { + yyerror("static-port cannot be redefined"); + YYERROR; + } + pool_opts.staticport = 1; + } + | STICKYADDRESS { + if (filter_opts.marker & POM_STICKYADDRESS) { + yyerror("sticky-address cannot be redefined"); + YYERROR; + } + pool_opts.marker |= POM_STICKYADDRESS; + pool_opts.opts |= PF_POOL_STICKYADDR; + } + ; + +redirection : /* empty */ { $$ = NULL; } + | ARROW host { + $$ = calloc(1, sizeof(struct redirection)); + if ($$ == NULL) + err(1, "redirection: calloc"); + $$->host = $2; + $$->rport.a = $$->rport.b = $$->rport.t = 0; + } + | ARROW host PORT portstar { + $$ = calloc(1, sizeof(struct redirection)); + if ($$ == NULL) + err(1, "redirection: calloc"); + $$->host = $2; + $$->rport = $4; + } + ; + +natpasslog : /* empty */ { $$.b1 = $$.b2 = 0; $$.w2 = 0; } + | PASS { $$.b1 = 1; $$.b2 = 0; $$.w2 = 0; } + | PASS log { $$.b1 = 1; $$.b2 = $2.log; $$.w2 = $2.logif; } + | log { $$.b1 = 0; $$.b2 = $1.log; $$.w2 = $1.logif; } + ; + +nataction : no NAT natpasslog { + if ($1 && $3.b1) { + yyerror("\"pass\" not valid with \"no\""); + YYERROR; + } + if ($1) + $$.b1 = PF_NONAT; + else + $$.b1 = PF_NAT; + $$.b2 = $3.b1; + $$.w = $3.b2; + $$.w2 = $3.w2; + } + | no RDR natpasslog { + if ($1 && $3.b1) { + yyerror("\"pass\" not valid with \"no\""); + YYERROR; + } + if ($1) + $$.b1 = PF_NORDR; + else + $$.b1 = PF_RDR; + $$.b2 = $3.b1; + $$.w = $3.b2; + $$.w2 = $3.w2; + } + ; + +natrule : nataction interface af proto fromto tag tagged rtable + redirpool pool_opts + { + struct pf_rule r; + + if (check_rulestate(PFCTL_STATE_NAT)) + YYERROR; + + memset(&r, 0, sizeof(r)); + + r.action = $1.b1; + r.natpass = $1.b2; + r.log = $1.w; + r.logif = $1.w2; + r.af = $3; + + if (!r.af) { + if ($5.src.host && $5.src.host->af && + !$5.src.host->ifindex) + r.af = $5.src.host->af; + else if ($5.dst.host && $5.dst.host->af && + !$5.dst.host->ifindex) + r.af = $5.dst.host->af; + } + + if ($6 != NULL) + if (strlcpy(r.tagname, $6, PF_TAG_NAME_SIZE) >= + PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + + if ($7.name) + if (strlcpy(r.match_tagname, $7.name, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + r.match_tag_not = $7.neg; + r.rtableid = $8; + + if (r.action == PF_NONAT || r.action == PF_NORDR) { + if ($9 != NULL) { + yyerror("translation rule with 'no' " + "does not need '->'"); + YYERROR; + } + } else { + if ($9 == NULL || $9->host == NULL) { + yyerror("translation rule requires '-> " + "address'"); + YYERROR; + } + if (!r.af && ! $9->host->ifindex) + r.af = $9->host->af; + + remove_invalid_hosts(&$9->host, &r.af); + if (invalid_redirect($9->host, r.af)) + YYERROR; + if (check_netmask($9->host, r.af)) + YYERROR; + + r.rpool.proxy_port[0] = ntohs($9->rport.a); + + switch (r.action) { + case PF_RDR: + if (!$9->rport.b && $9->rport.t && + $5.dst.port != NULL) { + r.rpool.proxy_port[1] = + ntohs($9->rport.a) + + (ntohs( + $5.dst.port->port[1]) - + ntohs( + $5.dst.port->port[0])); + } else + r.rpool.proxy_port[1] = + ntohs($9->rport.b); + break; + case PF_NAT: + r.rpool.proxy_port[1] = + ntohs($9->rport.b); + if (!r.rpool.proxy_port[0] && + !r.rpool.proxy_port[1]) { + r.rpool.proxy_port[0] = + PF_NAT_PROXY_PORT_LOW; + r.rpool.proxy_port[1] = + PF_NAT_PROXY_PORT_HIGH; + } else if (!r.rpool.proxy_port[1]) + r.rpool.proxy_port[1] = + r.rpool.proxy_port[0]; + break; + default: + break; + } + + r.rpool.opts = $10.type; + if ((r.rpool.opts & PF_POOL_TYPEMASK) == + PF_POOL_NONE && ($9->host->next != NULL || + $9->host->addr.type == PF_ADDR_TABLE || + DYNIF_MULTIADDR($9->host->addr))) + r.rpool.opts = PF_POOL_ROUNDROBIN; + if ((r.rpool.opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN && + disallow_table($9->host, "tables are only " + "supported in round-robin redirection " + "pools")) + YYERROR; + if ((r.rpool.opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN && + disallow_alias($9->host, "interface (%s) " + "is only supported in round-robin " + "redirection pools")) + YYERROR; + if ($9->host->next != NULL) { + if ((r.rpool.opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) { + yyerror("only round-robin " + "valid for multiple " + "redirection addresses"); + YYERROR; + } + } + } + + if ($10.key != NULL) + memcpy(&r.rpool.key, $10.key, + sizeof(struct pf_poolhashkey)); + + if ($10.opts) + r.rpool.opts |= $10.opts; + + if ($10.staticport) { + if (r.action != PF_NAT) { + yyerror("the 'static-port' option is " + "only valid with nat rules"); + YYERROR; + } + if (r.rpool.proxy_port[0] != + PF_NAT_PROXY_PORT_LOW && + r.rpool.proxy_port[1] != + PF_NAT_PROXY_PORT_HIGH) { + yyerror("the 'static-port' option can't" + " be used when specifying a port" + " range"); + YYERROR; + } + r.rpool.proxy_port[0] = 0; + r.rpool.proxy_port[1] = 0; + } + + expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, + $5.src_os, $5.src.host, $5.src.port, $5.dst.host, + $5.dst.port, 0, 0, 0, ""); + free($9); + } + ; + +binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag + tagged rtable redirection + { + struct pf_rule binat; + struct pf_pooladdr *pa; + + if (check_rulestate(PFCTL_STATE_NAT)) + YYERROR; + if (disallow_urpf_failed($9, "\"urpf-failed\" is not " + "permitted as a binat destination")) + YYERROR; + + memset(&binat, 0, sizeof(binat)); + + if ($1 && $3.b1) { + yyerror("\"pass\" not valid with \"no\""); + YYERROR; + } + if ($1) + binat.action = PF_NOBINAT; + else + binat.action = PF_BINAT; + binat.natpass = $3.b1; + binat.log = $3.b2; + binat.logif = $3.w2; + binat.af = $5; + if (!binat.af && $8 != NULL && $8->af) + binat.af = $8->af; + if (!binat.af && $9 != NULL && $9->af) + binat.af = $9->af; + + if (!binat.af && $13 != NULL && $13->host) + binat.af = $13->host->af; + if (!binat.af) { + yyerror("address family (inet/inet6) " + "undefined"); + YYERROR; + } + + if ($4 != NULL) { + memcpy(binat.ifname, $4->ifname, + sizeof(binat.ifname)); + binat.ifnot = $4->not; + free($4); + } + + if ($10 != NULL) + if (strlcpy(binat.tagname, $10, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + if ($11.name) + if (strlcpy(binat.match_tagname, $11.name, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + binat.match_tag_not = $11.neg; + binat.rtableid = $12; + + if ($6 != NULL) { + binat.proto = $6->proto; + free($6); + } + + if ($8 != NULL && disallow_table($8, "invalid use of " + "table <%s> as the source address of a binat rule")) + YYERROR; + if ($8 != NULL && disallow_alias($8, "invalid use of " + "interface (%s) as the source address of a binat " + "rule")) + YYERROR; + if ($13 != NULL && $13->host != NULL && disallow_table( + $13->host, "invalid use of table <%s> as the " + "redirect address of a binat rule")) + YYERROR; + if ($13 != NULL && $13->host != NULL && disallow_alias( + $13->host, "invalid use of interface (%s) as the " + "redirect address of a binat rule")) + YYERROR; + + if ($8 != NULL) { + if ($8->next) { + yyerror("multiple binat ip addresses"); + YYERROR; + } + if ($8->addr.type == PF_ADDR_DYNIFTL) + $8->af = binat.af; + if ($8->af != binat.af) { + yyerror("binat ip versions must match"); + YYERROR; + } + if (check_netmask($8, binat.af)) + YYERROR; + memcpy(&binat.src.addr, &$8->addr, + sizeof(binat.src.addr)); + free($8); + } + if ($9 != NULL) { + if ($9->next) { + yyerror("multiple binat ip addresses"); + YYERROR; + } + if ($9->af != binat.af && $9->af) { + yyerror("binat ip versions must match"); + YYERROR; + } + if (check_netmask($9, binat.af)) + YYERROR; + memcpy(&binat.dst.addr, &$9->addr, + sizeof(binat.dst.addr)); + binat.dst.neg = $9->not; + free($9); + } + + if (binat.action == PF_NOBINAT) { + if ($13 != NULL) { + yyerror("'no binat' rule does not need" + " '->'"); + YYERROR; + } + } else { + if ($13 == NULL || $13->host == NULL) { + yyerror("'binat' rule requires" + " '-> address'"); + YYERROR; + } + + remove_invalid_hosts(&$13->host, &binat.af); + if (invalid_redirect($13->host, binat.af)) + YYERROR; + if ($13->host->next != NULL) { + yyerror("binat rule must redirect to " + "a single address"); + YYERROR; + } + if (check_netmask($13->host, binat.af)) + YYERROR; + + if (!PF_AZERO(&binat.src.addr.v.a.mask, + binat.af) && + !PF_AEQ(&binat.src.addr.v.a.mask, + &$13->host->addr.v.a.mask, binat.af)) { + yyerror("'binat' source mask and " + "redirect mask must be the same"); + YYERROR; + } + + TAILQ_INIT(&binat.rpool.list); + pa = calloc(1, sizeof(struct pf_pooladdr)); + if (pa == NULL) + err(1, "binat: calloc"); + pa->addr = $13->host->addr; + pa->ifname[0] = 0; + TAILQ_INSERT_TAIL(&binat.rpool.list, + pa, entries); + + free($13); + } + + pfctl_add_rule(pf, &binat, ""); + } + ; + +tag : /* empty */ { $$ = NULL; } + | TAG STRING { $$ = $2; } + ; + +tagged : /* empty */ { $$.neg = 0; $$.name = NULL; } + | not TAGGED string { $$.neg = $1; $$.name = $3; } + ; + +rtable : /* empty */ { $$ = -1; } + | RTABLE NUMBER { + if ($2 < 0 || $2 > rt_tableid_max()) { + yyerror("invalid rtable id"); + YYERROR; + } + $$ = $2; + } + ; + +route_host : STRING { + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) + err(1, "route_host: calloc"); + $$->ifname = $1; + set_ipmask($$, 128); + $$->next = NULL; + $$->tail = $$; + } + | '(' STRING host ')' { + $$ = $3; + $$->ifname = $2; + } + ; + +route_host_list : route_host optnl { $$ = $1; } + | route_host_list comma route_host optnl { + if ($1->af == 0) + $1->af = $3->af; + if ($1->af != $3->af) { + yyerror("all pool addresses must be in the " + "same address family"); + YYERROR; + } + $1->tail->next = $3; + $1->tail = $3->tail; + $$ = $1; + } + ; + +routespec : route_host { $$ = $1; } + | '{' optnl route_host_list '}' { $$ = $3; } + ; + +route : /* empty */ { + $$.host = NULL; + $$.rt = 0; + $$.pool_opts = 0; + } + | FASTROUTE { + $$.host = NULL; + $$.rt = PF_FASTROUTE; + $$.pool_opts = 0; + } + | ROUTETO routespec pool_opts { + $$.host = $2; + $$.rt = PF_ROUTETO; + $$.pool_opts = $3.type | $3.opts; + if ($3.key != NULL) + $$.key = $3.key; + } + | REPLYTO routespec pool_opts { + $$.host = $2; + $$.rt = PF_REPLYTO; + $$.pool_opts = $3.type | $3.opts; + if ($3.key != NULL) + $$.key = $3.key; + } + | DUPTO routespec pool_opts { + $$.host = $2; + $$.rt = PF_DUPTO; + $$.pool_opts = $3.type | $3.opts; + if ($3.key != NULL) + $$.key = $3.key; + } + ; + +timeout_spec : STRING NUMBER + { + if (check_rulestate(PFCTL_STATE_OPTION)) { + free($1); + YYERROR; + } + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + if (pfctl_set_timeout(pf, $1, $2, 0) != 0) { + yyerror("unknown timeout %s", $1); + free($1); + YYERROR; + } + free($1); + } + ; + +timeout_list : timeout_list comma timeout_spec optnl + | timeout_spec optnl + ; + +limit_spec : STRING NUMBER + { + if (check_rulestate(PFCTL_STATE_OPTION)) { + free($1); + YYERROR; + } + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + if (pfctl_set_limit(pf, $1, $2) != 0) { + yyerror("unable to set limit %s %u", $1, $2); + free($1); + YYERROR; + } + free($1); + } + ; + +limit_list : limit_list comma limit_spec optnl + | limit_spec optnl + ; + +comma : ',' + | /* empty */ + ; + +yesno : NO { $$ = 0; } + | STRING { + if (!strcmp($1, "yes")) + $$ = 1; + else { + yyerror("invalid value '%s', expected 'yes' " + "or 'no'", $1); + free($1); + YYERROR; + } + free($1); + } + ; + +unaryop : '=' { $$ = PF_OP_EQ; } + | '!' '=' { $$ = PF_OP_NE; } + | '<' '=' { $$ = PF_OP_LE; } + | '<' { $$ = PF_OP_LT; } + | '>' '=' { $$ = PF_OP_GE; } + | '>' { $$ = PF_OP_GT; } + ; + +%% + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + file->errors++; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +disallow_table(struct node_host *h, const char *fmt) +{ + for (; h != NULL; h = h->next) + if (h->addr.type == PF_ADDR_TABLE) { + yyerror(fmt, h->addr.v.tblname); + return (1); + } + return (0); +} + +int +disallow_urpf_failed(struct node_host *h, const char *fmt) +{ + for (; h != NULL; h = h->next) + if (h->addr.type == PF_ADDR_URPFFAILED) { + yyerror(fmt); + return (1); + } + return (0); +} + +int +disallow_alias(struct node_host *h, const char *fmt) +{ + for (; h != NULL; h = h->next) + if (DYNIF_MULTIADDR(h->addr)) { + yyerror(fmt, h->addr.v.tblname); + return (1); + } + return (0); +} + +int +rule_consistent(struct pf_rule *r, int anchor_call) +{ + int problems = 0; + + switch (r->action) { + case PF_PASS: + case PF_DROP: + case PF_SCRUB: + case PF_NOSCRUB: + problems = filter_consistent(r, anchor_call); + break; + case PF_NAT: + case PF_NONAT: + problems = nat_consistent(r); + break; + case PF_RDR: + case PF_NORDR: + problems = rdr_consistent(r); + break; + case PF_BINAT: + case PF_NOBINAT: + default: + break; + } + return (problems); +} + +int +filter_consistent(struct pf_rule *r, int anchor_call) +{ + int problems = 0; + + if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP && + (r->src.port_op || r->dst.port_op)) { + yyerror("port only applies to tcp/udp"); + problems++; + } + if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 && + (r->type || r->code)) { + yyerror("icmp-type/code only applies to icmp"); + problems++; + } + if (!r->af && (r->type || r->code)) { + yyerror("must indicate address family with icmp-type/code"); + problems++; + } + if (r->overload_tblname[0] && + r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) { + yyerror("'overload' requires 'max-src-conn' " + "or 'max-src-conn-rate'"); + problems++; + } + if ((r->proto == IPPROTO_ICMP && r->af == AF_INET6) || + (r->proto == IPPROTO_ICMPV6 && r->af == AF_INET)) { + yyerror("proto %s doesn't match address family %s", + r->proto == IPPROTO_ICMP ? "icmp" : "icmp6", + r->af == AF_INET ? "inet" : "inet6"); + problems++; + } + if (r->allow_opts && r->action != PF_PASS) { + yyerror("allow-opts can only be specified for pass rules"); + problems++; + } + if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op || + r->dst.port_op || r->flagset || r->type || r->code)) { + yyerror("fragments can be filtered only on IP header fields"); + problems++; + } + if (r->rule_flag & PFRULE_RETURNRST && r->proto != IPPROTO_TCP) { + yyerror("return-rst can only be applied to TCP rules"); + problems++; + } + if (r->max_src_nodes && !(r->rule_flag & PFRULE_RULESRCTRACK)) { + yyerror("max-src-nodes requires 'source-track rule'"); + problems++; + } + if (r->action == PF_DROP && r->keep_state) { + yyerror("keep state on block rules doesn't make sense"); + problems++; + } + if (r->rule_flag & PFRULE_STATESLOPPY && + (r->keep_state == PF_STATE_MODULATE || + r->keep_state == PF_STATE_SYNPROXY)) { + yyerror("sloppy state matching cannot be used with " + "synproxy state or modulate state"); + problems++; + } + return (-problems); +} + +int +nat_consistent(struct pf_rule *r) +{ + return (0); /* yeah! */ +} + +int +rdr_consistent(struct pf_rule *r) +{ + int problems = 0; + + if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) { + if (r->src.port_op) { + yyerror("src port only applies to tcp/udp"); + problems++; + } + if (r->dst.port_op) { + yyerror("dst port only applies to tcp/udp"); + problems++; + } + if (r->rpool.proxy_port[0]) { + yyerror("rpool port only applies to tcp/udp"); + problems++; + } + } + if (r->dst.port_op && + r->dst.port_op != PF_OP_EQ && r->dst.port_op != PF_OP_RRG) { + yyerror("invalid port operator for rdr destination port"); + problems++; + } + return (-problems); +} + +int +process_tabledef(char *name, struct table_opts *opts) +{ + struct pfr_buffer ab; + struct node_tinit *ti; + + bzero(&ab, sizeof(ab)); + ab.pfrb_type = PFRB_ADDRS; + SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) { + if (ti->file) + if (pfr_buf_load(&ab, ti->file, 0, append_addr)) { + if (errno) + yyerror("cannot load \"%s\": %s", + ti->file, strerror(errno)); + else + yyerror("file \"%s\" contains bad data", + ti->file); + goto _error; + } + if (ti->host) + if (append_addr_host(&ab, ti->host, 0, 0)) { + yyerror("cannot create address buffer: %s", + strerror(errno)); + goto _error; + } + } + if (pf->opts & PF_OPT_VERBOSE) + print_tabledef(name, opts->flags, opts->init_addr, + &opts->init_nodes); + if (!(pf->opts & PF_OPT_NOACTION) && + pfctl_define_table(name, opts->flags, opts->init_addr, + pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) { + yyerror("cannot define table %s: %s", name, + pfr_strerror(errno)); + goto _error; + } + pf->tdirty = 1; + pfr_buf_clear(&ab); + return (0); +_error: + pfr_buf_clear(&ab); + return (-1); +} + +struct keywords { + const char *k_name; + int k_val; +}; + +/* macro gore, but you should've seen the prior indentation nightmare... */ + +#define FREE_LIST(T,r) \ + do { \ + T *p, *node = r; \ + while (node != NULL) { \ + p = node; \ + node = node->next; \ + free(p); \ + } \ + } while (0) + +#define LOOP_THROUGH(T,n,r,C) \ + do { \ + T *n; \ + if (r == NULL) { \ + r = calloc(1, sizeof(T)); \ + if (r == NULL) \ + err(1, "LOOP: calloc"); \ + r->next = NULL; \ + } \ + n = r; \ + while (n != NULL) { \ + do { \ + C; \ + } while (0); \ + n = n->next; \ + } \ + } while (0) + +void +expand_label_str(char *label, size_t len, const char *srch, const char *repl) +{ + char *tmp; + char *p, *q; + + if ((tmp = calloc(1, len)) == NULL) + err(1, "expand_label_str: calloc"); + p = q = label; + while ((q = strstr(p, srch)) != NULL) { + *q = '\0'; + if ((strlcat(tmp, p, len) >= len) || + (strlcat(tmp, repl, len) >= len)) + errx(1, "expand_label: label too long"); + q += strlen(srch); + p = q; + } + if (strlcat(tmp, p, len) >= len) + errx(1, "expand_label: label too long"); + strlcpy(label, tmp, len); /* always fits */ + free(tmp); +} + +void +expand_label_if(const char *name, char *label, size_t len, const char *ifname) +{ + if (strstr(label, name) != NULL) { + if (!*ifname) + expand_label_str(label, len, name, "any"); + else + expand_label_str(label, len, name, ifname); + } +} + +void +expand_label_addr(const char *name, char *label, size_t len, sa_family_t af, + struct node_host *h) +{ + char tmp[64], tmp_not[66]; + + if (strstr(label, name) != NULL) { + switch (h->addr.type) { + case PF_ADDR_DYNIFTL: + snprintf(tmp, sizeof(tmp), "(%s)", h->addr.v.ifname); + break; + case PF_ADDR_TABLE: + snprintf(tmp, sizeof(tmp), "<%s>", h->addr.v.tblname); + break; + case PF_ADDR_NOROUTE: + snprintf(tmp, sizeof(tmp), "no-route"); + break; + case PF_ADDR_URPFFAILED: + snprintf(tmp, sizeof(tmp), "urpf-failed"); + break; + case PF_ADDR_ADDRMASK: + if (!af || (PF_AZERO(&h->addr.v.a.addr, af) && + PF_AZERO(&h->addr.v.a.mask, af))) + snprintf(tmp, sizeof(tmp), "any"); + else { + char a[48]; + int bits; + + if (inet_ntop(af, &h->addr.v.a.addr, a, + sizeof(a)) == NULL) + snprintf(tmp, sizeof(tmp), "?"); + else { + bits = unmask(&h->addr.v.a.mask, af); + if ((af == AF_INET && bits < 32) || + (af == AF_INET6 && bits < 128)) + snprintf(tmp, sizeof(tmp), + "%s/%d", a, bits); + else + snprintf(tmp, sizeof(tmp), + "%s", a); + } + } + break; + default: + snprintf(tmp, sizeof(tmp), "?"); + break; + } + + if (h->not) { + snprintf(tmp_not, sizeof(tmp_not), "! %s", tmp); + expand_label_str(label, len, name, tmp_not); + } else + expand_label_str(label, len, name, tmp); + } +} + +void +expand_label_port(const char *name, char *label, size_t len, + struct node_port *port) +{ + char a1[6], a2[6], op[13] = ""; + + if (strstr(label, name) != NULL) { + snprintf(a1, sizeof(a1), "%u", ntohs(port->port[0])); + snprintf(a2, sizeof(a2), "%u", ntohs(port->port[1])); + if (!port->op) + ; + else if (port->op == PF_OP_IRG) + snprintf(op, sizeof(op), "%s><%s", a1, a2); + else if (port->op == PF_OP_XRG) + snprintf(op, sizeof(op), "%s<>%s", a1, a2); + else if (port->op == PF_OP_EQ) + snprintf(op, sizeof(op), "%s", a1); + else if (port->op == PF_OP_NE) + snprintf(op, sizeof(op), "!=%s", a1); + else if (port->op == PF_OP_LT) + snprintf(op, sizeof(op), "<%s", a1); + else if (port->op == PF_OP_LE) + snprintf(op, sizeof(op), "<=%s", a1); + else if (port->op == PF_OP_GT) + snprintf(op, sizeof(op), ">%s", a1); + else if (port->op == PF_OP_GE) + snprintf(op, sizeof(op), ">=%s", a1); + expand_label_str(label, len, name, op); + } +} + +void +expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto) +{ + struct protoent *pe; + char n[4]; + + if (strstr(label, name) != NULL) { + pe = getprotobynumber(proto); + if (pe != NULL) + expand_label_str(label, len, name, pe->p_name); + else { + snprintf(n, sizeof(n), "%u", proto); + expand_label_str(label, len, name, n); + } + } +} + +void +expand_label_nr(const char *name, char *label, size_t len) +{ + char n[11]; + + if (strstr(label, name) != NULL) { + snprintf(n, sizeof(n), "%u", pf->anchor->match); + expand_label_str(label, len, name, n); + } +} + +void +expand_label(char *label, size_t len, const char *ifname, sa_family_t af, + struct node_host *src_host, struct node_port *src_port, + struct node_host *dst_host, struct node_port *dst_port, + u_int8_t proto) +{ + expand_label_if("$if", label, len, ifname); + expand_label_addr("$srcaddr", label, len, af, src_host); + expand_label_addr("$dstaddr", label, len, af, dst_host); + expand_label_port("$srcport", label, len, src_port); + expand_label_port("$dstport", label, len, dst_port); + expand_label_proto("$proto", label, len, proto); + expand_label_nr("$nr", label, len); +} + +int +expand_altq(struct pf_altq *a, struct node_if *interfaces, + struct node_queue *nqueues, struct node_queue_bw bwspec, + struct node_queue_opt *opts) +{ + struct pf_altq pa, pb; + char qname[PF_QNAME_SIZE]; + struct node_queue *n; + struct node_queue_bw bw; + int errs = 0; + + if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { + FREE_LIST(struct node_if, interfaces); + FREE_LIST(struct node_queue, nqueues); + return (0); + } + + LOOP_THROUGH(struct node_if, interface, interfaces, + memcpy(&pa, a, sizeof(struct pf_altq)); + if (strlcpy(pa.ifname, interface->ifname, + sizeof(pa.ifname)) >= sizeof(pa.ifname)) + errx(1, "expand_altq: strlcpy"); + + if (interface->not) { + yyerror("altq on ! is not supported"); + errs++; + } else { + if (eval_pfaltq(pf, &pa, &bwspec, opts)) + errs++; + else + if (pfctl_add_altq(pf, &pa)) + errs++; + + if (pf->opts & PF_OPT_VERBOSE) { + print_altq(&pf->paltq->altq, 0, + &bwspec, opts); + if (nqueues && nqueues->tail) { + printf("queue { "); + LOOP_THROUGH(struct node_queue, queue, + nqueues, + printf("%s ", + queue->queue); + ); + printf("}"); + } + printf("\n"); + } + + if (pa.scheduler == ALTQT_CBQ || + pa.scheduler == ALTQT_HFSC) { + /* now create a root queue */ + memset(&pb, 0, sizeof(struct pf_altq)); + if (strlcpy(qname, "root_", sizeof(qname)) >= + sizeof(qname)) + errx(1, "expand_altq: strlcpy"); + if (strlcat(qname, interface->ifname, + sizeof(qname)) >= sizeof(qname)) + errx(1, "expand_altq: strlcat"); + if (strlcpy(pb.qname, qname, + sizeof(pb.qname)) >= sizeof(pb.qname)) + errx(1, "expand_altq: strlcpy"); + if (strlcpy(pb.ifname, interface->ifname, + sizeof(pb.ifname)) >= sizeof(pb.ifname)) + errx(1, "expand_altq: strlcpy"); + pb.qlimit = pa.qlimit; + pb.scheduler = pa.scheduler; + bw.bw_absolute = pa.ifbandwidth; + bw.bw_percent = 0; + if (eval_pfqueue(pf, &pb, &bw, opts)) + errs++; + else + if (pfctl_add_altq(pf, &pb)) + errs++; + } + + LOOP_THROUGH(struct node_queue, queue, nqueues, + n = calloc(1, sizeof(struct node_queue)); + if (n == NULL) + err(1, "expand_altq: calloc"); + if (pa.scheduler == ALTQT_CBQ || + pa.scheduler == ALTQT_HFSC) + if (strlcpy(n->parent, qname, + sizeof(n->parent)) >= + sizeof(n->parent)) + errx(1, "expand_altq: strlcpy"); + if (strlcpy(n->queue, queue->queue, + sizeof(n->queue)) >= sizeof(n->queue)) + errx(1, "expand_altq: strlcpy"); + if (strlcpy(n->ifname, interface->ifname, + sizeof(n->ifname)) >= sizeof(n->ifname)) + errx(1, "expand_altq: strlcpy"); + n->scheduler = pa.scheduler; + n->next = NULL; + n->tail = n; + if (queues == NULL) + queues = n; + else { + queues->tail->next = n; + queues->tail = n; + } + ); + } + ); + FREE_LIST(struct node_if, interfaces); + FREE_LIST(struct node_queue, nqueues); + + return (errs); +} + +int +expand_queue(struct pf_altq *a, struct node_if *interfaces, + struct node_queue *nqueues, struct node_queue_bw bwspec, + struct node_queue_opt *opts) +{ + struct node_queue *n, *nq; + struct pf_altq pa; + u_int8_t found = 0; + u_int8_t errs = 0; + + if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) { + FREE_LIST(struct node_queue, nqueues); + return (0); + } + + if (queues == NULL) { + yyerror("queue %s has no parent", a->qname); + FREE_LIST(struct node_queue, nqueues); + return (1); + } + + LOOP_THROUGH(struct node_if, interface, interfaces, + LOOP_THROUGH(struct node_queue, tqueue, queues, + if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) && + (interface->ifname[0] == 0 || + (!interface->not && !strncmp(interface->ifname, + tqueue->ifname, IFNAMSIZ)) || + (interface->not && strncmp(interface->ifname, + tqueue->ifname, IFNAMSIZ)))) { + /* found ourself in queues */ + found++; + + memcpy(&pa, a, sizeof(struct pf_altq)); + + if (pa.scheduler != ALTQT_NONE && + pa.scheduler != tqueue->scheduler) { + yyerror("exactly one scheduler type " + "per interface allowed"); + return (1); + } + pa.scheduler = tqueue->scheduler; + + /* scheduler dependent error checking */ + switch (pa.scheduler) { + case ALTQT_PRIQ: + if (nqueues != NULL) { + yyerror("priq queues cannot " + "have child queues"); + return (1); + } + if (bwspec.bw_absolute > 0 || + bwspec.bw_percent < 100) { + yyerror("priq doesn't take " + "bandwidth"); + return (1); + } + break; + default: + break; + } + + if (strlcpy(pa.ifname, tqueue->ifname, + sizeof(pa.ifname)) >= sizeof(pa.ifname)) + errx(1, "expand_queue: strlcpy"); + if (strlcpy(pa.parent, tqueue->parent, + sizeof(pa.parent)) >= sizeof(pa.parent)) + errx(1, "expand_queue: strlcpy"); + + if (eval_pfqueue(pf, &pa, &bwspec, opts)) + errs++; + else + if (pfctl_add_altq(pf, &pa)) + errs++; + + for (nq = nqueues; nq != NULL; nq = nq->next) { + if (!strcmp(a->qname, nq->queue)) { + yyerror("queue cannot have " + "itself as child"); + errs++; + continue; + } + n = calloc(1, + sizeof(struct node_queue)); + if (n == NULL) + err(1, "expand_queue: calloc"); + if (strlcpy(n->parent, a->qname, + sizeof(n->parent)) >= + sizeof(n->parent)) + errx(1, "expand_queue strlcpy"); + if (strlcpy(n->queue, nq->queue, + sizeof(n->queue)) >= + sizeof(n->queue)) + errx(1, "expand_queue strlcpy"); + if (strlcpy(n->ifname, tqueue->ifname, + sizeof(n->ifname)) >= + sizeof(n->ifname)) + errx(1, "expand_queue strlcpy"); + n->scheduler = tqueue->scheduler; + n->next = NULL; + n->tail = n; + if (queues == NULL) + queues = n; + else { + queues->tail->next = n; + queues->tail = n; + } + } + if ((pf->opts & PF_OPT_VERBOSE) && ( + (found == 1 && interface->ifname[0] == 0) || + (found > 0 && interface->ifname[0] != 0))) { + print_queue(&pf->paltq->altq, 0, + &bwspec, interface->ifname[0] != 0, + opts); + if (nqueues && nqueues->tail) { + printf("{ "); + LOOP_THROUGH(struct node_queue, + queue, nqueues, + printf("%s ", + queue->queue); + ); + printf("}"); + } + printf("\n"); + } + } + ); + ); + + FREE_LIST(struct node_queue, nqueues); + FREE_LIST(struct node_if, interfaces); + + if (!found) { + yyerror("queue %s has no parent", a->qname); + errs++; + } + + if (errs) + return (1); + else + return (0); +} + +void +expand_rule(struct pf_rule *r, + struct node_if *interfaces, struct node_host *rpool_hosts, + struct node_proto *protos, struct node_os *src_oses, + struct node_host *src_hosts, struct node_port *src_ports, + struct node_host *dst_hosts, struct node_port *dst_ports, + struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types, + const char *anchor_call) +{ + sa_family_t af = r->af; + int added = 0, error = 0; + char ifname[IF_NAMESIZE]; + char label[PF_RULE_LABEL_SIZE]; + char tagname[PF_TAG_NAME_SIZE]; + char match_tagname[PF_TAG_NAME_SIZE]; + struct pf_pooladdr *pa; + struct node_host *h; + u_int8_t flags, flagset, keep_state; + + if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label)) + errx(1, "expand_rule: strlcpy"); + if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname)) + errx(1, "expand_rule: strlcpy"); + if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >= + sizeof(match_tagname)) + errx(1, "expand_rule: strlcpy"); + flags = r->flags; + flagset = r->flagset; + keep_state = r->keep_state; + + LOOP_THROUGH(struct node_if, interface, interfaces, + LOOP_THROUGH(struct node_proto, proto, protos, + LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types, + LOOP_THROUGH(struct node_host, src_host, src_hosts, + LOOP_THROUGH(struct node_port, src_port, src_ports, + LOOP_THROUGH(struct node_os, src_os, src_oses, + LOOP_THROUGH(struct node_host, dst_host, dst_hosts, + LOOP_THROUGH(struct node_port, dst_port, dst_ports, + LOOP_THROUGH(struct node_uid, uid, uids, + LOOP_THROUGH(struct node_gid, gid, gids, + + r->af = af; + /* for link-local IPv6 address, interface must match up */ + if ((r->af && src_host->af && r->af != src_host->af) || + (r->af && dst_host->af && r->af != dst_host->af) || + (src_host->af && dst_host->af && + src_host->af != dst_host->af) || + (src_host->ifindex && dst_host->ifindex && + src_host->ifindex != dst_host->ifindex) || + (src_host->ifindex && *interface->ifname && + src_host->ifindex != if_nametoindex(interface->ifname)) || + (dst_host->ifindex && *interface->ifname && + dst_host->ifindex != if_nametoindex(interface->ifname))) + continue; + if (!r->af && src_host->af) + r->af = src_host->af; + else if (!r->af && dst_host->af) + r->af = dst_host->af; + + if (*interface->ifname) + strlcpy(r->ifname, interface->ifname, + sizeof(r->ifname)); + else if (if_indextoname(src_host->ifindex, ifname)) + strlcpy(r->ifname, ifname, sizeof(r->ifname)); + else if (if_indextoname(dst_host->ifindex, ifname)) + strlcpy(r->ifname, ifname, sizeof(r->ifname)); + else + memset(r->ifname, '\0', sizeof(r->ifname)); + + if (strlcpy(r->label, label, sizeof(r->label)) >= + sizeof(r->label)) + errx(1, "expand_rule: strlcpy"); + if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >= + sizeof(r->tagname)) + errx(1, "expand_rule: strlcpy"); + if (strlcpy(r->match_tagname, match_tagname, + sizeof(r->match_tagname)) >= sizeof(r->match_tagname)) + errx(1, "expand_rule: strlcpy"); + expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af, + src_host, src_port, dst_host, dst_port, proto->proto); + expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af, + src_host, src_port, dst_host, dst_port, proto->proto); + expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname, + r->af, src_host, src_port, dst_host, dst_port, + proto->proto); + + error += check_netmask(src_host, r->af); + error += check_netmask(dst_host, r->af); + + r->ifnot = interface->not; + r->proto = proto->proto; + r->src.addr = src_host->addr; + r->src.neg = src_host->not; + r->src.port[0] = src_port->port[0]; + r->src.port[1] = src_port->port[1]; + r->src.port_op = src_port->op; + r->dst.addr = dst_host->addr; + r->dst.neg = dst_host->not; + r->dst.port[0] = dst_port->port[0]; + r->dst.port[1] = dst_port->port[1]; + r->dst.port_op = dst_port->op; + r->uid.op = uid->op; + r->uid.uid[0] = uid->uid[0]; + r->uid.uid[1] = uid->uid[1]; + r->gid.op = gid->op; + r->gid.gid[0] = gid->gid[0]; + r->gid.gid[1] = gid->gid[1]; + r->type = icmp_type->type; + r->code = icmp_type->code; + + if ((keep_state == PF_STATE_MODULATE || + keep_state == PF_STATE_SYNPROXY) && + r->proto && r->proto != IPPROTO_TCP) + r->keep_state = PF_STATE_NORMAL; + else + r->keep_state = keep_state; + + if (r->proto && r->proto != IPPROTO_TCP) { + r->flags = 0; + r->flagset = 0; + } else { + r->flags = flags; + r->flagset = flagset; + } + if (icmp_type->proto && r->proto != icmp_type->proto) { + yyerror("icmp-type mismatch"); + error++; + } + + if (src_os && src_os->os) { + r->os_fingerprint = pfctl_get_fingerprint(src_os->os); + if ((pf->opts & PF_OPT_VERBOSE2) && + r->os_fingerprint == PF_OSFP_NOMATCH) + fprintf(stderr, + "warning: unknown '%s' OS fingerprint\n", + src_os->os); + } else { + r->os_fingerprint = PF_OSFP_ANY; + } + + TAILQ_INIT(&r->rpool.list); + for (h = rpool_hosts; h != NULL; h = h->next) { + pa = calloc(1, sizeof(struct pf_pooladdr)); + if (pa == NULL) + err(1, "expand_rule: calloc"); + pa->addr = h->addr; + if (h->ifname != NULL) { + if (strlcpy(pa->ifname, h->ifname, + sizeof(pa->ifname)) >= + sizeof(pa->ifname)) + errx(1, "expand_rule: strlcpy"); + } else + pa->ifname[0] = 0; + TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries); + } + + if (rule_consistent(r, anchor_call[0]) < 0 || error) + yyerror("skipping rule due to errors"); + else { + r->nr = pf->astack[pf->asd]->match++; + pfctl_add_rule(pf, r, anchor_call); + added++; + } + + )))))))))); + + FREE_LIST(struct node_if, interfaces); + FREE_LIST(struct node_proto, protos); + FREE_LIST(struct node_host, src_hosts); + FREE_LIST(struct node_port, src_ports); + FREE_LIST(struct node_os, src_oses); + FREE_LIST(struct node_host, dst_hosts); + FREE_LIST(struct node_port, dst_ports); + FREE_LIST(struct node_uid, uids); + FREE_LIST(struct node_gid, gids); + FREE_LIST(struct node_icmp, icmp_types); + FREE_LIST(struct node_host, rpool_hosts); + + if (!added) + yyerror("rule expands to no valid combination"); +} + +int +expand_skip_interface(struct node_if *interfaces) +{ + int errs = 0; + + if (!interfaces || (!interfaces->next && !interfaces->not && + !strcmp(interfaces->ifname, "none"))) { + if (pf->opts & PF_OPT_VERBOSE) + printf("set skip on none\n"); + errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0); + return (errs); + } + + if (pf->opts & PF_OPT_VERBOSE) + printf("set skip on {"); + LOOP_THROUGH(struct node_if, interface, interfaces, + if (pf->opts & PF_OPT_VERBOSE) + printf(" %s", interface->ifname); + if (interface->not) { + yyerror("skip on ! is not supported"); + errs++; + } else + errs += pfctl_set_interface_flags(pf, + interface->ifname, PFI_IFLAG_SKIP, 1); + ); + if (pf->opts & PF_OPT_VERBOSE) + printf(" }\n"); + + FREE_LIST(struct node_if, interfaces); + + if (errs) + return (1); + else + return (0); +} + +#undef FREE_LIST +#undef LOOP_THROUGH + +int +check_rulestate(int desired_state) +{ + if (require_order && (rulestate > desired_state)) { + yyerror("Rules must be in order: options, normalization, " + "queueing, translation, filtering"); + return (1); + } + rulestate = desired_state; + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "all", ALL}, + { "allow-opts", ALLOWOPTS}, + { "altq", ALTQ}, + { "anchor", ANCHOR}, + { "antispoof", ANTISPOOF}, + { "any", ANY}, + { "bandwidth", BANDWIDTH}, + { "binat", BINAT}, + { "binat-anchor", BINATANCHOR}, + { "bitmask", BITMASK}, + { "block", BLOCK}, + { "block-policy", BLOCKPOLICY}, + { "cbq", CBQ}, + { "code", CODE}, + { "crop", FRAGCROP}, + { "debug", DEBUG}, + { "divert-reply", DIVERTREPLY}, + { "divert-to", DIVERTTO}, + { "drop", DROP}, + { "drop-ovl", FRAGDROP}, + { "dup-to", DUPTO}, + { "fastroute", FASTROUTE}, + { "file", FILENAME}, + { "fingerprints", FINGERPRINTS}, + { "flags", FLAGS}, + { "floating", FLOATING}, + { "flush", FLUSH}, + { "for", FOR}, + { "fragment", FRAGMENT}, + { "from", FROM}, + { "global", GLOBAL}, + { "group", GROUP}, + { "hfsc", HFSC}, + { "hostid", HOSTID}, + { "icmp-type", ICMPTYPE}, + { "icmp6-type", ICMP6TYPE}, + { "if-bound", IFBOUND}, + { "in", IN}, + { "include", INCLUDE}, + { "inet", INET}, + { "inet6", INET6}, + { "keep", KEEP}, + { "label", LABEL}, + { "limit", LIMIT}, + { "linkshare", LINKSHARE}, + { "load", LOAD}, + { "log", LOG}, + { "loginterface", LOGINTERFACE}, + { "max", MAXIMUM}, + { "max-mss", MAXMSS}, + { "max-src-conn", MAXSRCCONN}, + { "max-src-conn-rate", MAXSRCCONNRATE}, + { "max-src-nodes", MAXSRCNODES}, + { "max-src-states", MAXSRCSTATES}, + { "min-ttl", MINTTL}, + { "modulate", MODULATE}, + { "nat", NAT}, + { "nat-anchor", NATANCHOR}, + { "no", NO}, + { "no-df", NODF}, + { "no-route", NOROUTE}, + { "no-sync", NOSYNC}, + { "on", ON}, + { "optimization", OPTIMIZATION}, + { "os", OS}, + { "out", OUT}, + { "overload", OVERLOAD}, + { "pass", PASS}, + { "port", PORT}, + { "priority", PRIORITY}, + { "priq", PRIQ}, + { "probability", PROBABILITY}, + { "proto", PROTO}, + { "qlimit", QLIMIT}, + { "queue", QUEUE}, + { "quick", QUICK}, + { "random", RANDOM}, + { "random-id", RANDOMID}, + { "rdr", RDR}, + { "rdr-anchor", RDRANCHOR}, + { "realtime", REALTIME}, + { "reassemble", REASSEMBLE}, + { "reply-to", REPLYTO}, + { "require-order", REQUIREORDER}, + { "return", RETURN}, + { "return-icmp", RETURNICMP}, + { "return-icmp6", RETURNICMP6}, + { "return-rst", RETURNRST}, + { "round-robin", ROUNDROBIN}, + { "route", ROUTE}, + { "route-to", ROUTETO}, + { "rtable", RTABLE}, + { "rule", RULE}, + { "ruleset-optimization", RULESET_OPTIMIZATION}, + { "scrub", SCRUB}, + { "set", SET}, + { "set-tos", SETTOS}, + { "skip", SKIP}, + { "sloppy", SLOPPY}, + { "source-hash", SOURCEHASH}, + { "source-track", SOURCETRACK}, + { "state", STATE}, + { "state-defaults", STATEDEFAULTS}, + { "state-policy", STATEPOLICY}, + { "static-port", STATICPORT}, + { "sticky-address", STICKYADDRESS}, + { "synproxy", SYNPROXY}, + { "table", TABLE}, + { "tag", TAG}, + { "tagged", TAGGED}, + { "tbrsize", TBRSIZE}, + { "timeout", TIMEOUT}, + { "to", TO}, + { "tos", TOS}, + { "ttl", TTL}, + { "upperlimit", UPPERLIMIT}, + { "urpf-failed", URPFFAILED}, + { "user", USER}, + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) { + if (debug > 1) + fprintf(stderr, "%s: %d\n", s, p->k_val); + return (p->k_val); + } else { + if (debug > 1) + fprintf(stderr, "string: %s\n", s); + return (STRING); + } +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing quoted string"); + if (popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + while (c == EOF) { + if (popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + + /* skip to either EOF or the first real EOL */ + while (1) { + if (pushback_index) + c = pushback_buffer[--pushback_index]; + else + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int quotec, next, c; + int token; + +top: + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = (char)c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || c == ' ' || c == '\t') + c = next; + else if (next == '\n') + continue; + else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + err(1, "yylex: strdup"); + return (STRING); + case '<': + next = lgetc(0); + if (next == '>') { + yylval.v.i = PF_OP_XRG; + return (PORTBINARY); + } + lungetc(next); + break; + case '>': + next = lgetc(0); + if (next == '<') { + yylval.v.i = PF_OP_IRG; + return (PORTBINARY); + } + lungetc(next); + break; + case '-': + next = lgetc(0); + if (next == '>') + return (ARROW); + lungetc(next); + break; + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '/' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + warn("cannot stat %s", fname); + return (-1); + } + if (st.st_uid != 0 && st.st_uid != getuid()) { + warnx("%s: owner not root or current user", fname); + return (-1); + } + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + warnx("%s: group/world readable/writeable", fname); + return (-1); + } + return (0); +} + +struct file * +pushfile(const char *name, int secret) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(struct file))) == NULL || + (nfile->name = strdup(name)) == NULL) { + warn("malloc"); + return (NULL); + } + if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) { + nfile->stream = stdin; + free(nfile->name); + if ((nfile->name = strdup("stdin")) == NULL) { + warn("strdup"); + free(nfile); + return (NULL); + } + } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + warn("%s", nfile->name); + free(nfile->name); + free(nfile); + return (NULL); + } else if (secret && + check_file_secrecy(fileno(nfile->stream), nfile->name)) { + fclose(nfile->stream); + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { + prev->errors += file->errors; + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (0); + } + return (EOF); +} + +int +parse_config(char *filename, struct pfctl *xpf) +{ + int errors = 0; + struct sym *sym; + + pf = xpf; + errors = 0; + rulestate = PFCTL_STATE_NONE; + returnicmpdefault = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT; + returnicmp6default = + (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT; + blockpolicy = PFRULE_DROP; + require_order = 1; + + if ((file = pushfile(filename, 0)) == NULL) { + warn("cannot open the main config file!"); + return (-1); + } + + yyparse(); + errors = file->errors; + popfile(); + + /* Free macros and check which have not been used. */ + while ((sym = TAILQ_FIRST(&symhead))) { + if ((pf->opts & PF_OPT_VERBOSE2) && !sym->used) + fprintf(stderr, "warning: macro '%s' not " + "used\n", sym->nam); + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + + return (errors ? -1 : 0); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entry)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return (0); +} + +int +pfctl_cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL) + err(1, "pfctl_cmdline_symset: malloc"); + + strlcpy(sym, s, strlen(s) - strlen(val) + 1); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +void +mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst) +{ + int i; + struct pf_rule *r; + + for (i = 0; i < PF_RULESET_MAX; ++i) { + while ((r = TAILQ_FIRST(src->rules[i].active.ptr)) + != NULL) { + TAILQ_REMOVE(src->rules[i].active.ptr, r, entries); + TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries); + dst->anchor->match++; + } + src->anchor->match = 0; + while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr)) + != NULL) { + TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries); + TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr, + r, entries); + } + } +} + +void +decide_address_family(struct node_host *n, sa_family_t *af) +{ + if (*af != 0 || n == NULL) + return; + *af = n->af; + while ((n = n->next) != NULL) { + if (n->af != *af) { + *af = 0; + return; + } + } +} + +void +remove_invalid_hosts(struct node_host **nh, sa_family_t *af) +{ + struct node_host *n = *nh, *prev = NULL; + + while (n != NULL) { + if (*af && n->af && n->af != *af) { + /* unlink and free n */ + struct node_host *next = n->next; + + /* adjust tail pointer */ + if (n == (*nh)->tail) + (*nh)->tail = prev; + /* adjust previous node's next pointer */ + if (prev == NULL) + *nh = next; + else + prev->next = next; + /* free node */ + if (n->ifname != NULL) + free(n->ifname); + free(n); + n = next; + } else { + if (n->af && !*af) + *af = n->af; + prev = n; + n = n->next; + } + } +} + +int +invalid_redirect(struct node_host *nh, sa_family_t af) +{ + if (!af) { + struct node_host *n; + + /* tables and dyniftl are ok without an address family */ + for (n = nh; n != NULL; n = n->next) { + if (n->addr.type != PF_ADDR_TABLE && + n->addr.type != PF_ADDR_DYNIFTL) { + yyerror("address family not given and " + "translation address expands to multiple " + "address families"); + return (1); + } + } + } + if (nh == NULL) { + yyerror("no translation address with matching address family " + "found."); + return (1); + } + return (0); +} + +int +atoul(char *s, u_long *ulvalp) +{ + u_long ulval; + char *ep; + + errno = 0; + ulval = strtoul(s, &ep, 0); + if (s[0] == '\0' || *ep != '\0') + return (-1); + if (errno == ERANGE && ulval == ULONG_MAX) + return (-1); + *ulvalp = ulval; + return (0); +} + +int +getservice(char *n) +{ + struct servent *s; + u_long ulval; + + if (atoul(n, &ulval) == 0) { + if (ulval > 65535) { + yyerror("illegal port value %lu", ulval); + return (-1); + } + return (htons(ulval)); + } else { + s = getservbyname(n, "tcp"); + if (s == NULL) + s = getservbyname(n, "udp"); + if (s == NULL) { + yyerror("unknown port %s", n); + return (-1); + } + return (s->s_port); + } +} + +int +rule_label(struct pf_rule *r, char *s) +{ + if (s) { + if (strlcpy(r->label, s, sizeof(r->label)) >= + sizeof(r->label)) { + yyerror("rule label too long (max %d chars)", + sizeof(r->label)-1); + return (-1); + } + } + return (0); +} + +u_int16_t +parseicmpspec(char *w, sa_family_t af) +{ + const struct icmpcodeent *p; + u_long ulval; + u_int8_t icmptype; + + if (af == AF_INET) + icmptype = returnicmpdefault >> 8; + else + icmptype = returnicmp6default >> 8; + + if (atoul(w, &ulval) == -1) { + if ((p = geticmpcodebyname(icmptype, w, af)) == NULL) { + yyerror("unknown icmp code %s", w); + return (0); + } + ulval = p->code; + } + if (ulval > 255) { + yyerror("invalid icmp code %lu", ulval); + return (0); + } + return (icmptype << 8 | ulval); +} + +int +parseport(char *port, struct range *r, int extensions) +{ + char *p = strchr(port, ':'); + + if (p == NULL) { + if ((r->a = getservice(port)) == -1) + return (-1); + r->b = 0; + r->t = PF_OP_NONE; + return (0); + } + if ((extensions & PPORT_STAR) && !strcmp(p+1, "*")) { + *p = 0; + if ((r->a = getservice(port)) == -1) + return (-1); + r->b = 0; + r->t = PF_OP_IRG; + return (0); + } + if ((extensions & PPORT_RANGE)) { + *p++ = 0; + if ((r->a = getservice(port)) == -1 || + (r->b = getservice(p)) == -1) + return (-1); + if (r->a == r->b) { + r->b = 0; + r->t = PF_OP_NONE; + } else + r->t = PF_OP_RRG; + return (0); + } + return (-1); +} + +int +pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans) +{ + struct loadanchors *la; + + TAILQ_FOREACH(la, &loadanchorshead, entries) { + if (pf->opts & PF_OPT_VERBOSE) + fprintf(stderr, "\nLoading anchor %s from %s\n", + la->anchorname, la->filename); + if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize, + la->anchorname, trans) == -1) + return (-1); + } + + return (0); +} + +int +rt_tableid_max(void) +{ +#ifdef __FreeBSD__ + int fibs; + size_t l = sizeof(fibs); + + if (sysctlbyname("net.fibs", &fibs, &l, NULL, 0) == -1) + fibs = 16; /* XXX RT_MAXFIBS, at least limit it some. */ + /* + * As the OpenBSD code only compares > and not >= we need to adjust + * here given we only accept values of 0..n and want to avoid #ifdefs + * in the grammer. + */ + return (fibs - 1); +#else + return (RT_TABLEID_MAX); +#endif +} diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c new file mode 100644 index 0000000000000..d6637b445c350 --- /dev/null +++ b/sbin/pfctl/pf_print_state.c @@ -0,0 +1,375 @@ +/* $OpenBSD: pf_print_state.c,v 1.52 2008/08/12 16:40:18 david Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#ifdef __FreeBSD__ +#include +#define betoh64 be64toh +#endif +#include +#define TCPSTATES +#include +#include +#include +#include + +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +void print_name(struct pf_addr *, sa_family_t); + +void +print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) +{ + switch (addr->type) { + case PF_ADDR_DYNIFTL: + printf("(%s", addr->v.ifname); + if (addr->iflags & PFI_AFLAG_NETWORK) + printf(":network"); + if (addr->iflags & PFI_AFLAG_BROADCAST) + printf(":broadcast"); + if (addr->iflags & PFI_AFLAG_PEER) + printf(":peer"); + if (addr->iflags & PFI_AFLAG_NOALIAS) + printf(":0"); + if (verbose) { + if (addr->p.dyncnt <= 0) + printf(":*"); + else + printf(":%d", addr->p.dyncnt); + } + printf(")"); + break; + case PF_ADDR_TABLE: + if (verbose) + if (addr->p.tblcnt == -1) + printf("<%s:*>", addr->v.tblname); + else + printf("<%s:%d>", addr->v.tblname, + addr->p.tblcnt); + else + printf("<%s>", addr->v.tblname); + return; + case PF_ADDR_RANGE: { + char buf[48]; + + if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL) + printf("?"); + else + printf("%s", buf); + if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL) + printf(" - ?"); + else + printf(" - %s", buf); + break; + } + case PF_ADDR_ADDRMASK: + if (PF_AZERO(&addr->v.a.addr, AF_INET6) && + PF_AZERO(&addr->v.a.mask, AF_INET6)) + printf("any"); + else { + char buf[48]; + + if (inet_ntop(af, &addr->v.a.addr, buf, + sizeof(buf)) == NULL) + printf("?"); + else + printf("%s", buf); + } + break; + case PF_ADDR_NOROUTE: + printf("no-route"); + return; + case PF_ADDR_URPFFAILED: + printf("urpf-failed"); + return; + default: + printf("?"); + return; + } + + /* mask if not _both_ address and mask are zero */ + if (addr->type != PF_ADDR_RANGE && + !(PF_AZERO(&addr->v.a.addr, AF_INET6) && + PF_AZERO(&addr->v.a.mask, AF_INET6))) { + int bits = unmask(&addr->v.a.mask, af); + + if (bits != (af == AF_INET ? 32 : 128)) + printf("/%d", bits); + } +} + +void +print_name(struct pf_addr *addr, sa_family_t af) +{ + char host[NI_MAXHOST]; + + strlcpy(host, "?", sizeof(host)); + switch (af) { + case AF_INET: { + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr = addr->v4; + getnameinfo((struct sockaddr *)&sin, sin.sin_len, + host, sizeof(host), NULL, 0, NI_NOFQDN); + break; + } + case AF_INET6: { + struct sockaddr_in6 sin6; + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = addr->v6; + getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + host, sizeof(host), NULL, 0, NI_NOFQDN); + break; + } + } + printf("%s", host); +} + +void +print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts) +{ + if (opts & PF_OPT_USEDNS) + print_name(addr, af); + else { + struct pf_addr_wrap aw; + + memset(&aw, 0, sizeof(aw)); + aw.v.a.addr = *addr; + if (af == AF_INET) + aw.v.a.mask.addr32[0] = 0xffffffff; + else { + memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); + af = AF_INET6; + } + print_addr(&aw, af, opts & PF_OPT_VERBOSE2); + } + + if (port) { + if (af == AF_INET) + printf(":%u", ntohs(port)); + else + printf("[%u]", ntohs(port)); + } +} + +void +print_seq(struct pfsync_state_peer *p) +{ + if (p->seqdiff) + printf("[%u + %u](+%u)", ntohl(p->seqlo), + ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff)); + else + printf("[%u + %u]", ntohl(p->seqlo), + ntohl(p->seqhi) - ntohl(p->seqlo)); +} + +void +print_state(struct pfsync_state *s, int opts) +{ + struct pfsync_state_peer *src, *dst; + struct pfsync_state_key *sk, *nk; + struct protoent *p; + int min, sec; + + if (s->direction == PF_OUT) { + src = &s->src; + dst = &s->dst; + sk = &s->key[PF_SK_STACK]; + nk = &s->key[PF_SK_WIRE]; + if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) + sk->port[0] = nk->port[0]; + } else { + src = &s->dst; + dst = &s->src; + sk = &s->key[PF_SK_WIRE]; + nk = &s->key[PF_SK_STACK]; + if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) + sk->port[1] = nk->port[1]; + } + printf("%s ", s->ifname); + if ((p = getprotobynumber(s->proto)) != NULL) + printf("%s ", p->p_name); + else + printf("%u ", s->proto); + + print_host(&nk->addr[1], nk->port[1], s->af, opts); + if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) || + nk->port[1] != sk->port[1]) { + printf(" ("); + print_host(&sk->addr[1], sk->port[1], s->af, opts); + printf(")"); + } + if (s->direction == PF_OUT) + printf(" -> "); + else + printf(" <- "); + print_host(&nk->addr[0], nk->port[0], s->af, opts); + if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) || + nk->port[0] != sk->port[0]) { + printf(" ("); + print_host(&sk->addr[0], sk->port[0], s->af, opts); + printf(")"); + } + + printf(" "); + if (s->proto == IPPROTO_TCP) { + if (src->state <= TCPS_TIME_WAIT && + dst->state <= TCPS_TIME_WAIT) + printf(" %s:%s\n", tcpstates[src->state], + tcpstates[dst->state]); + else if (src->state == PF_TCPS_PROXY_SRC || + dst->state == PF_TCPS_PROXY_SRC) + printf(" PROXY:SRC\n"); + else if (src->state == PF_TCPS_PROXY_DST || + dst->state == PF_TCPS_PROXY_DST) + printf(" PROXY:DST\n"); + else + printf(" \n", + src->state, dst->state); + if (opts & PF_OPT_VERBOSE) { + printf(" "); + print_seq(src); + if (src->wscale && dst->wscale) + printf(" wscale %u", + src->wscale & PF_WSCALE_MASK); + printf(" "); + print_seq(dst); + if (src->wscale && dst->wscale) + printf(" wscale %u", + dst->wscale & PF_WSCALE_MASK); + printf("\n"); + } + } else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES && + dst->state < PFUDPS_NSTATES) { + const char *states[] = PFUDPS_NAMES; + + printf(" %s:%s\n", states[src->state], states[dst->state]); + } else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES && + dst->state < PFOTHERS_NSTATES) { + /* XXX ICMP doesn't really have state levels */ + const char *states[] = PFOTHERS_NAMES; + + printf(" %s:%s\n", states[src->state], states[dst->state]); + } else { + printf(" %u:%u\n", src->state, dst->state); + } + + if (opts & PF_OPT_VERBOSE) { + u_int64_t packets[2]; + u_int64_t bytes[2]; + u_int32_t creation = ntohl(s->creation); + u_int32_t expire = ntohl(s->expire); + + sec = creation % 60; + creation /= 60; + min = creation % 60; + creation /= 60; + printf(" age %.2u:%.2u:%.2u", creation, min, sec); + sec = expire % 60; + expire /= 60; + min = expire % 60; + expire /= 60; + printf(", expires in %.2u:%.2u:%.2u", expire, min, sec); + + bcopy(s->packets[0], &packets[0], sizeof(u_int64_t)); + bcopy(s->packets[1], &packets[1], sizeof(u_int64_t)); + bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t)); + bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t)); + printf(", %llu:%llu pkts, %llu:%llu bytes", +#ifdef __FreeBSD__ + (unsigned long long)betoh64(packets[0]), + (unsigned long long)betoh64(packets[1]), + (unsigned long long)betoh64(bytes[0]), + (unsigned long long)betoh64(bytes[1])); +#else + betoh64(packets[0]), + betoh64(packets[1]), + betoh64(bytes[0]), + betoh64(bytes[1])); +#endif + if (ntohl(s->anchor) != -1) + printf(", anchor %u", ntohl(s->anchor)); + if (ntohl(s->rule) != -1) + printf(", rule %u", ntohl(s->rule)); + if (s->state_flags & PFSTATE_SLOPPY) + printf(", sloppy"); + if (s->sync_flags & PFSYNC_FLAG_SRCNODE) + printf(", source-track"); + if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE) + printf(", sticky-address"); + printf("\n"); + } + if (opts & PF_OPT_VERBOSE2) { + u_int64_t id; + + bcopy(&s->id, &id, sizeof(u_int64_t)); + printf(" id: %016llx creatorid: %08x", +#ifdef __FreeBSD__ + (unsigned long long)betoh64(id), ntohl(s->creatorid)); +#else + betoh64(id), ntohl(s->creatorid)); +#endif + printf("\n"); + } +} + +int +unmask(struct pf_addr *m, sa_family_t af) +{ + int i = 31, j = 0, b = 0; + u_int32_t tmp; + + while (j < 4 && m->addr32[j] == 0xffffffff) { + b += 32; + j++; + } + if (j < 4) { + tmp = ntohl(m->addr32[j]); + for (i = 31; tmp & (1 << i); --i) + b++; + } + return (b); +} diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8 new file mode 100644 index 0000000000000..b178a07e8434f --- /dev/null +++ b/sbin/pfctl/pfctl.8 @@ -0,0 +1,687 @@ +.\" $OpenBSD: pfctl.8,v 1.138 2008/06/10 20:55:02 mcbride Exp $ +.\" +.\" Copyright (c) 2001 Kjell Wooding. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 21, 2011 +.Dt PFCTL 8 +.Os +.Sh NAME +.Nm pfctl +.Nd control the packet filter (PF) device +.Sh SYNOPSIS +.Nm pfctl +.Bk -words +.Op Fl AdeghmNnOPqRrvz +.Op Fl a Ar anchor +.Oo Fl D Ar macro Ns = +.Ar value Oc +.Op Fl F Ar modifier +.Op Fl f Ar file +.Op Fl i Ar interface +.Op Fl K Ar host | network +.Xo +.Oo Fl k +.Ar host | network | label | id +.Oc Xc +.Op Fl o Ar level +.Op Fl p Ar device +.Op Fl s Ar modifier +.Xo +.Oo Fl t Ar table +.Fl T Ar command +.Op Ar address ... Oc +.Xc +.Op Fl x Ar level +.Ek +.Sh DESCRIPTION +The +.Nm +utility communicates with the packet filter device using the +ioctl interface described in +.Xr pf 4 . +It allows ruleset and parameter configuration and retrieval of status +information from the packet filter. +.Pp +Packet filtering restricts the types of packets that pass through +network interfaces entering or leaving the host based on filter +rules as described in +.Xr pf.conf 5 . +The packet filter can also replace addresses and ports of packets. +Replacing source addresses and ports of outgoing packets is called +NAT (Network Address Translation) and is used to connect an internal +network (usually reserved address space) to an external one (the +Internet) by making all connections to external hosts appear to +come from the gateway. +Replacing destination addresses and ports of incoming packets +is used to redirect connections to different hosts and/or ports. +A combination of both translations, bidirectional NAT, is also +supported. +Translation rules are described in +.Xr pf.conf 5 . +.Pp +When the variable +.Va pf +is set to +.Dv YES +in +.Xr rc.conf 5 , +the rule file specified with the variable +.Va pf_rules +is loaded automatically by the +.Xr rc 8 +scripts and the packet filter is enabled. +.Pp +The packet filter does not itself forward packets between interfaces. +Forwarding can be enabled by setting the +.Xr sysctl 8 +variables +.Em net.inet.ip.forwarding +and/or +.Em net.inet6.ip6.forwarding +to 1. +Set them permanently in +.Xr sysctl.conf 5 . +.Pp +The +.Nm +utility provides several commands. +The options are as follows: +.Bl -tag -width Ds +.It Fl A +Load only the queue rules present in the rule file. +Other rules and options are ignored. +.It Fl a Ar anchor +Apply flags +.Fl f , +.Fl F , +and +.Fl s +only to the rules in the specified +.Ar anchor . +In addition to the main ruleset, +.Nm +can load and manipulate additional rulesets by name, +called anchors. +The main ruleset is the default anchor. +.Pp +Anchors are referenced by name and may be nested, +with the various components of the anchor path separated by +.Sq / +characters, similar to how file system hierarchies are laid out. +The last component of the anchor path is where ruleset operations are +performed. +.Pp +Evaluation of +.Ar anchor +rules from the main ruleset is described in +.Xr pf.conf 5 . +.Pp +For example, the following will show all filter rules (see the +.Fl s +flag below) inside the anchor +.Dq authpf/smith(1234) , +which would have been created for user +.Dq smith +by +.Xr authpf 8 , +PID 1234: +.Bd -literal -offset indent +# pfctl -a "authpf/smith(1234)" -s rules +.Ed +.Pp +Private tables can also be put inside anchors, either by having table +statements in the +.Xr pf.conf 5 +file that is loaded in the anchor, or by using regular table commands, as in: +.Bd -literal -offset indent +# pfctl -a foo/bar -t mytable -T add 1.2.3.4 5.6.7.8 +.Ed +.Pp +When a rule referring to a table is loaded in an anchor, the rule will use the +private table if one is defined, and then fall back to the table defined in the +main ruleset, if there is one. +This is similar to C rules for variable scope. +It is possible to create distinct tables with the same name in the global +ruleset and in an anchor, but this is often bad design and a warning will be +issued in that case. +.Pp +By default, recursive inline printing of anchors applies only to unnamed +anchors specified inline in the ruleset. +If the anchor name is terminated with a +.Sq * +character, the +.Fl s +flag will recursively print all anchors in a brace delimited block. +For example the following will print the +.Dq authpf +ruleset recursively: +.Bd -literal -offset indent +# pfctl -a 'authpf/*' -sr +.Ed +.Pp +To print the main ruleset recursively, specify only +.Sq * +as the anchor name: +.Bd -literal -offset indent +# pfctl -a '*' -sr +.Ed +.It Fl D Ar macro Ns = Ns Ar value +Define +.Ar macro +to be set to +.Ar value +on the command line. +Overrides the definition of +.Ar macro +in the ruleset. +.It Fl d +Disable the packet filter. +.It Fl e +Enable the packet filter. +.It Fl F Ar modifier +Flush the filter parameters specified by +.Ar modifier +(may be abbreviated): +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Fl F Cm nat +Flush the NAT rules. +.It Fl F Cm queue +Flush the queue rules. +.It Fl F Cm rules +Flush the filter rules. +.It Fl F Cm states +Flush the state table (NAT and filter). +.It Fl F Cm Sources +Flush the source tracking table. +.It Fl F Cm info +Flush the filter information (statistics that are not bound to rules). +.It Fl F Cm Tables +Flush the tables. +.It Fl F Cm osfp +Flush the passive operating system fingerprints. +.It Fl F Cm all +Flush all of the above. +.El +.It Fl f Ar file +Load the rules contained in +.Ar file . +This +.Ar file +may contain macros, tables, options, and normalization, queueing, +translation, and filtering rules. +With the exception of macros and tables, the statements must appear in that +order. +.It Fl g +Include output helpful for debugging. +.It Fl h +Help. +.It Fl i Ar interface +Restrict the operation to the given +.Ar interface . +.It Fl K Ar host | network +Kill all of the source tracking entries originating from the specified +.Ar host +or +.Ar network . +A second +.Fl K Ar host +or +.Fl K Ar network +option may be specified, which will kill all the source tracking +entries from the first host/network to the second. +.It Xo +.Fl k +.Ar host | network | label | id +.Xc +Kill all of the state entries matching the specified +.Ar host , +.Ar network , +.Ar label , +or +.Ar id . +.Pp +For example, to kill all of the state entries originating from +.Dq host : +.Pp +.Dl # pfctl -k host +.Pp +A second +.Fl k Ar host +or +.Fl k Ar network +option may be specified, which will kill all the state entries +from the first host/network to the second. +To kill all of the state entries from +.Dq host1 +to +.Dq host2 : +.Pp +.Dl # pfctl -k host1 -k host2 +.Pp +To kill all states originating from 192.168.1.0/24 to 172.16.0.0/16: +.Pp +.Dl # pfctl -k 192.168.1.0/24 -k 172.16.0.0/16 +.Pp +A network prefix length of 0 can be used as a wildcard. +To kill all states with the target +.Dq host2 : +.Pp +.Dl # pfctl -k 0.0.0.0/0 -k host2 +.Pp +It is also possible to kill states by rule label or state ID. +In this mode the first +.Fl k +argument is used to specify the type +of the second argument. +The following command would kill all states that have been created +from rules carrying the label +.Dq foobar : +.Pp +.Dl # pfctl -k label -k foobar +.Pp +To kill one specific state by its unique state ID +(as shown by pfctl -s state -vv), +use the +.Ar id +modifier and as a second argument the state ID and optional creator ID. +To kill a state with ID 4823e84500000003 use: +.Pp +.Dl # pfctl -k id -k 4823e84500000003 +.Pp +To kill a state with ID 4823e84500000018 created from a backup +firewall with hostid 00000002 use: +.Pp +.Dl # pfctl -k id -k 4823e84500000018/2 +.Pp +.It Fl m +Merge in explicitly given options without resetting those +which are omitted. +Allows single options to be modified without disturbing the others: +.Bd -literal -offset indent +# echo "set loginterface fxp0" | pfctl -mf - +.Ed +.It Fl N +Load only the NAT rules present in the rule file. +Other rules and options are ignored. +.It Fl n +Do not actually load rules, just parse them. +.It Fl O +Load only the options present in the rule file. +Other rules and options are ignored. +.It Fl o Ar level +Control the ruleset optimizer, overriding any rule file settings. +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Fl o Cm none +Disable the ruleset optimizer. +.It Fl o Cm basic +Enable basic ruleset optimizations. +This is the default behaviour. +.It Fl o Cm profile +Enable basic ruleset optimizations with profiling. +.El +For further information on the ruleset optimizer, see +.Xr pf.conf 5 . +.It Fl P +Do not perform service name lookup for port specific rules, +instead display the ports numerically. +.It Fl p Ar device +Use the device file +.Ar device +instead of the default +.Pa /dev/pf . +.It Fl q +Only print errors and warnings. +.It Fl R +Load only the filter rules present in the rule file. +Other rules and options are ignored. +.It Fl r +Perform reverse DNS lookups on states when displaying them. +.It Fl s Ar modifier +Show the filter parameters specified by +.Ar modifier +(may be abbreviated): +.Pp +.Bl -tag -width xxxxxxxxxxxxx -compact +.It Fl s Cm nat +Show the currently loaded NAT rules. +.It Fl s Cm queue +Show the currently loaded queue rules. +When used together with +.Fl v , +per-queue statistics are also shown. +When used together with +.Fl v v , +.Nm +will loop and show updated queue statistics every five seconds, including +measured bandwidth and packets per second. +.It Fl s Cm rules +Show the currently loaded filter rules. +When used together with +.Fl v , +the per-rule statistics (number of evaluations, +packets and bytes) are also shown. +Note that the +.Dq skip step +optimization done automatically by the kernel +will skip evaluation of rules where possible. +Packets passed statefully are counted in the rule that created the state +(even though the rule isn't evaluated more than once for the entire +connection). +.It Fl s Cm Anchors +Show the currently loaded anchors directly attached to the main ruleset. +If +.Fl a Ar anchor +is specified as well, the anchors loaded directly below the given +.Ar anchor +are shown instead. +If +.Fl v +is specified, all anchors attached under the target anchor will be +displayed recursively. +.It Fl s Cm states +Show the contents of the state table. +.It Fl s Cm Sources +Show the contents of the source tracking table. +.It Fl s Cm info +Show filter information (statistics and counters). +When used together with +.Fl v , +source tracking statistics are also shown. +.It Fl s Cm labels +Show per-rule statistics (label, evaluations, packets total, bytes total, +packets in, bytes in, packets out, bytes out, state creations) of +filter rules with labels, useful for accounting. +.It Fl s Cm timeouts +Show the current global timeouts. +.It Fl s Cm memory +Show the current pool memory hard limits. +.It Fl s Cm Tables +Show the list of tables. +.It Fl s Cm osfp +Show the list of operating system fingerprints. +.It Fl s Cm Interfaces +Show the list of interfaces and interface drivers available to PF. +When used together with +.Fl v , +it additionally lists which interfaces have skip rules activated. +When used together with +.Fl vv , +interface statistics are also shown. +.Fl i +can be used to select an interface or a group of interfaces. +.It Fl s Cm all +Show all of the above, except for the lists of interfaces and operating +system fingerprints. +.El +.It Fl T Ar command Op Ar address ... +Specify the +.Ar command +(may be abbreviated) to apply to the table. +Commands include: +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Fl T Cm kill +Kill a table. +.It Fl T Cm flush +Flush all addresses of a table. +.It Fl T Cm add +Add one or more addresses in a table. +Automatically create a nonexisting table. +.It Fl T Cm delete +Delete one or more addresses from a table. +.It Fl T Cm expire Ar number +Delete addresses which had their statistics cleared more than +.Ar number +seconds ago. +For entries which have never had their statistics cleared, +.Ar number +refers to the time they were added to the table. +.It Fl T Cm replace +Replace the addresses of the table. +Automatically create a nonexisting table. +.It Fl T Cm show +Show the content (addresses) of a table. +.It Fl T Cm test +Test if the given addresses match a table. +.It Fl T Cm zero +Clear all the statistics of a table. +.It Fl T Cm load +Load only the table definitions from +.Xr pf.conf 5 . +This is used in conjunction with the +.Fl f +flag, as in: +.Bd -literal -offset indent +# pfctl -Tl -f pf.conf +.Ed +.El +.Pp +For the +.Cm add , +.Cm delete , +.Cm replace , +and +.Cm test +commands, the list of addresses can be specified either directly on the command +line and/or in an unformatted text file, using the +.Fl f +flag. +Comments starting with a +.Sq # +are allowed in the text file. +With these commands, the +.Fl v +flag can also be used once or twice, in which case +.Nm +will print the +detailed result of the operation for each individual address, prefixed by +one of the following letters: +.Pp +.Bl -tag -width XXX -compact +.It A +The address/network has been added. +.It C +The address/network has been changed (negated). +.It D +The address/network has been deleted. +.It M +The address matches +.Po +.Cm test +operation only +.Pc . +.It X +The address/network is duplicated and therefore ignored. +.It Y +The address/network cannot be added/deleted due to conflicting +.Sq \&! +attributes. +.It Z +The address/network has been cleared (statistics). +.El +.Pp +Each table can maintain a set of counters that can be retrieved using the +.Fl v +flag of +.Nm . +For example, the following commands define a wide open firewall which will keep +track of packets going to or coming from the +.Ox +FTP server. +The following commands configure the firewall and send 10 pings to the FTP +server: +.Bd -literal -offset indent +# printf "table counters { ftp.openbsd.org }\en \e + pass out to \en" | pfctl -f- +# ping -qc10 ftp.openbsd.org +.Ed +.Pp +We can now use the table +.Cm show +command to output, for each address and packet direction, the number of packets +and bytes that are being passed or blocked by rules referencing the table. +The time at which the current accounting started is also shown with the +.Dq Cleared +line. +.Bd -literal -offset indent +# pfctl -t test -vTshow + 129.128.5.191 + Cleared: Thu Feb 13 18:55:18 2003 + In/Block: [ Packets: 0 Bytes: 0 ] + In/Pass: [ Packets: 10 Bytes: 840 ] + Out/Block: [ Packets: 0 Bytes: 0 ] + Out/Pass: [ Packets: 10 Bytes: 840 ] +.Ed +.Pp +Similarly, it is possible to view global information about the tables +by using the +.Fl v +modifier twice and the +.Fl s +.Cm Tables +command. +This will display the number of addresses on each table, +the number of rules which reference the table, and the global +packet statistics for the whole table: +.Bd -literal -offset indent +# pfctl -vvsTables +--a-r-C test + Addresses: 1 + Cleared: Thu Feb 13 18:55:18 2003 + References: [ Anchors: 0 Rules: 1 ] + Evaluations: [ NoMatch: 3496 Match: 1 ] + In/Block: [ Packets: 0 Bytes: 0 ] + In/Pass: [ Packets: 10 Bytes: 840 ] + In/XPass: [ Packets: 0 Bytes: 0 ] + Out/Block: [ Packets: 0 Bytes: 0 ] + Out/Pass: [ Packets: 10 Bytes: 840 ] + Out/XPass: [ Packets: 0 Bytes: 0 ] +.Ed +.Pp +As we can see here, only one packet \- the initial ping request \- matched the +table, but all packets passing as the result of the state are correctly +accounted for. +Reloading the table(s) or ruleset will not affect packet accounting in any way. +The two +.Dq XPass +counters are incremented instead of the +.Dq Pass +counters when a +.Dq stateful +packet is passed but doesn't match the table anymore. +This will happen in our example if someone flushes the table while the +.Xr ping 8 +command is running. +.Pp +When used with a single +.Fl v , +.Nm +will only display the first line containing the table flags and name. +The flags are defined as follows: +.Pp +.Bl -tag -width XXX -compact +.It c +For constant tables, which cannot be altered outside +.Xr pf.conf 5 . +.It p +For persistent tables, which don't get automatically killed when no rules +refer to them. +.It a +For tables which are part of the +.Em active +tableset. +Tables without this flag do not really exist, cannot contain addresses, and are +only listed if the +.Fl g +flag is given. +.It i +For tables which are part of the +.Em inactive +tableset. +This flag can only be witnessed briefly during the loading of +.Xr pf.conf 5 . +.It r +For tables which are referenced (used) by rules. +.It h +This flag is set when a table in the main ruleset is hidden by one or more +tables of the same name from anchors attached below it. +.It C +This flag is set when per-address counters are enabled on the table. +.El +.It Fl t Ar table +Specify the name of the table. +.It Fl v +Produce more verbose output. +A second use of +.Fl v +will produce even more verbose output including ruleset warnings. +See the previous section for its effect on table commands. +.It Fl x Ar level +Set the debug +.Ar level +(may be abbreviated) to one of the following: +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Fl x Cm none +Don't generate debug messages. +.It Fl x Cm urgent +Generate debug messages only for serious errors. +.It Fl x Cm misc +Generate debug messages for various errors. +.It Fl x Cm loud +Generate debug messages for common conditions. +.El +.It Fl z +Clear per-rule statistics. +.El +.Sh FILES +.Bl -tag -width "/etc/pf.conf" -compact +.It Pa /etc/pf.conf +Packet filter rules file. +.It Pa /etc/pf.os +Passive operating system fingerprint database. +.El +.Sh SEE ALSO +.Xr pf 4 , +.Xr pf.conf 5 , +.Xr pf.os 5 , +.Xr rc.conf 5 , +.Xr services 5 , +.Xr sysctl.conf 5 , +.Xr authpf 8 , +.Xr ftp-proxy 8 , +.Xr rc 8 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +program and the +.Xr pf 4 +filter mechanism first appeared in +.Ox 3.0 . diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c new file mode 100644 index 0000000000000..90a2bb5b7c7dd --- /dev/null +++ b/sbin/pfctl/pfctl.c @@ -0,0 +1,2391 @@ +/* $OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002,2003 Henning Brauer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +void usage(void); +int pfctl_enable(int, int); +int pfctl_disable(int, int); +int pfctl_clear_stats(int, int); +int pfctl_clear_interface_flags(int, int); +int pfctl_clear_rules(int, int, char *); +int pfctl_clear_nat(int, int, char *); +int pfctl_clear_altq(int, int); +int pfctl_clear_src_nodes(int, int); +int pfctl_clear_states(int, const char *, int); +void pfctl_addrprefix(char *, struct pf_addr *); +int pfctl_kill_src_nodes(int, const char *, int); +int pfctl_net_kill_states(int, const char *, int); +int pfctl_label_kill_states(int, const char *, int); +int pfctl_id_kill_states(int, const char *, int); +void pfctl_init_options(struct pfctl *); +int pfctl_load_options(struct pfctl *); +int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int); +int pfctl_load_timeout(struct pfctl *, unsigned int, unsigned int); +int pfctl_load_debug(struct pfctl *, unsigned int); +int pfctl_load_logif(struct pfctl *, char *); +int pfctl_load_hostid(struct pfctl *, unsigned int); +int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int, + char *); +void pfctl_print_rule_counters(struct pf_rule *, int); +int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int); +int pfctl_show_nat(int, int, char *); +int pfctl_show_src_nodes(int, int); +int pfctl_show_states(int, const char *, int); +int pfctl_show_status(int, int); +int pfctl_show_timeouts(int, int); +int pfctl_show_limits(int, int); +void pfctl_debug(int, u_int32_t, int); +int pfctl_test_altqsupport(int, int); +int pfctl_show_anchors(int, int, char *); +int pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *); +int pfctl_load_ruleset(struct pfctl *, char *, + struct pf_ruleset *, int, int); +int pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int); +const char *pfctl_lookup_option(char *, const char **); + +struct pf_anchor_global pf_anchors; +struct pf_anchor pf_main_anchor; + +const char *clearopt; +char *rulesopt; +const char *showopt; +const char *debugopt; +char *anchoropt; +const char *optiopt = NULL; +char *pf_device = "/dev/pf"; +char *ifaceopt; +char *tableopt; +const char *tblcmdopt; +int src_node_killers; +char *src_node_kill[2]; +int state_killers; +char *state_kill[2]; +int loadopt; +int altqsupport; + +int dev = -1; +int first_title = 1; +int labels = 0; + +#define INDENT(d, o) do { \ + if (o) { \ + int i; \ + for (i=0; i < d; i++) \ + printf(" "); \ + } \ + } while (0); \ + + +static const struct { + const char *name; + int index; +} pf_limits[] = { + { "states", PF_LIMIT_STATES }, + { "src-nodes", PF_LIMIT_SRC_NODES }, + { "frags", PF_LIMIT_FRAGS }, + { "table-entries", PF_LIMIT_TABLE_ENTRIES }, + { NULL, 0 } +}; + +struct pf_hint { + const char *name; + int timeout; +}; +static const struct pf_hint pf_hint_normal[] = { + { "tcp.first", 2 * 60 }, + { "tcp.opening", 30 }, + { "tcp.established", 24 * 60 * 60 }, + { "tcp.closing", 15 * 60 }, + { "tcp.finwait", 45 }, + { "tcp.closed", 90 }, + { "tcp.tsdiff", 30 }, + { NULL, 0 } +}; +static const struct pf_hint pf_hint_satellite[] = { + { "tcp.first", 3 * 60 }, + { "tcp.opening", 30 + 5 }, + { "tcp.established", 24 * 60 * 60 }, + { "tcp.closing", 15 * 60 + 5 }, + { "tcp.finwait", 45 + 5 }, + { "tcp.closed", 90 + 5 }, + { "tcp.tsdiff", 60 }, + { NULL, 0 } +}; +static const struct pf_hint pf_hint_conservative[] = { + { "tcp.first", 60 * 60 }, + { "tcp.opening", 15 * 60 }, + { "tcp.established", 5 * 24 * 60 * 60 }, + { "tcp.closing", 60 * 60 }, + { "tcp.finwait", 10 * 60 }, + { "tcp.closed", 3 * 60 }, + { "tcp.tsdiff", 60 }, + { NULL, 0 } +}; +static const struct pf_hint pf_hint_aggressive[] = { + { "tcp.first", 30 }, + { "tcp.opening", 5 }, + { "tcp.established", 5 * 60 * 60 }, + { "tcp.closing", 60 }, + { "tcp.finwait", 30 }, + { "tcp.closed", 30 }, + { "tcp.tsdiff", 10 }, + { NULL, 0 } +}; + +static const struct { + const char *name; + const struct pf_hint *hint; +} pf_hints[] = { + { "normal", pf_hint_normal }, + { "satellite", pf_hint_satellite }, + { "high-latency", pf_hint_satellite }, + { "conservative", pf_hint_conservative }, + { "aggressive", pf_hint_aggressive }, + { NULL, NULL } +}; + +static const char *clearopt_list[] = { + "nat", "queue", "rules", "Sources", + "states", "info", "Tables", "osfp", "all", NULL +}; + +static const char *showopt_list[] = { + "nat", "queue", "rules", "Anchors", "Sources", "states", "info", + "Interfaces", "labels", "timeouts", "memory", "Tables", "osfp", + "all", NULL +}; + +static const char *tblcmdopt_list[] = { + "kill", "flush", "add", "delete", "load", "replace", "show", + "test", "zero", "expire", NULL +}; + +static const char *debugopt_list[] = { + "none", "urgent", "misc", "loud", NULL +}; + +static const char *optiopt_list[] = { + "none", "basic", "profile", NULL +}; + +void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-AdeghmNnOPqRrvz] ", __progname); + fprintf(stderr, "[-a anchor] [-D macro=value] [-F modifier]\n"); + fprintf(stderr, "\t[-f file] [-i interface] [-K host | network]\n"); + fprintf(stderr, "\t[-k host | network | label | id] "); + fprintf(stderr, "[-o level] [-p device]\n"); + fprintf(stderr, "\t[-s modifier] "); + fprintf(stderr, "[-t table -T command [address ...]] [-x level]\n"); + exit(1); +} + +int +pfctl_enable(int dev, int opts) +{ + if (ioctl(dev, DIOCSTART)) { + if (errno == EEXIST) + errx(1, "pf already enabled"); +#ifdef __FreeBSD__ + else if (errno == ESRCH) + errx(1, "pfil registeration failed"); +#endif + else + err(1, "DIOCSTART"); + } + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "pf enabled\n"); + + if (altqsupport && ioctl(dev, DIOCSTARTALTQ)) + if (errno != EEXIST) + err(1, "DIOCSTARTALTQ"); + + return (0); +} + +int +pfctl_disable(int dev, int opts) +{ + if (ioctl(dev, DIOCSTOP)) { + if (errno == ENOENT) + errx(1, "pf not enabled"); + else + err(1, "DIOCSTOP"); + } + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "pf disabled\n"); + + if (altqsupport && ioctl(dev, DIOCSTOPALTQ)) + if (errno != ENOENT) + err(1, "DIOCSTOPALTQ"); + + return (0); +} + +int +pfctl_clear_stats(int dev, int opts) +{ + if (ioctl(dev, DIOCCLRSTATUS)) + err(1, "DIOCCLRSTATUS"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "pf: statistics cleared\n"); + return (0); +} + +int +pfctl_clear_interface_flags(int dev, int opts) +{ + struct pfioc_iface pi; + + if ((opts & PF_OPT_NOACTION) == 0) { + bzero(&pi, sizeof(pi)); + pi.pfiio_flags = PFI_IFLAG_SKIP; + + if (ioctl(dev, DIOCCLRIFFLAG, &pi)) + err(1, "DIOCCLRIFFLAG"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "pf: interface flags reset\n"); + } + return (0); +} + +int +pfctl_clear_rules(int dev, int opts, char *anchorname) +{ + struct pfr_buffer t; + + memset(&t, 0, sizeof(t)); + t.pfrb_type = PFRB_TRANS; + if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname) || + pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname) || + pfctl_trans(dev, &t, DIOCXBEGIN, 0) || + pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) + err(1, "pfctl_clear_rules"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "rules cleared\n"); + return (0); +} + +int +pfctl_clear_nat(int dev, int opts, char *anchorname) +{ + struct pfr_buffer t; + + memset(&t, 0, sizeof(t)); + t.pfrb_type = PFRB_TRANS; + if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname) || + pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname) || + pfctl_add_trans(&t, PF_RULESET_RDR, anchorname) || + pfctl_trans(dev, &t, DIOCXBEGIN, 0) || + pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) + err(1, "pfctl_clear_nat"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "nat cleared\n"); + return (0); +} + +int +pfctl_clear_altq(int dev, int opts) +{ + struct pfr_buffer t; + + if (!altqsupport) + return (-1); + memset(&t, 0, sizeof(t)); + t.pfrb_type = PFRB_TRANS; + if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") || + pfctl_trans(dev, &t, DIOCXBEGIN, 0) || + pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) + err(1, "pfctl_clear_altq"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "altq cleared\n"); + return (0); +} + +int +pfctl_clear_src_nodes(int dev, int opts) +{ + if (ioctl(dev, DIOCCLRSRCNODES)) + err(1, "DIOCCLRSRCNODES"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "source tracking entries cleared\n"); + return (0); +} + +int +pfctl_clear_states(int dev, const char *iface, int opts) +{ + struct pfioc_state_kill psk; + + memset(&psk, 0, sizeof(psk)); + if (iface != NULL && strlcpy(psk.psk_ifname, iface, + sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) + errx(1, "invalid interface: %s", iface); + + if (ioctl(dev, DIOCCLRSTATES, &psk)) + err(1, "DIOCCLRSTATES"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "%d states cleared\n", psk.psk_killed); + return (0); +} + +void +pfctl_addrprefix(char *addr, struct pf_addr *mask) +{ + char *p; + const char *errstr; + int prefix, ret_ga, q, r; + struct addrinfo hints, *res; + + if ((p = strchr(addr, '/')) == NULL) + return; + + *p++ = '\0'; + prefix = strtonum(p, 0, 128, &errstr); + if (errstr) + errx(1, "prefix is %s: %s", errstr, p); + + bzero(&hints, sizeof(hints)); + /* prefix only with numeric addresses */ + hints.ai_flags |= AI_NUMERICHOST; + + if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) { + errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); + /* NOTREACHED */ + } + + if (res->ai_family == AF_INET && prefix > 32) + errx(1, "prefix too long for AF_INET"); + else if (res->ai_family == AF_INET6 && prefix > 128) + errx(1, "prefix too long for AF_INET6"); + + q = prefix >> 3; + r = prefix & 7; + switch (res->ai_family) { + case AF_INET: + bzero(&mask->v4, sizeof(mask->v4)); + mask->v4.s_addr = htonl((u_int32_t) + (0xffffffffffULL << (32 - prefix))); + break; + case AF_INET6: + bzero(&mask->v6, sizeof(mask->v6)); + if (q > 0) + memset((void *)&mask->v6, 0xff, q); + if (r > 0) + *((u_char *)&mask->v6 + q) = + (0xff00 >> r) & 0xff; + break; + } + freeaddrinfo(res); +} + +int +pfctl_kill_src_nodes(int dev, const char *iface, int opts) +{ + struct pfioc_src_node_kill psnk; + struct addrinfo *res[2], *resp[2]; + struct sockaddr last_src, last_dst; + int killed, sources, dests; + int ret_ga; + + killed = sources = dests = 0; + + memset(&psnk, 0, sizeof(psnk)); + memset(&psnk.psnk_src.addr.v.a.mask, 0xff, + sizeof(psnk.psnk_src.addr.v.a.mask)); + memset(&last_src, 0xff, sizeof(last_src)); + memset(&last_dst, 0xff, sizeof(last_dst)); + + pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask); + + if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) { + errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); + /* NOTREACHED */ + } + for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { + if (resp[0]->ai_addr == NULL) + continue; + /* We get lots of duplicates. Catch the easy ones */ + if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) + continue; + last_src = *(struct sockaddr *)resp[0]->ai_addr; + + psnk.psnk_af = resp[0]->ai_family; + sources++; + + if (psnk.psnk_af == AF_INET) + psnk.psnk_src.addr.v.a.addr.v4 = + ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; + else if (psnk.psnk_af == AF_INET6) + psnk.psnk_src.addr.v.a.addr.v6 = + ((struct sockaddr_in6 *)resp[0]->ai_addr)-> + sin6_addr; + else + errx(1, "Unknown address family %d", psnk.psnk_af); + + if (src_node_killers > 1) { + dests = 0; + memset(&psnk.psnk_dst.addr.v.a.mask, 0xff, + sizeof(psnk.psnk_dst.addr.v.a.mask)); + memset(&last_dst, 0xff, sizeof(last_dst)); + pfctl_addrprefix(src_node_kill[1], + &psnk.psnk_dst.addr.v.a.mask); + if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL, + &res[1]))) { + errx(1, "getaddrinfo: %s", + gai_strerror(ret_ga)); + /* NOTREACHED */ + } + for (resp[1] = res[1]; resp[1]; + resp[1] = resp[1]->ai_next) { + if (resp[1]->ai_addr == NULL) + continue; + if (psnk.psnk_af != resp[1]->ai_family) + continue; + + if (memcmp(&last_dst, resp[1]->ai_addr, + sizeof(last_dst)) == 0) + continue; + last_dst = *(struct sockaddr *)resp[1]->ai_addr; + + dests++; + + if (psnk.psnk_af == AF_INET) + psnk.psnk_dst.addr.v.a.addr.v4 = + ((struct sockaddr_in *)resp[1]-> + ai_addr)->sin_addr; + else if (psnk.psnk_af == AF_INET6) + psnk.psnk_dst.addr.v.a.addr.v6 = + ((struct sockaddr_in6 *)resp[1]-> + ai_addr)->sin6_addr; + else + errx(1, "Unknown address family %d", + psnk.psnk_af); + + if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) + err(1, "DIOCKILLSRCNODES"); + killed += psnk.psnk_killed; + } + freeaddrinfo(res[1]); + } else { + if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) + err(1, "DIOCKILLSRCNODES"); + killed += psnk.psnk_killed; + } + } + + freeaddrinfo(res[0]); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d src nodes from %d sources and %d " + "destinations\n", killed, sources, dests); + return (0); +} + +int +pfctl_net_kill_states(int dev, const char *iface, int opts) +{ + struct pfioc_state_kill psk; + struct addrinfo *res[2], *resp[2]; + struct sockaddr last_src, last_dst; + int killed, sources, dests; + int ret_ga; + + killed = sources = dests = 0; + + memset(&psk, 0, sizeof(psk)); + memset(&psk.psk_src.addr.v.a.mask, 0xff, + sizeof(psk.psk_src.addr.v.a.mask)); + memset(&last_src, 0xff, sizeof(last_src)); + memset(&last_dst, 0xff, sizeof(last_dst)); + if (iface != NULL && strlcpy(psk.psk_ifname, iface, + sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) + errx(1, "invalid interface: %s", iface); + + pfctl_addrprefix(state_kill[0], &psk.psk_src.addr.v.a.mask); + + if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) { + errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); + /* NOTREACHED */ + } + for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { + if (resp[0]->ai_addr == NULL) + continue; + /* We get lots of duplicates. Catch the easy ones */ + if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) + continue; + last_src = *(struct sockaddr *)resp[0]->ai_addr; + + psk.psk_af = resp[0]->ai_family; + sources++; + + if (psk.psk_af == AF_INET) + psk.psk_src.addr.v.a.addr.v4 = + ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; + else if (psk.psk_af == AF_INET6) + psk.psk_src.addr.v.a.addr.v6 = + ((struct sockaddr_in6 *)resp[0]->ai_addr)-> + sin6_addr; + else + errx(1, "Unknown address family %d", psk.psk_af); + + if (state_killers > 1) { + dests = 0; + memset(&psk.psk_dst.addr.v.a.mask, 0xff, + sizeof(psk.psk_dst.addr.v.a.mask)); + memset(&last_dst, 0xff, sizeof(last_dst)); + pfctl_addrprefix(state_kill[1], + &psk.psk_dst.addr.v.a.mask); + if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, + &res[1]))) { + errx(1, "getaddrinfo: %s", + gai_strerror(ret_ga)); + /* NOTREACHED */ + } + for (resp[1] = res[1]; resp[1]; + resp[1] = resp[1]->ai_next) { + if (resp[1]->ai_addr == NULL) + continue; + if (psk.psk_af != resp[1]->ai_family) + continue; + + if (memcmp(&last_dst, resp[1]->ai_addr, + sizeof(last_dst)) == 0) + continue; + last_dst = *(struct sockaddr *)resp[1]->ai_addr; + + dests++; + + if (psk.psk_af == AF_INET) + psk.psk_dst.addr.v.a.addr.v4 = + ((struct sockaddr_in *)resp[1]-> + ai_addr)->sin_addr; + else if (psk.psk_af == AF_INET6) + psk.psk_dst.addr.v.a.addr.v6 = + ((struct sockaddr_in6 *)resp[1]-> + ai_addr)->sin6_addr; + else + errx(1, "Unknown address family %d", + psk.psk_af); + + if (ioctl(dev, DIOCKILLSTATES, &psk)) + err(1, "DIOCKILLSTATES"); + killed += psk.psk_killed; + } + freeaddrinfo(res[1]); + } else { + if (ioctl(dev, DIOCKILLSTATES, &psk)) + err(1, "DIOCKILLSTATES"); + killed += psk.psk_killed; + } + } + + freeaddrinfo(res[0]); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d states from %d sources and %d " + "destinations\n", killed, sources, dests); + return (0); +} + +int +pfctl_label_kill_states(int dev, const char *iface, int opts) +{ + struct pfioc_state_kill psk; + + if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { + warnx("no label specified"); + usage(); + } + memset(&psk, 0, sizeof(psk)); + if (iface != NULL && strlcpy(psk.psk_ifname, iface, + sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) + errx(1, "invalid interface: %s", iface); + + if (strlcpy(psk.psk_label, state_kill[1], sizeof(psk.psk_label)) >= + sizeof(psk.psk_label)) + errx(1, "label too long: %s", state_kill[1]); + + if (ioctl(dev, DIOCKILLSTATES, &psk)) + err(1, "DIOCKILLSTATES"); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d states\n", psk.psk_killed); + + return (0); +} + +int +pfctl_id_kill_states(int dev, const char *iface, int opts) +{ + struct pfioc_state_kill psk; + + if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { + warnx("no id specified"); + usage(); + } + + memset(&psk, 0, sizeof(psk)); + if ((sscanf(state_kill[1], "%jx/%x", + &psk.psk_pfcmp.id, &psk.psk_pfcmp.creatorid)) == 2) + HTONL(psk.psk_pfcmp.creatorid); + else if ((sscanf(state_kill[1], "%jx", &psk.psk_pfcmp.id)) == 1) { + psk.psk_pfcmp.creatorid = 0; + } else { + warnx("wrong id format specified"); + usage(); + } + if (psk.psk_pfcmp.id == 0) { + warnx("cannot kill id 0"); + usage(); + } + + psk.psk_pfcmp.id = htobe64(psk.psk_pfcmp.id); + if (ioctl(dev, DIOCKILLSTATES, &psk)) + err(1, "DIOCKILLSTATES"); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d states\n", psk.psk_killed); + + return (0); +} + +int +pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr, + u_int32_t ticket, int r_action, char *anchorname) +{ + struct pfioc_pooladdr pp; + struct pf_pooladdr *pa; + u_int32_t pnr, mpnr; + + memset(&pp, 0, sizeof(pp)); + memcpy(pp.anchor, anchorname, sizeof(pp.anchor)); + pp.r_action = r_action; + pp.r_num = nr; + pp.ticket = ticket; + if (ioctl(dev, DIOCGETADDRS, &pp)) { + warn("DIOCGETADDRS"); + return (-1); + } + mpnr = pp.nr; + TAILQ_INIT(&pool->list); + for (pnr = 0; pnr < mpnr; ++pnr) { + pp.nr = pnr; + if (ioctl(dev, DIOCGETADDR, &pp)) { + warn("DIOCGETADDR"); + return (-1); + } + pa = calloc(1, sizeof(struct pf_pooladdr)); + if (pa == NULL) + err(1, "calloc"); + bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr)); + TAILQ_INSERT_TAIL(&pool->list, pa, entries); + } + + return (0); +} + +void +pfctl_move_pool(struct pf_pool *src, struct pf_pool *dst) +{ + struct pf_pooladdr *pa; + + while ((pa = TAILQ_FIRST(&src->list)) != NULL) { + TAILQ_REMOVE(&src->list, pa, entries); + TAILQ_INSERT_TAIL(&dst->list, pa, entries); + } +} + +void +pfctl_clear_pool(struct pf_pool *pool) +{ + struct pf_pooladdr *pa; + + while ((pa = TAILQ_FIRST(&pool->list)) != NULL) { + TAILQ_REMOVE(&pool->list, pa, entries); + free(pa); + } +} + +void +pfctl_print_rule_counters(struct pf_rule *rule, int opts) +{ + if (opts & PF_OPT_DEBUG) { + const char *t[PF_SKIP_COUNT] = { "i", "d", "f", + "p", "sa", "sp", "da", "dp" }; + int i; + + printf(" [ Skip steps: "); + for (i = 0; i < PF_SKIP_COUNT; ++i) { + if (rule->skip[i].nr == rule->nr + 1) + continue; + printf("%s=", t[i]); + if (rule->skip[i].nr == -1) + printf("end "); + else + printf("%u ", rule->skip[i].nr); + } + printf("]\n"); + + printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n", + rule->qname, rule->qid, rule->pqname, rule->pqid); + } + if (opts & PF_OPT_VERBOSE) { + printf(" [ Evaluations: %-8llu Packets: %-8llu " + "Bytes: %-10llu States: %-6u]\n", + (unsigned long long)rule->evaluations, + (unsigned long long)(rule->packets[0] + + rule->packets[1]), + (unsigned long long)(rule->bytes[0] + + rule->bytes[1]), rule->states_cur); + if (!(opts & PF_OPT_DEBUG)) + printf(" [ Inserted: uid %u pid %u " + "State Creations: %-6u]\n", + (unsigned)rule->cuid, (unsigned)rule->cpid, + rule->states_tot); + } +} + +void +pfctl_print_title(char *title) +{ + if (!first_title) + printf("\n"); + first_title = 0; + printf("%s\n", title); +} + +int +pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, + char *anchorname, int depth) +{ + struct pfioc_rule pr; + u_int32_t nr, mnr, header = 0; + int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG); + int numeric = opts & PF_OPT_NUMERIC; + int len = strlen(path); + int brace; + char *p; + + if (path[0]) + snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); + else + snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname); + + memset(&pr, 0, sizeof(pr)); + memcpy(pr.anchor, path, sizeof(pr.anchor)); + if (opts & PF_OPT_SHOWALL) { + pr.rule.action = PF_PASS; + if (ioctl(dev, DIOCGETRULES, &pr)) { + warn("DIOCGETRULES"); + goto error; + } + header++; + } + pr.rule.action = PF_SCRUB; + if (ioctl(dev, DIOCGETRULES, &pr)) { + warn("DIOCGETRULES"); + goto error; + } + if (opts & PF_OPT_SHOWALL) { + if (format == PFCTL_SHOW_RULES && (pr.nr > 0 || header)) + pfctl_print_title("FILTER RULES:"); + else if (format == PFCTL_SHOW_LABELS && labels) + pfctl_print_title("LABEL COUNTERS:"); + } + mnr = pr.nr; + if (opts & PF_OPT_CLRRULECTRS) + pr.action = PF_GET_CLR_CNTR; + + for (nr = 0; nr < mnr; ++nr) { + pr.nr = nr; + if (ioctl(dev, DIOCGETRULE, &pr)) { + warn("DIOCGETRULE"); + goto error; + } + + if (pfctl_get_pool(dev, &pr.rule.rpool, + nr, pr.ticket, PF_SCRUB, path) != 0) + goto error; + + switch (format) { + case PFCTL_SHOW_LABELS: + break; + case PFCTL_SHOW_RULES: + if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) + labels = 1; + print_rule(&pr.rule, pr.anchor_call, rule_numbers, numeric); + printf("\n"); + pfctl_print_rule_counters(&pr.rule, opts); + break; + case PFCTL_SHOW_NOTHING: + break; + } + pfctl_clear_pool(&pr.rule.rpool); + } + pr.rule.action = PF_PASS; + if (ioctl(dev, DIOCGETRULES, &pr)) { + warn("DIOCGETRULES"); + goto error; + } + mnr = pr.nr; + for (nr = 0; nr < mnr; ++nr) { + pr.nr = nr; + if (ioctl(dev, DIOCGETRULE, &pr)) { + warn("DIOCGETRULE"); + goto error; + } + + if (pfctl_get_pool(dev, &pr.rule.rpool, + nr, pr.ticket, PF_PASS, path) != 0) + goto error; + + switch (format) { + case PFCTL_SHOW_LABELS: + if (pr.rule.label[0]) { + printf("%s %llu %llu %llu %llu" + " %llu %llu %llu %llu\n", + pr.rule.label, + (unsigned long long)pr.rule.evaluations, + (unsigned long long)(pr.rule.packets[0] + + pr.rule.packets[1]), + (unsigned long long)(pr.rule.bytes[0] + + pr.rule.bytes[1]), + (unsigned long long)pr.rule.packets[0], + (unsigned long long)pr.rule.bytes[0], + (unsigned long long)pr.rule.packets[1], + (unsigned long long)pr.rule.bytes[1], + (unsigned long long)pr.rule.states_tot); + } + break; + case PFCTL_SHOW_RULES: + brace = 0; + if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) + labels = 1; + INDENT(depth, !(opts & PF_OPT_VERBOSE)); + if (pr.anchor_call[0] && + ((((p = strrchr(pr.anchor_call, '_')) != NULL) && + ((void *)p == (void *)pr.anchor_call || + *(--p) == '/')) || (opts & PF_OPT_RECURSE))) { + brace++; + if ((p = strrchr(pr.anchor_call, '/')) != + NULL) + p++; + else + p = &pr.anchor_call[0]; + } else + p = &pr.anchor_call[0]; + + print_rule(&pr.rule, p, rule_numbers, numeric); + if (brace) + printf(" {\n"); + else + printf("\n"); + pfctl_print_rule_counters(&pr.rule, opts); + if (brace) { + pfctl_show_rules(dev, path, opts, format, + p, depth + 1); + INDENT(depth, !(opts & PF_OPT_VERBOSE)); + printf("}\n"); + } + break; + case PFCTL_SHOW_NOTHING: + break; + } + pfctl_clear_pool(&pr.rule.rpool); + } + path[len] = '\0'; + return (0); + + error: + path[len] = '\0'; + return (-1); +} + +int +pfctl_show_nat(int dev, int opts, char *anchorname) +{ + struct pfioc_rule pr; + u_int32_t mnr, nr; + static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT }; + int i, dotitle = opts & PF_OPT_SHOWALL; + + memset(&pr, 0, sizeof(pr)); + memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); + for (i = 0; i < 3; i++) { + pr.rule.action = nattype[i]; + if (ioctl(dev, DIOCGETRULES, &pr)) { + warn("DIOCGETRULES"); + return (-1); + } + mnr = pr.nr; + for (nr = 0; nr < mnr; ++nr) { + pr.nr = nr; + if (ioctl(dev, DIOCGETRULE, &pr)) { + warn("DIOCGETRULE"); + return (-1); + } + if (pfctl_get_pool(dev, &pr.rule.rpool, nr, + pr.ticket, nattype[i], anchorname) != 0) + return (-1); + if (dotitle) { + pfctl_print_title("TRANSLATION RULES:"); + dotitle = 0; + } + print_rule(&pr.rule, pr.anchor_call, + opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC); + printf("\n"); + pfctl_print_rule_counters(&pr.rule, opts); + pfctl_clear_pool(&pr.rule.rpool); + } + } + return (0); +} + +int +pfctl_show_src_nodes(int dev, int opts) +{ + struct pfioc_src_nodes psn; + struct pf_src_node *p; + char *inbuf = NULL, *newinbuf = NULL; + unsigned int len = 0; + int i; + + memset(&psn, 0, sizeof(psn)); + for (;;) { + psn.psn_len = len; + if (len) { + newinbuf = realloc(inbuf, len); + if (newinbuf == NULL) + err(1, "realloc"); + psn.psn_buf = inbuf = newinbuf; + } + if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) { + warn("DIOCGETSRCNODES"); + free(inbuf); + return (-1); + } + if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len) + break; + if (len == 0 && psn.psn_len == 0) + goto done; + if (len == 0 && psn.psn_len != 0) + len = psn.psn_len; + if (psn.psn_len == 0) + goto done; /* no src_nodes */ + len *= 2; + } + p = psn.psn_src_nodes; + if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL)) + pfctl_print_title("SOURCE TRACKING NODES:"); + for (i = 0; i < psn.psn_len; i += sizeof(*p)) { + print_src_node(p, opts); + p++; + } +done: + free(inbuf); + return (0); +} + +int +pfctl_show_states(int dev, const char *iface, int opts) +{ + struct pfioc_states ps; + struct pfsync_state *p; + char *inbuf = NULL, *newinbuf = NULL; + unsigned int len = 0; + int i, dotitle = (opts & PF_OPT_SHOWALL); + + memset(&ps, 0, sizeof(ps)); + for (;;) { + ps.ps_len = len; + if (len) { + newinbuf = realloc(inbuf, len); + if (newinbuf == NULL) + err(1, "realloc"); + ps.ps_buf = inbuf = newinbuf; + } + if (ioctl(dev, DIOCGETSTATES, &ps) < 0) { + warn("DIOCGETSTATES"); + free(inbuf); + return (-1); + } + if (ps.ps_len + sizeof(struct pfioc_states) < len) + break; + if (len == 0 && ps.ps_len == 0) + goto done; + if (len == 0 && ps.ps_len != 0) + len = ps.ps_len; + if (ps.ps_len == 0) + goto done; /* no states */ + len *= 2; + } + p = ps.ps_states; + for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) { + if (iface != NULL && strcmp(p->ifname, iface)) + continue; + if (dotitle) { + pfctl_print_title("STATES:"); + dotitle = 0; + } + print_state(p, opts); + } +done: + free(inbuf); + return (0); +} + +int +pfctl_show_status(int dev, int opts) +{ + struct pf_status status; + + if (ioctl(dev, DIOCGETSTATUS, &status)) { + warn("DIOCGETSTATUS"); + return (-1); + } + if (opts & PF_OPT_SHOWALL) + pfctl_print_title("INFO:"); + print_status(&status, opts); + return (0); +} + +int +pfctl_show_timeouts(int dev, int opts) +{ + struct pfioc_tm pt; + int i; + + if (opts & PF_OPT_SHOWALL) + pfctl_print_title("TIMEOUTS:"); + memset(&pt, 0, sizeof(pt)); + for (i = 0; pf_timeouts[i].name; i++) { + pt.timeout = pf_timeouts[i].timeout; + if (ioctl(dev, DIOCGETTIMEOUT, &pt)) + err(1, "DIOCGETTIMEOUT"); + printf("%-20s %10d", pf_timeouts[i].name, pt.seconds); + if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START && + pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END) + printf(" states"); + else + printf("s"); + printf("\n"); + } + return (0); + +} + +int +pfctl_show_limits(int dev, int opts) +{ + struct pfioc_limit pl; + int i; + + if (opts & PF_OPT_SHOWALL) + pfctl_print_title("LIMITS:"); + memset(&pl, 0, sizeof(pl)); + for (i = 0; pf_limits[i].name; i++) { + pl.index = pf_limits[i].index; + if (ioctl(dev, DIOCGETLIMIT, &pl)) + err(1, "DIOCGETLIMIT"); + printf("%-13s ", pf_limits[i].name); + if (pl.limit == UINT_MAX) + printf("unlimited\n"); + else + printf("hard limit %8u\n", pl.limit); + } + return (0); +} + +/* callbacks for rule/nat/rdr/addr */ +int +pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af) +{ + struct pf_pooladdr *pa; + + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) + err(1, "DIOCBEGINADDRS"); + } + + pf->paddr.af = af; + TAILQ_FOREACH(pa, &p->list, entries) { + memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr)); + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) + err(1, "DIOCADDADDR"); + } + } + return (0); +} + +int +pfctl_add_rule(struct pfctl *pf, struct pf_rule *r, const char *anchor_call) +{ + u_int8_t rs_num; + struct pf_rule *rule; + struct pf_ruleset *rs; + char *p; + + rs_num = pf_get_ruleset_number(r->action); + if (rs_num == PF_RULESET_MAX) + errx(1, "Invalid rule type %d", r->action); + + rs = &pf->anchor->ruleset; + + if (anchor_call[0] && r->anchor == NULL) { + /* + * Don't make non-brace anchors part of the main anchor pool. + */ + if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL) + err(1, "pfctl_add_rule: calloc"); + + pf_init_ruleset(&r->anchor->ruleset); + r->anchor->ruleset.anchor = r->anchor; + if (strlcpy(r->anchor->path, anchor_call, + sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path)) + errx(1, "pfctl_add_rule: strlcpy"); + if ((p = strrchr(anchor_call, '/')) != NULL) { + if (!strlen(p)) + err(1, "pfctl_add_rule: bad anchor name %s", + anchor_call); + } else + p = (char *)anchor_call; + if (strlcpy(r->anchor->name, p, + sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name)) + errx(1, "pfctl_add_rule: strlcpy"); + } + + if ((rule = calloc(1, sizeof(*rule))) == NULL) + err(1, "calloc"); + bcopy(r, rule, sizeof(*rule)); + TAILQ_INIT(&rule->rpool.list); + pfctl_move_pool(&r->rpool, &rule->rpool); + + TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries); + return (0); +} + +int +pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a) +{ + int osize = pf->trans->pfrb_size; + + if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) { + if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) || + pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) || + pfctl_add_trans(pf->trans, PF_RULESET_RDR, path)) + return (1); + } + if (a == pf->astack[0] && ((altqsupport && + (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) { + if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path)) + return (2); + } + if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) { + if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) || + pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path)) + return (3); + } + if (pf->loadopt & PFCTL_FLAG_TABLE) + if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path)) + return (4); + if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize)) + return (5); + + return (0); +} + +int +pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs, + int rs_num, int depth) +{ + struct pf_rule *r; + int error, len = strlen(path); + int brace = 0; + + pf->anchor = rs->anchor; + + if (path[0]) + snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name); + else + snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name); + + if (depth) { + if (TAILQ_FIRST(rs->rules[rs_num].active.ptr) != NULL) { + brace++; + if (pf->opts & PF_OPT_VERBOSE) + printf(" {\n"); + if ((pf->opts & PF_OPT_NOACTION) == 0 && + (error = pfctl_ruleset_trans(pf, + path, rs->anchor))) { + printf("pfctl_load_rulesets: " + "pfctl_ruleset_trans %d\n", error); + goto error; + } + } else if (pf->opts & PF_OPT_VERBOSE) + printf("\n"); + + } + + if (pf->optimize && rs_num == PF_RULESET_FILTER) + pfctl_optimize_ruleset(pf, rs); + + while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) { + TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries); + if ((error = pfctl_load_rule(pf, path, r, depth))) + goto error; + if (r->anchor) { + if ((error = pfctl_load_ruleset(pf, path, + &r->anchor->ruleset, rs_num, depth + 1))) + goto error; + } else if (pf->opts & PF_OPT_VERBOSE) + printf("\n"); + free(r); + } + if (brace && pf->opts & PF_OPT_VERBOSE) { + INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE)); + printf("}\n"); + } + path[len] = '\0'; + return (0); + + error: + path[len] = '\0'; + return (error); + +} + +int +pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth) +{ + u_int8_t rs_num = pf_get_ruleset_number(r->action); + char *name; + struct pfioc_rule pr; + int len = strlen(path); + + bzero(&pr, sizeof(pr)); + /* set up anchor before adding to path for anchor_call */ + if ((pf->opts & PF_OPT_NOACTION) == 0) + pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path); + if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor)) + errx(1, "pfctl_load_rule: strlcpy"); + + if (r->anchor) { + if (r->anchor->match) { + if (path[0]) + snprintf(&path[len], MAXPATHLEN - len, + "/%s", r->anchor->name); + else + snprintf(&path[len], MAXPATHLEN - len, + "%s", r->anchor->name); + name = path; + } else + name = r->anchor->path; + } else + name = ""; + + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if (pfctl_add_pool(pf, &r->rpool, r->af)) + return (1); + pr.pool_ticket = pf->paddr.ticket; + memcpy(&pr.rule, r, sizeof(pr.rule)); + if (r->anchor && strlcpy(pr.anchor_call, name, + sizeof(pr.anchor_call)) >= sizeof(pr.anchor_call)) + errx(1, "pfctl_load_rule: strlcpy"); + if (ioctl(pf->dev, DIOCADDRULE, &pr)) + err(1, "DIOCADDRULE"); + } + + if (pf->opts & PF_OPT_VERBOSE) { + INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2)); + print_rule(r, r->anchor ? r->anchor->name : "", + pf->opts & PF_OPT_VERBOSE2, + pf->opts & PF_OPT_NUMERIC); + } + path[len] = '\0'; + pfctl_clear_pool(&r->rpool); + return (0); +} + +int +pfctl_add_altq(struct pfctl *pf, struct pf_altq *a) +{ + if (altqsupport && + (loadopt & PFCTL_FLAG_ALTQ) != 0) { + memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq)); + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) { + if (errno == ENXIO) + errx(1, "qtype not configured"); + else if (errno == ENODEV) + errx(1, "%s: driver does not support " + "altq", a->ifname); + else + err(1, "DIOCADDALTQ"); + } + } + pfaltq_store(&pf->paltq->altq); + } + return (0); +} + +int +pfctl_rules(int dev, char *filename, int opts, int optimize, + char *anchorname, struct pfr_buffer *trans) +{ +#define ERR(x) do { warn(x); goto _error; } while(0) +#define ERRX(x) do { warnx(x); goto _error; } while(0) + + struct pfr_buffer *t, buf; + struct pfioc_altq pa; + struct pfctl pf; + struct pf_ruleset *rs; + struct pfr_table trs; + char *path; + int osize; + + RB_INIT(&pf_anchors); + memset(&pf_main_anchor, 0, sizeof(pf_main_anchor)); + pf_init_ruleset(&pf_main_anchor.ruleset); + pf_main_anchor.ruleset.anchor = &pf_main_anchor; + if (trans == NULL) { + bzero(&buf, sizeof(buf)); + buf.pfrb_type = PFRB_TRANS; + t = &buf; + osize = 0; + } else { + t = trans; + osize = t->pfrb_size; + } + + memset(&pa, 0, sizeof(pa)); + memset(&pf, 0, sizeof(pf)); + memset(&trs, 0, sizeof(trs)); + if ((path = calloc(1, MAXPATHLEN)) == NULL) + ERRX("pfctl_rules: calloc"); + if (strlcpy(trs.pfrt_anchor, anchorname, + sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor)) + ERRX("pfctl_rules: strlcpy"); + pf.dev = dev; + pf.opts = opts; + pf.optimize = optimize; + pf.loadopt = loadopt; + + /* non-brace anchor, create without resolving the path */ + if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL) + ERRX("pfctl_rules: calloc"); + rs = &pf.anchor->ruleset; + pf_init_ruleset(rs); + rs->anchor = pf.anchor; + if (strlcpy(pf.anchor->path, anchorname, + sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path)) + errx(1, "pfctl_add_rule: strlcpy"); + if (strlcpy(pf.anchor->name, anchorname, + sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name)) + errx(1, "pfctl_add_rule: strlcpy"); + + + pf.astack[0] = pf.anchor; + pf.asd = 0; + if (anchorname[0]) + pf.loadopt &= ~PFCTL_FLAG_ALTQ; + pf.paltq = &pa; + pf.trans = t; + pfctl_init_options(&pf); + + if ((opts & PF_OPT_NOACTION) == 0) { + /* + * XXX For the time being we need to open transactions for + * the main ruleset before parsing, because tables are still + * loaded at parse time. + */ + if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor)) + ERRX("pfctl_rules"); + if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) + pa.ticket = + pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname); + if (pf.loadopt & PFCTL_FLAG_TABLE) + pf.astack[0]->ruleset.tticket = + pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname); + } + + if (parse_config(filename, &pf) < 0) { + if ((opts & PF_OPT_NOACTION) == 0) + ERRX("Syntax error in config file: " + "pf rules not loaded"); + else + goto _error; + } + + if ((pf.loadopt & PFCTL_FLAG_FILTER && + (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) || + (pf.loadopt & PFCTL_FLAG_NAT && + (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) || + pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) || + pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) || + (pf.loadopt & PFCTL_FLAG_FILTER && + pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) { + if ((opts & PF_OPT_NOACTION) == 0) + ERRX("Unable to load rules into kernel"); + else + goto _error; + } + + if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0)) + if (check_commit_altq(dev, opts) != 0) + ERRX("errors in altq config"); + + /* process "load anchor" directives */ + if (!anchorname[0]) + if (pfctl_load_anchors(dev, &pf, t) == -1) + ERRX("load anchors"); + + if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) { + if (!anchorname[0]) + if (pfctl_load_options(&pf)) + goto _error; + if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) + ERR("DIOCXCOMMIT"); + } + return (0); + +_error: + if (trans == NULL) { /* main ruleset */ + if ((opts & PF_OPT_NOACTION) == 0) + if (pfctl_trans(dev, t, DIOCXROLLBACK, osize)) + err(1, "DIOCXROLLBACK"); + exit(1); + } else { /* sub ruleset */ + return (-1); + } + +#undef ERR +#undef ERRX +} + +FILE * +pfctl_fopen(const char *name, const char *mode) +{ + struct stat st; + FILE *fp; + + fp = fopen(name, mode); + if (fp == NULL) + return (NULL); + if (fstat(fileno(fp), &st)) { + fclose(fp); + return (NULL); + } + if (S_ISDIR(st.st_mode)) { + fclose(fp); + errno = EISDIR; + return (NULL); + } + return (fp); +} + +void +pfctl_init_options(struct pfctl *pf) +{ + + pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; + pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; + pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; + pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; + pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; + pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; + pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; + pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; + pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; + pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; + pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; + pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; + pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; + pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; + pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL; + pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL; + pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL; + pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL; + pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; + pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; + + pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT; + pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT; + pf->limit[PF_LIMIT_SRC_NODES] = PFSNODE_HIWAT; + pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT; + + pf->debug = PF_DEBUG_URGENT; +} + +int +pfctl_load_options(struct pfctl *pf) +{ + int i, error = 0; + + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + /* load limits */ + for (i = 0; i < PF_LIMIT_MAX; i++) { + if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i]) + continue; + if (pfctl_load_limit(pf, i, pf->limit[i])) + error = 1; + } + + /* + * If we've set the limit, but haven't explicitly set adaptive + * timeouts, do it now with a start of 60% and end of 120%. + */ + if (pf->limit_set[PF_LIMIT_STATES] && + !pf->timeout_set[PFTM_ADAPTIVE_START] && + !pf->timeout_set[PFTM_ADAPTIVE_END]) { + pf->timeout[PFTM_ADAPTIVE_START] = + (pf->limit[PF_LIMIT_STATES] / 10) * 6; + pf->timeout_set[PFTM_ADAPTIVE_START] = 1; + pf->timeout[PFTM_ADAPTIVE_END] = + (pf->limit[PF_LIMIT_STATES] / 10) * 12; + pf->timeout_set[PFTM_ADAPTIVE_END] = 1; + } + + /* load timeouts */ + for (i = 0; i < PFTM_MAX; i++) { + if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i]) + continue; + if (pfctl_load_timeout(pf, i, pf->timeout[i])) + error = 1; + } + + /* load debug */ + if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set) + if (pfctl_load_debug(pf, pf->debug)) + error = 1; + + /* load logif */ + if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set) + if (pfctl_load_logif(pf, pf->ifname)) + error = 1; + + /* load hostid */ + if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set) + if (pfctl_load_hostid(pf, pf->hostid)) + error = 1; + + return (error); +} + +int +pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit) +{ + int i; + + + for (i = 0; pf_limits[i].name; i++) { + if (strcasecmp(opt, pf_limits[i].name) == 0) { + pf->limit[pf_limits[i].index] = limit; + pf->limit_set[pf_limits[i].index] = 1; + break; + } + } + if (pf_limits[i].name == NULL) { + warnx("Bad pool name."); + return (1); + } + + if (pf->opts & PF_OPT_VERBOSE) + printf("set limit %s %d\n", opt, limit); + + return (0); +} + +int +pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit) +{ + struct pfioc_limit pl; + + memset(&pl, 0, sizeof(pl)); + pl.index = index; + pl.limit = limit; + if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) { + if (errno == EBUSY) + warnx("Current pool size exceeds requested hard limit"); + else + warnx("DIOCSETLIMIT"); + return (1); + } + return (0); +} + +int +pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) +{ + int i; + + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + for (i = 0; pf_timeouts[i].name; i++) { + if (strcasecmp(opt, pf_timeouts[i].name) == 0) { + pf->timeout[pf_timeouts[i].timeout] = seconds; + pf->timeout_set[pf_timeouts[i].timeout] = 1; + break; + } + } + + if (pf_timeouts[i].name == NULL) { + warnx("Bad timeout name."); + return (1); + } + + + if (pf->opts & PF_OPT_VERBOSE && ! quiet) + printf("set timeout %s %d\n", opt, seconds); + + return (0); +} + +int +pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds) +{ + struct pfioc_tm pt; + + memset(&pt, 0, sizeof(pt)); + pt.timeout = timeout; + pt.seconds = seconds; + if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) { + warnx("DIOCSETTIMEOUT"); + return (1); + } + return (0); +} + +int +pfctl_set_optimization(struct pfctl *pf, const char *opt) +{ + const struct pf_hint *hint; + int i, r; + + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + for (i = 0; pf_hints[i].name; i++) + if (strcasecmp(opt, pf_hints[i].name) == 0) + break; + + hint = pf_hints[i].hint; + if (hint == NULL) { + warnx("invalid state timeouts optimization"); + return (1); + } + + for (i = 0; hint[i].name; i++) + if ((r = pfctl_set_timeout(pf, hint[i].name, + hint[i].timeout, 1))) + return (r); + + if (pf->opts & PF_OPT_VERBOSE) + printf("set optimization %s\n", opt); + + return (0); +} + +int +pfctl_set_logif(struct pfctl *pf, char *ifname) +{ + + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + if (!strcmp(ifname, "none")) { + free(pf->ifname); + pf->ifname = NULL; + } else { + pf->ifname = strdup(ifname); + if (!pf->ifname) + errx(1, "pfctl_set_logif: strdup"); + } + pf->ifname_set = 1; + + if (pf->opts & PF_OPT_VERBOSE) + printf("set loginterface %s\n", ifname); + + return (0); +} + +int +pfctl_load_logif(struct pfctl *pf, char *ifname) +{ + struct pfioc_if pi; + + memset(&pi, 0, sizeof(pi)); + if (ifname && strlcpy(pi.ifname, ifname, + sizeof(pi.ifname)) >= sizeof(pi.ifname)) { + warnx("pfctl_load_logif: strlcpy"); + return (1); + } + if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) { + warnx("DIOCSETSTATUSIF"); + return (1); + } + return (0); +} + +int +pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) +{ + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + HTONL(hostid); + + pf->hostid = hostid; + pf->hostid_set = 1; + + if (pf->opts & PF_OPT_VERBOSE) + printf("set hostid 0x%08x\n", ntohl(hostid)); + + return (0); +} + +int +pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid) +{ + if (ioctl(dev, DIOCSETHOSTID, &hostid)) { + warnx("DIOCSETHOSTID"); + return (1); + } + return (0); +} + +int +pfctl_set_debug(struct pfctl *pf, char *d) +{ + u_int32_t level; + + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + if (!strcmp(d, "none")) + pf->debug = PF_DEBUG_NONE; + else if (!strcmp(d, "urgent")) + pf->debug = PF_DEBUG_URGENT; + else if (!strcmp(d, "misc")) + pf->debug = PF_DEBUG_MISC; + else if (!strcmp(d, "loud")) + pf->debug = PF_DEBUG_NOISY; + else { + warnx("unknown debug level \"%s\"", d); + return (-1); + } + + pf->debug_set = 1; + + if ((pf->opts & PF_OPT_NOACTION) == 0) + if (ioctl(dev, DIOCSETDEBUG, &level)) + err(1, "DIOCSETDEBUG"); + + if (pf->opts & PF_OPT_VERBOSE) + printf("set debug %s\n", d); + + return (0); +} + +int +pfctl_load_debug(struct pfctl *pf, unsigned int level) +{ + if (ioctl(pf->dev, DIOCSETDEBUG, &level)) { + warnx("DIOCSETDEBUG"); + return (1); + } + return (0); +} + +int +pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how) +{ + struct pfioc_iface pi; + + if ((loadopt & PFCTL_FLAG_OPTION) == 0) + return (0); + + bzero(&pi, sizeof(pi)); + + pi.pfiio_flags = flags; + + if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >= + sizeof(pi.pfiio_name)) + errx(1, "pfctl_set_interface_flags: strlcpy"); + + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if (how == 0) { + if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi)) + err(1, "DIOCCLRIFFLAG"); + } else { + if (ioctl(pf->dev, DIOCSETIFFLAG, &pi)) + err(1, "DIOCSETIFFLAG"); + } + } + return (0); +} + +void +pfctl_debug(int dev, u_int32_t level, int opts) +{ + if (ioctl(dev, DIOCSETDEBUG, &level)) + err(1, "DIOCSETDEBUG"); + if ((opts & PF_OPT_QUIET) == 0) { + fprintf(stderr, "debug level set to '"); + switch (level) { + case PF_DEBUG_NONE: + fprintf(stderr, "none"); + break; + case PF_DEBUG_URGENT: + fprintf(stderr, "urgent"); + break; + case PF_DEBUG_MISC: + fprintf(stderr, "misc"); + break; + case PF_DEBUG_NOISY: + fprintf(stderr, "loud"); + break; + default: + fprintf(stderr, ""); + break; + } + fprintf(stderr, "'\n"); + } +} + +int +pfctl_test_altqsupport(int dev, int opts) +{ + struct pfioc_altq pa; + + if (ioctl(dev, DIOCGETALTQS, &pa)) { + if (errno == ENODEV) { + if (!(opts & PF_OPT_QUIET)) + fprintf(stderr, "No ALTQ support in kernel\n" + "ALTQ related functions disabled\n"); + return (0); + } else + err(1, "DIOCGETALTQS"); + } + return (1); +} + +int +pfctl_show_anchors(int dev, int opts, char *anchorname) +{ + struct pfioc_ruleset pr; + u_int32_t mnr, nr; + + memset(&pr, 0, sizeof(pr)); + memcpy(pr.path, anchorname, sizeof(pr.path)); + if (ioctl(dev, DIOCGETRULESETS, &pr)) { + if (errno == EINVAL) + fprintf(stderr, "Anchor '%s' not found.\n", + anchorname); + else + err(1, "DIOCGETRULESETS"); + return (-1); + } + mnr = pr.nr; + for (nr = 0; nr < mnr; ++nr) { + char sub[MAXPATHLEN]; + + pr.nr = nr; + if (ioctl(dev, DIOCGETRULESET, &pr)) + err(1, "DIOCGETRULESET"); + if (!strcmp(pr.name, PF_RESERVED_ANCHOR)) + continue; + sub[0] = 0; + if (pr.path[0]) { + strlcat(sub, pr.path, sizeof(sub)); + strlcat(sub, "/", sizeof(sub)); + } + strlcat(sub, pr.name, sizeof(sub)); + if (sub[0] != '_' || (opts & PF_OPT_VERBOSE)) + printf(" %s\n", sub); + if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub)) + return (-1); + } + return (0); +} + +const char * +pfctl_lookup_option(char *cmd, const char **list) +{ + if (cmd != NULL && *cmd) + for (; *list; list++) + if (!strncmp(cmd, *list, strlen(cmd))) + return (*list); + return (NULL); +} + +int +main(int argc, char *argv[]) +{ + int error = 0; + int ch; + int mode = O_RDONLY; + int opts = 0; + int optimize = PF_OPTIMIZE_BASIC; + char anchorname[MAXPATHLEN]; + char *path; + + if (argc < 2) + usage(); + + while ((ch = getopt(argc, argv, + "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) { + switch (ch) { + case 'a': + anchoropt = optarg; + break; + case 'd': + opts |= PF_OPT_DISABLE; + mode = O_RDWR; + break; + case 'D': + if (pfctl_cmdline_symset(optarg) < 0) + warnx("could not parse macro definition %s", + optarg); + break; + case 'e': + opts |= PF_OPT_ENABLE; + mode = O_RDWR; + break; + case 'q': + opts |= PF_OPT_QUIET; + break; + case 'F': + clearopt = pfctl_lookup_option(optarg, clearopt_list); + if (clearopt == NULL) { + warnx("Unknown flush modifier '%s'", optarg); + usage(); + } + mode = O_RDWR; + break; + case 'i': + ifaceopt = optarg; + break; + case 'k': + if (state_killers >= 2) { + warnx("can only specify -k twice"); + usage(); + /* NOTREACHED */ + } + state_kill[state_killers++] = optarg; + mode = O_RDWR; + break; + case 'K': + if (src_node_killers >= 2) { + warnx("can only specify -K twice"); + usage(); + /* NOTREACHED */ + } + src_node_kill[src_node_killers++] = optarg; + mode = O_RDWR; + break; + case 'm': + opts |= PF_OPT_MERGE; + break; + case 'n': + opts |= PF_OPT_NOACTION; + break; + case 'N': + loadopt |= PFCTL_FLAG_NAT; + break; + case 'r': + opts |= PF_OPT_USEDNS; + break; + case 'f': + rulesopt = optarg; + mode = O_RDWR; + break; + case 'g': + opts |= PF_OPT_DEBUG; + break; + case 'A': + loadopt |= PFCTL_FLAG_ALTQ; + break; + case 'R': + loadopt |= PFCTL_FLAG_FILTER; + break; + case 'o': + optiopt = pfctl_lookup_option(optarg, optiopt_list); + if (optiopt == NULL) { + warnx("Unknown optimization '%s'", optarg); + usage(); + } + opts |= PF_OPT_OPTIMIZE; + break; + case 'O': + loadopt |= PFCTL_FLAG_OPTION; + break; + case 'p': + pf_device = optarg; + break; + case 'P': + opts |= PF_OPT_NUMERIC; + break; + case 's': + showopt = pfctl_lookup_option(optarg, showopt_list); + if (showopt == NULL) { + warnx("Unknown show modifier '%s'", optarg); + usage(); + } + break; + case 't': + tableopt = optarg; + break; + case 'T': + tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list); + if (tblcmdopt == NULL) { + warnx("Unknown table command '%s'", optarg); + usage(); + } + break; + case 'v': + if (opts & PF_OPT_VERBOSE) + opts |= PF_OPT_VERBOSE2; + opts |= PF_OPT_VERBOSE; + break; + case 'x': + debugopt = pfctl_lookup_option(optarg, debugopt_list); + if (debugopt == NULL) { + warnx("Unknown debug level '%s'", optarg); + usage(); + } + mode = O_RDWR; + break; + case 'z': + opts |= PF_OPT_CLRRULECTRS; + mode = O_RDWR; + break; + case 'h': + /* FALLTHROUGH */ + default: + usage(); + /* NOTREACHED */ + } + } + + if (tblcmdopt != NULL) { + argc -= optind; + argv += optind; + ch = *tblcmdopt; + if (ch == 'l') { + loadopt |= PFCTL_FLAG_TABLE; + tblcmdopt = NULL; + } else + mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY; + } else if (argc != optind) { + warnx("unknown command line argument: %s ...", argv[optind]); + usage(); + /* NOTREACHED */ + } + if (loadopt == 0) + loadopt = ~0; + + if ((path = calloc(1, MAXPATHLEN)) == NULL) + errx(1, "pfctl: calloc"); + memset(anchorname, 0, sizeof(anchorname)); + if (anchoropt != NULL) { + int len = strlen(anchoropt); + + if (anchoropt[len - 1] == '*') { + if (len >= 2 && anchoropt[len - 2] == '/') + anchoropt[len - 2] = '\0'; + else + anchoropt[len - 1] = '\0'; + opts |= PF_OPT_RECURSE; + } + if (strlcpy(anchorname, anchoropt, + sizeof(anchorname)) >= sizeof(anchorname)) + errx(1, "anchor name '%s' too long", + anchoropt); + loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE; + } + + if ((opts & PF_OPT_NOACTION) == 0) { + dev = open(pf_device, mode); + if (dev == -1) + err(1, "%s", pf_device); + altqsupport = pfctl_test_altqsupport(dev, opts); + } else { + dev = open(pf_device, O_RDONLY); + if (dev >= 0) + opts |= PF_OPT_DUMMYACTION; + /* turn off options */ + opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE); + clearopt = showopt = debugopt = NULL; +#if defined(__FreeBSD__) && !defined(ENABLE_ALTQ) + altqsupport = 0; +#else + altqsupport = 1; +#endif + } + + if (opts & PF_OPT_DISABLE) + if (pfctl_disable(dev, opts)) + error = 1; + + if (showopt != NULL) { + switch (*showopt) { + case 'A': + pfctl_show_anchors(dev, opts, anchorname); + break; + case 'r': + pfctl_load_fingerprints(dev, opts); + pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, + anchorname, 0); + break; + case 'l': + pfctl_load_fingerprints(dev, opts); + pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, + anchorname, 0); + break; + case 'n': + pfctl_load_fingerprints(dev, opts); + pfctl_show_nat(dev, opts, anchorname); + break; + case 'q': + pfctl_show_altq(dev, ifaceopt, opts, + opts & PF_OPT_VERBOSE2); + break; + case 's': + pfctl_show_states(dev, ifaceopt, opts); + break; + case 'S': + pfctl_show_src_nodes(dev, opts); + break; + case 'i': + pfctl_show_status(dev, opts); + break; + case 't': + pfctl_show_timeouts(dev, opts); + break; + case 'm': + pfctl_show_limits(dev, opts); + break; + case 'a': + opts |= PF_OPT_SHOWALL; + pfctl_load_fingerprints(dev, opts); + + pfctl_show_nat(dev, opts, anchorname); + pfctl_show_rules(dev, path, opts, 0, anchorname, 0); + pfctl_show_altq(dev, ifaceopt, opts, 0); + pfctl_show_states(dev, ifaceopt, opts); + pfctl_show_src_nodes(dev, opts); + pfctl_show_status(dev, opts); + pfctl_show_rules(dev, path, opts, 1, anchorname, 0); + pfctl_show_timeouts(dev, opts); + pfctl_show_limits(dev, opts); + pfctl_show_tables(anchorname, opts); + pfctl_show_fingerprints(opts); + break; + case 'T': + pfctl_show_tables(anchorname, opts); + break; + case 'o': + pfctl_load_fingerprints(dev, opts); + pfctl_show_fingerprints(opts); + break; + case 'I': + pfctl_show_ifaces(ifaceopt, opts); + break; + } + } + + if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL) + pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING, + anchorname, 0); + + if (clearopt != NULL) { + if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) + errx(1, "anchor names beginning with '_' cannot " + "be modified from the command line"); + + switch (*clearopt) { + case 'r': + pfctl_clear_rules(dev, opts, anchorname); + break; + case 'n': + pfctl_clear_nat(dev, opts, anchorname); + break; + case 'q': + pfctl_clear_altq(dev, opts); + break; + case 's': + pfctl_clear_states(dev, ifaceopt, opts); + break; + case 'S': + pfctl_clear_src_nodes(dev, opts); + break; + case 'i': + pfctl_clear_stats(dev, opts); + break; + case 'a': + pfctl_clear_rules(dev, opts, anchorname); + pfctl_clear_nat(dev, opts, anchorname); + pfctl_clear_tables(anchorname, opts); + if (!*anchorname) { + pfctl_clear_altq(dev, opts); + pfctl_clear_states(dev, ifaceopt, opts); + pfctl_clear_src_nodes(dev, opts); + pfctl_clear_stats(dev, opts); + pfctl_clear_fingerprints(dev, opts); + pfctl_clear_interface_flags(dev, opts); + } + break; + case 'o': + pfctl_clear_fingerprints(dev, opts); + break; + case 'T': + pfctl_clear_tables(anchorname, opts); + break; + } + } + if (state_killers) { + if (!strcmp(state_kill[0], "label")) + pfctl_label_kill_states(dev, ifaceopt, opts); + else if (!strcmp(state_kill[0], "id")) + pfctl_id_kill_states(dev, ifaceopt, opts); + else + pfctl_net_kill_states(dev, ifaceopt, opts); + } + + if (src_node_killers) + pfctl_kill_src_nodes(dev, ifaceopt, opts); + + if (tblcmdopt != NULL) { + error = pfctl_command_tables(argc, argv, tableopt, + tblcmdopt, rulesopt, anchorname, opts); + rulesopt = NULL; + } + if (optiopt != NULL) { + switch (*optiopt) { + case 'n': + optimize = 0; + break; + case 'b': + optimize |= PF_OPTIMIZE_BASIC; + break; + case 'o': + case 'p': + optimize |= PF_OPTIMIZE_PROFILE; + break; + } + } + + if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) && + !anchorname[0]) + if (pfctl_clear_interface_flags(dev, opts | PF_OPT_QUIET)) + error = 1; + + if (rulesopt != NULL && !(opts & (PF_OPT_MERGE|PF_OPT_NOACTION)) && + !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION)) + if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) + error = 1; + + if (rulesopt != NULL) { + if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) + errx(1, "anchor names beginning with '_' cannot " + "be modified from the command line"); + if (pfctl_rules(dev, rulesopt, opts, optimize, + anchorname, NULL)) + error = 1; + else if (!(opts & PF_OPT_NOACTION) && + (loadopt & PFCTL_FLAG_TABLE)) + warn_namespace_collision(NULL); + } + + if (opts & PF_OPT_ENABLE) + if (pfctl_enable(dev, opts)) + error = 1; + + if (debugopt != NULL) { + switch (*debugopt) { + case 'n': + pfctl_debug(dev, PF_DEBUG_NONE, opts); + break; + case 'u': + pfctl_debug(dev, PF_DEBUG_URGENT, opts); + break; + case 'm': + pfctl_debug(dev, PF_DEBUG_MISC, opts); + break; + case 'l': + pfctl_debug(dev, PF_DEBUG_NOISY, opts); + break; + } + } + + exit(error); +} diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h new file mode 100644 index 0000000000000..2c69bc2068b0c --- /dev/null +++ b/sbin/pfctl/pfctl.h @@ -0,0 +1,130 @@ +/* $OpenBSD: pfctl.h,v 1.42 2007/12/05 12:01:47 chl Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PFCTL_H_ +#define _PFCTL_H_ + +enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING }; + +enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS, + PFRB_IFACES, PFRB_TRANS, PFRB_MAX }; +struct pfr_buffer { + int pfrb_type; /* type of content, see enum above */ + int pfrb_size; /* number of objects in buffer */ + int pfrb_msize; /* maximum number of objects in buffer */ + void *pfrb_caddr; /* malloc'ated memory area */ +}; +#define PFRB_FOREACH(var, buf) \ + for ((var) = pfr_buf_next((buf), NULL); \ + (var) != NULL; \ + (var) = pfr_buf_next((buf), (var))) + +int pfr_get_fd(void); +int pfr_clr_tables(struct pfr_table *, int *, int); +int pfr_add_tables(struct pfr_table *, int, int *, int); +int pfr_del_tables(struct pfr_table *, int, int *, int); +int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int); +int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); +int pfr_clr_tstats(struct pfr_table *, int, int *, int); +int pfr_clr_addrs(struct pfr_table *, int *, int); +int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); +int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); +int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int *, int *, int *, int); +int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int); +int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int); +int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); +int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, + int *, int, int); +void pfr_buf_clear(struct pfr_buffer *); +int pfr_buf_add(struct pfr_buffer *, const void *); +void *pfr_buf_next(struct pfr_buffer *, const void *); +int pfr_buf_grow(struct pfr_buffer *, int); +int pfr_buf_load(struct pfr_buffer *, char *, int, + int (*)(struct pfr_buffer *, char *, int)); +char *pfr_strerror(int); +int pfi_get_ifaces(const char *, struct pfi_kif *, int *); +int pfi_clr_istats(const char *, int *, int); + +void pfctl_print_title(char *); +int pfctl_clear_tables(const char *, int); +int pfctl_show_tables(const char *, int); +int pfctl_command_tables(int, char *[], char *, const char *, char *, + const char *, int); +int pfctl_show_altq(int, const char *, int, int); +void warn_namespace_collision(const char *); +int pfctl_show_ifaces(const char *, int); +FILE *pfctl_fopen(const char *, const char *); + +#ifdef __FreeBSD__ +extern int altqsupport; +extern int dummynetsupport; +#define HTONL(x) (x) = htonl((__uint32_t)(x)) +#endif + +#ifndef DEFAULT_PRIORITY +#define DEFAULT_PRIORITY 1 +#endif + +#ifndef DEFAULT_QLIMIT +#define DEFAULT_QLIMIT 50 +#endif + +/* + * generalized service curve used for admission control + */ +struct segment { + LIST_ENTRY(segment) _next; + double x, y, d, m; +}; + +extern int loadopt; + +int check_commit_altq(int, int); +void pfaltq_store(struct pf_altq *); +struct pf_altq *pfaltq_lookup(const char *); +char *rate2str(double); + +void print_addr(struct pf_addr_wrap *, sa_family_t, int); +void print_host(struct pf_addr *, u_int16_t p, sa_family_t, int); +void print_seq(struct pfsync_state_peer *); +void print_state(struct pfsync_state *, int); +int unmask(struct pf_addr *, sa_family_t); + +int pfctl_cmdline_symset(char *); +int pfctl_add_trans(struct pfr_buffer *, int, const char *); +u_int32_t + pfctl_get_ticket(struct pfr_buffer *, int, const char *); +int pfctl_trans(int, struct pfr_buffer *, u_long, int); + +#endif /* _PFCTL_H_ */ diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c new file mode 100644 index 0000000000000..40e11d5a9eb99 --- /dev/null +++ b/sbin/pfctl/pfctl_altq.c @@ -0,0 +1,1258 @@ +/* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */ + +/* + * Copyright (c) 2002 + * Sony Computer Science Laboratories Inc. + * Copyright (c) 2002, 2003 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) + +TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs); +LIST_HEAD(gen_sc, segment) rtsc, lssc; + +struct pf_altq *qname_to_pfaltq(const char *, const char *); +u_int32_t qname_to_qid(const char *); + +static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *); +static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); +static int check_commit_cbq(int, int, struct pf_altq *); +static int print_cbq_opts(const struct pf_altq *); + +static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); +static int check_commit_priq(int, int, struct pf_altq *); +static int print_priq_opts(const struct pf_altq *); + +static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *); +static int check_commit_hfsc(int, int, struct pf_altq *); +static int print_hfsc_opts(const struct pf_altq *, + const struct node_queue_opt *); + +static void gsc_add_sc(struct gen_sc *, struct service_curve *); +static int is_gsc_under_sc(struct gen_sc *, + struct service_curve *); +static void gsc_destroy(struct gen_sc *); +static struct segment *gsc_getentry(struct gen_sc *, double); +static int gsc_add_seg(struct gen_sc *, double, double, double, + double); +static double sc_x2y(struct service_curve *, double); + +#ifdef __FreeBSD__ +u_int32_t getifspeed(int, char *); +#else +u_int32_t getifspeed(char *); +#endif +u_long getifmtu(char *); +int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, + u_int32_t); +u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); +void print_hfsc_sc(const char *, u_int, u_int, u_int, + const struct node_hfsc_sc *); + +void +pfaltq_store(struct pf_altq *a) +{ + struct pf_altq *altq; + + if ((altq = malloc(sizeof(*altq))) == NULL) + err(1, "malloc"); + memcpy(altq, a, sizeof(struct pf_altq)); + TAILQ_INSERT_TAIL(&altqs, altq, entries); +} + +struct pf_altq * +pfaltq_lookup(const char *ifname) +{ + struct pf_altq *altq; + + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && + altq->qname[0] == 0) + return (altq); + } + return (NULL); +} + +struct pf_altq * +qname_to_pfaltq(const char *qname, const char *ifname) +{ + struct pf_altq *altq; + + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && + strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) + return (altq); + } + return (NULL); +} + +u_int32_t +qname_to_qid(const char *qname) +{ + struct pf_altq *altq; + + /* + * We guarantee that same named queues on different interfaces + * have the same qid, so we do NOT need to limit matching on + * one interface! + */ + + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) + return (altq->qid); + } + return (0); +} + +void +print_altq(const struct pf_altq *a, unsigned int level, + struct node_queue_bw *bw, struct node_queue_opt *qopts) +{ + if (a->qname[0] != 0) { + print_queue(a, level, bw, 1, qopts); + return; + } + +#ifdef __FreeBSD__ + if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) + printf("INACTIVE "); +#endif + + printf("altq on %s ", a->ifname); + + switch (a->scheduler) { + case ALTQT_CBQ: + if (!print_cbq_opts(a)) + printf("cbq "); + break; + case ALTQT_PRIQ: + if (!print_priq_opts(a)) + printf("priq "); + break; + case ALTQT_HFSC: + if (!print_hfsc_opts(a, qopts)) + printf("hfsc "); + break; + } + + if (bw != NULL && bw->bw_percent > 0) { + if (bw->bw_percent < 100) + printf("bandwidth %u%% ", bw->bw_percent); + } else + printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); + + if (a->qlimit != DEFAULT_QLIMIT) + printf("qlimit %u ", a->qlimit); + printf("tbrsize %u ", a->tbrsize); +} + +void +print_queue(const struct pf_altq *a, unsigned int level, + struct node_queue_bw *bw, int print_interface, + struct node_queue_opt *qopts) +{ + unsigned int i; + +#ifdef __FreeBSD__ + if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) + printf("INACTIVE "); +#endif + printf("queue "); + for (i = 0; i < level; ++i) + printf(" "); + printf("%s ", a->qname); + if (print_interface) + printf("on %s ", a->ifname); + if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { + if (bw != NULL && bw->bw_percent > 0) { + if (bw->bw_percent < 100) + printf("bandwidth %u%% ", bw->bw_percent); + } else + printf("bandwidth %s ", rate2str((double)a->bandwidth)); + } + if (a->priority != DEFAULT_PRIORITY) + printf("priority %u ", a->priority); + if (a->qlimit != DEFAULT_QLIMIT) + printf("qlimit %u ", a->qlimit); + switch (a->scheduler) { + case ALTQT_CBQ: + print_cbq_opts(a); + break; + case ALTQT_PRIQ: + print_priq_opts(a); + break; + case ALTQT_HFSC: + print_hfsc_opts(a, qopts); + break; + } +} + +/* + * eval_pfaltq computes the discipline parameters. + */ +int +eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, + struct node_queue_opt *opts) +{ + u_int rate, size, errors = 0; + + if (bw->bw_absolute > 0) + pa->ifbandwidth = bw->bw_absolute; + else +#ifdef __FreeBSD__ + if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { +#else + if ((rate = getifspeed(pa->ifname)) == 0) { +#endif + fprintf(stderr, "interface %s does not know its bandwidth, " + "please specify an absolute bandwidth\n", + pa->ifname); + errors++; + } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) + pa->ifbandwidth = rate; + + errors += eval_queue_opts(pa, opts, pa->ifbandwidth); + + /* if tbrsize is not specified, use heuristics */ + if (pa->tbrsize == 0) { + rate = pa->ifbandwidth; + if (rate <= 1 * 1000 * 1000) + size = 1; + else if (rate <= 10 * 1000 * 1000) + size = 4; + else if (rate <= 200 * 1000 * 1000) + size = 8; + else + size = 24; + size = size * getifmtu(pa->ifname); + if (size > 0xffff) + size = 0xffff; + pa->tbrsize = size; + } + return (errors); +} + +/* + * check_commit_altq does consistency check for each interface + */ +int +check_commit_altq(int dev, int opts) +{ + struct pf_altq *altq; + int error = 0; + + /* call the discipline check for each interface. */ + TAILQ_FOREACH(altq, &altqs, entries) { + if (altq->qname[0] == 0) { + switch (altq->scheduler) { + case ALTQT_CBQ: + error = check_commit_cbq(dev, opts, altq); + break; + case ALTQT_PRIQ: + error = check_commit_priq(dev, opts, altq); + break; + case ALTQT_HFSC: + error = check_commit_hfsc(dev, opts, altq); + break; + default: + break; + } + } + } + return (error); +} + +/* + * eval_pfqueue computes the queue parameters. + */ +int +eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, + struct node_queue_opt *opts) +{ + /* should be merged with expand_queue */ + struct pf_altq *if_pa, *parent, *altq; + u_int32_t bwsum; + int error = 0; + + /* find the corresponding interface and copy fields used by queues */ + if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) { + fprintf(stderr, "altq not defined on %s\n", pa->ifname); + return (1); + } + pa->scheduler = if_pa->scheduler; + pa->ifbandwidth = if_pa->ifbandwidth; + + if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { + fprintf(stderr, "queue %s already exists on interface %s\n", + pa->qname, pa->ifname); + return (1); + } + pa->qid = qname_to_qid(pa->qname); + + parent = NULL; + if (pa->parent[0] != 0) { + parent = qname_to_pfaltq(pa->parent, pa->ifname); + if (parent == NULL) { + fprintf(stderr, "parent %s not found for %s\n", + pa->parent, pa->qname); + return (1); + } + pa->parent_qid = parent->qid; + } + if (pa->qlimit == 0) + pa->qlimit = DEFAULT_QLIMIT; + + if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { + pa->bandwidth = eval_bwspec(bw, + parent == NULL ? 0 : parent->bandwidth); + + if (pa->bandwidth > pa->ifbandwidth) { + fprintf(stderr, "bandwidth for %s higher than " + "interface\n", pa->qname); + return (1); + } + /* check the sum of the child bandwidth is under parent's */ + if (parent != NULL) { + if (pa->bandwidth > parent->bandwidth) { + warnx("bandwidth for %s higher than parent", + pa->qname); + return (1); + } + bwsum = 0; + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, + IFNAMSIZ) == 0 && + altq->qname[0] != 0 && + strncmp(altq->parent, pa->parent, + PF_QNAME_SIZE) == 0) + bwsum += altq->bandwidth; + } + bwsum += pa->bandwidth; + if (bwsum > parent->bandwidth) { + warnx("the sum of the child bandwidth higher" + " than parent \"%s\"", parent->qname); + } + } + } + + if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) + return (1); + + switch (pa->scheduler) { + case ALTQT_CBQ: + error = eval_pfqueue_cbq(pf, pa); + break; + case ALTQT_PRIQ: + error = eval_pfqueue_priq(pf, pa); + break; + case ALTQT_HFSC: + error = eval_pfqueue_hfsc(pf, pa); + break; + default: + break; + } + return (error); +} + +/* + * CBQ support functions + */ +#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ +#define RM_NS_PER_SEC (1000000000) + +static int +eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa) +{ + struct cbq_opts *opts; + u_int ifmtu; + + if (pa->priority >= CBQ_MAXPRI) { + warnx("priority out of range: max %d", CBQ_MAXPRI - 1); + return (-1); + } + + ifmtu = getifmtu(pa->ifname); + opts = &pa->pq_u.cbq_opts; + + if (opts->pktsize == 0) { /* use default */ + opts->pktsize = ifmtu; + if (opts->pktsize > MCLBYTES) /* do what TCP does */ + opts->pktsize &= ~MCLBYTES; + } else if (opts->pktsize > ifmtu) + opts->pktsize = ifmtu; + if (opts->maxpktsize == 0) /* use default */ + opts->maxpktsize = ifmtu; + else if (opts->maxpktsize > ifmtu) + opts->pktsize = ifmtu; + + if (opts->pktsize > opts->maxpktsize) + opts->pktsize = opts->maxpktsize; + + if (pa->parent[0] == 0) + opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); + + cbq_compute_idletime(pf, pa); + return (0); +} + +/* + * compute ns_per_byte, maxidle, minidle, and offtime + */ +static int +cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) +{ + struct cbq_opts *opts; + double maxidle_s, maxidle, minidle; + double offtime, nsPerByte, ifnsPerByte, ptime, cptime; + double z, g, f, gton, gtom; + u_int minburst, maxburst; + + opts = &pa->pq_u.cbq_opts; + ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; + minburst = opts->minburst; + maxburst = opts->maxburst; + + if (pa->bandwidth == 0) + f = 0.0001; /* small enough? */ + else + f = ((double) pa->bandwidth / (double) pa->ifbandwidth); + + nsPerByte = ifnsPerByte / f; + ptime = (double)opts->pktsize * ifnsPerByte; + cptime = ptime * (1.0 - f) / f; + + if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { + /* + * this causes integer overflow in kernel! + * (bandwidth < 6Kbps when max_pkt_size=1500) + */ + if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) + warnx("queue bandwidth must be larger than %s", + rate2str(ifnsPerByte * (double)opts->maxpktsize / + (double)INT_MAX * (double)pa->ifbandwidth)); + fprintf(stderr, "cbq: queue %s is too slow!\n", + pa->qname); + nsPerByte = (double)(INT_MAX / opts->maxpktsize); + } + + if (maxburst == 0) { /* use default */ + if (cptime > 10.0 * 1000000) + maxburst = 4; + else + maxburst = 16; + } + if (minburst == 0) /* use default */ + minburst = 2; + if (minburst > maxburst) + minburst = maxburst; + + z = (double)(1 << RM_FILTER_GAIN); + g = (1.0 - 1.0 / z); + gton = pow(g, (double)maxburst); + gtom = pow(g, (double)(minburst-1)); + maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); + maxidle_s = (1.0 - g); + if (maxidle > maxidle_s) + maxidle = ptime * maxidle; + else + maxidle = ptime * maxidle_s; + offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); + minidle = -((double)opts->maxpktsize * (double)nsPerByte); + + /* scale parameters */ + maxidle = ((maxidle * 8.0) / nsPerByte) * + pow(2.0, (double)RM_FILTER_GAIN); + offtime = (offtime * 8.0) / nsPerByte * + pow(2.0, (double)RM_FILTER_GAIN); + minidle = ((minidle * 8.0) / nsPerByte) * + pow(2.0, (double)RM_FILTER_GAIN); + + maxidle = maxidle / 1000.0; + offtime = offtime / 1000.0; + minidle = minidle / 1000.0; + + opts->minburst = minburst; + opts->maxburst = maxburst; + opts->ns_per_byte = (u_int)nsPerByte; + opts->maxidle = (u_int)fabs(maxidle); + opts->minidle = (int)minidle; + opts->offtime = (u_int)fabs(offtime); + + return (0); +} + +static int +check_commit_cbq(int dev, int opts, struct pf_altq *pa) +{ + struct pf_altq *altq; + int root_class, default_class; + int error = 0; + + /* + * check if cbq has one root queue and one default queue + * for this interface + */ + root_class = default_class = 0; + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) + root_class++; + if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) + default_class++; + } + if (root_class != 1) { + warnx("should have one root queue on %s", pa->ifname); + error++; + } + if (default_class != 1) { + warnx("should have one default queue on %s", pa->ifname); + error++; + } + return (error); +} + +static int +print_cbq_opts(const struct pf_altq *a) +{ + const struct cbq_opts *opts; + + opts = &a->pq_u.cbq_opts; + if (opts->flags) { + printf("cbq("); + if (opts->flags & CBQCLF_RED) + printf(" red"); + if (opts->flags & CBQCLF_ECN) + printf(" ecn"); + if (opts->flags & CBQCLF_RIO) + printf(" rio"); + if (opts->flags & CBQCLF_CLEARDSCP) + printf(" cleardscp"); + if (opts->flags & CBQCLF_FLOWVALVE) + printf(" flowvalve"); + if (opts->flags & CBQCLF_BORROW) + printf(" borrow"); + if (opts->flags & CBQCLF_WRR) + printf(" wrr"); + if (opts->flags & CBQCLF_EFFICIENT) + printf(" efficient"); + if (opts->flags & CBQCLF_ROOTCLASS) + printf(" root"); + if (opts->flags & CBQCLF_DEFCLASS) + printf(" default"); + printf(" ) "); + + return (1); + } else + return (0); +} + +/* + * PRIQ support functions + */ +static int +eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) +{ + struct pf_altq *altq; + + if (pa->priority >= PRIQ_MAXPRI) { + warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); + return (-1); + } + /* the priority should be unique for the interface */ + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 && + altq->qname[0] != 0 && altq->priority == pa->priority) { + warnx("%s and %s have the same priority", + altq->qname, pa->qname); + return (-1); + } + } + + return (0); +} + +static int +check_commit_priq(int dev, int opts, struct pf_altq *pa) +{ + struct pf_altq *altq; + int default_class; + int error = 0; + + /* + * check if priq has one default class for this interface + */ + default_class = 0; + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) + default_class++; + } + if (default_class != 1) { + warnx("should have one default queue on %s", pa->ifname); + error++; + } + return (error); +} + +static int +print_priq_opts(const struct pf_altq *a) +{ + const struct priq_opts *opts; + + opts = &a->pq_u.priq_opts; + + if (opts->flags) { + printf("priq("); + if (opts->flags & PRCF_RED) + printf(" red"); + if (opts->flags & PRCF_ECN) + printf(" ecn"); + if (opts->flags & PRCF_RIO) + printf(" rio"); + if (opts->flags & PRCF_CLEARDSCP) + printf(" cleardscp"); + if (opts->flags & PRCF_DEFAULTCLASS) + printf(" default"); + printf(" ) "); + + return (1); + } else + return (0); +} + +/* + * HFSC support functions + */ +static int +eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) +{ + struct pf_altq *altq, *parent; + struct hfsc_opts *opts; + struct service_curve sc; + + opts = &pa->pq_u.hfsc_opts; + + if (pa->parent[0] == 0) { + /* root queue */ + opts->lssc_m1 = pa->ifbandwidth; + opts->lssc_m2 = pa->ifbandwidth; + opts->lssc_d = 0; + return (0); + } + + LIST_INIT(&rtsc); + LIST_INIT(&lssc); + + /* if link_share is not specified, use bandwidth */ + if (opts->lssc_m2 == 0) + opts->lssc_m2 = pa->bandwidth; + + if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || + (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || + (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { + warnx("m2 is zero for %s", pa->qname); + return (-1); + } + + if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || + (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || + (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { + warnx("m1 must be zero for convex curve: %s", pa->qname); + return (-1); + } + + /* + * admission control: + * for the real-time service curve, the sum of the service curves + * should not exceed 80% of the interface bandwidth. 20% is reserved + * not to over-commit the actual interface bandwidth. + * for the linkshare service curve, the sum of the child service + * curve should not exceed the parent service curve. + * for the upper-limit service curve, the assigned bandwidth should + * be smaller than the interface bandwidth, and the upper-limit should + * be larger than the real-time service curve when both are defined. + */ + parent = qname_to_pfaltq(pa->parent, pa->ifname); + if (parent == NULL) + errx(1, "parent %s not found for %s", pa->parent, pa->qname); + + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + + /* if the class has a real-time service curve, add it. */ + if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) { + sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1; + sc.d = altq->pq_u.hfsc_opts.rtsc_d; + sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2; + gsc_add_sc(&rtsc, &sc); + } + + if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) + continue; + + /* if the class has a linkshare service curve, add it. */ + if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) { + sc.m1 = altq->pq_u.hfsc_opts.lssc_m1; + sc.d = altq->pq_u.hfsc_opts.lssc_d; + sc.m2 = altq->pq_u.hfsc_opts.lssc_m2; + gsc_add_sc(&lssc, &sc); + } + } + + /* check the real-time service curve. reserve 20% of interface bw */ + if (opts->rtsc_m2 != 0) { + /* add this queue to the sum */ + sc.m1 = opts->rtsc_m1; + sc.d = opts->rtsc_d; + sc.m2 = opts->rtsc_m2; + gsc_add_sc(&rtsc, &sc); + /* compare the sum with 80% of the interface */ + sc.m1 = 0; + sc.d = 0; + sc.m2 = pa->ifbandwidth / 100 * 80; + if (!is_gsc_under_sc(&rtsc, &sc)) { + warnx("real-time sc exceeds 80%% of the interface " + "bandwidth (%s)", rate2str((double)sc.m2)); + goto err_ret; + } + } + + /* check the linkshare service curve. */ + if (opts->lssc_m2 != 0) { + /* add this queue to the child sum */ + sc.m1 = opts->lssc_m1; + sc.d = opts->lssc_d; + sc.m2 = opts->lssc_m2; + gsc_add_sc(&lssc, &sc); + /* compare the sum of the children with parent's sc */ + sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; + sc.d = parent->pq_u.hfsc_opts.lssc_d; + sc.m2 = parent->pq_u.hfsc_opts.lssc_m2; + if (!is_gsc_under_sc(&lssc, &sc)) { + warnx("linkshare sc exceeds parent's sc"); + goto err_ret; + } + } + + /* check the upper-limit service curve. */ + if (opts->ulsc_m2 != 0) { + if (opts->ulsc_m1 > pa->ifbandwidth || + opts->ulsc_m2 > pa->ifbandwidth) { + warnx("upper-limit larger than interface bandwidth"); + goto err_ret; + } + if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { + warnx("upper-limit sc smaller than real-time sc"); + goto err_ret; + } + } + + gsc_destroy(&rtsc); + gsc_destroy(&lssc); + + return (0); + +err_ret: + gsc_destroy(&rtsc); + gsc_destroy(&lssc); + return (-1); +} + +static int +check_commit_hfsc(int dev, int opts, struct pf_altq *pa) +{ + struct pf_altq *altq, *def = NULL; + int default_class; + int error = 0; + + /* check if hfsc has one default queue for this interface */ + default_class = 0; + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (altq->parent[0] == 0) /* dummy root */ + continue; + if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { + default_class++; + def = altq; + } + } + if (default_class != 1) { + warnx("should have one default queue on %s", pa->ifname); + return (1); + } + /* make sure the default queue is a leaf */ + TAILQ_FOREACH(altq, &altqs, entries) { + if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) + continue; + if (altq->qname[0] == 0) /* this is for interface */ + continue; + if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { + warnx("default queue is not a leaf"); + error++; + } + } + return (error); +} + +static int +print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) +{ + const struct hfsc_opts *opts; + const struct node_hfsc_sc *rtsc, *lssc, *ulsc; + + opts = &a->pq_u.hfsc_opts; + if (qopts == NULL) + rtsc = lssc = ulsc = NULL; + else { + rtsc = &qopts->data.hfsc_opts.realtime; + lssc = &qopts->data.hfsc_opts.linkshare; + ulsc = &qopts->data.hfsc_opts.upperlimit; + } + + if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || + (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || + opts->lssc_d != 0))) { + printf("hfsc("); + if (opts->flags & HFCF_RED) + printf(" red"); + if (opts->flags & HFCF_ECN) + printf(" ecn"); + if (opts->flags & HFCF_RIO) + printf(" rio"); + if (opts->flags & HFCF_CLEARDSCP) + printf(" cleardscp"); + if (opts->flags & HFCF_DEFAULTCLASS) + printf(" default"); + if (opts->rtsc_m2 != 0) + print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, + opts->rtsc_m2, rtsc); + if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || + opts->lssc_d != 0)) + print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, + opts->lssc_m2, lssc); + if (opts->ulsc_m2 != 0) + print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, + opts->ulsc_m2, ulsc); + printf(" ) "); + + return (1); + } else + return (0); +} + +/* + * admission control using generalized service curve + */ + +/* add a new service curve to a generalized service curve */ +static void +gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) +{ + if (is_sc_null(sc)) + return; + if (sc->d != 0) + gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); + gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); +} + +/* + * check whether all points of a generalized service curve have + * their y-coordinates no larger than a given two-piece linear + * service curve. + */ +static int +is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) +{ + struct segment *s, *last, *end; + double y; + + if (is_sc_null(sc)) { + if (LIST_EMPTY(gsc)) + return (1); + LIST_FOREACH(s, gsc, _next) { + if (s->m != 0) + return (0); + } + return (1); + } + /* + * gsc has a dummy entry at the end with x = INFINITY. + * loop through up to this dummy entry. + */ + end = gsc_getentry(gsc, INFINITY); + if (end == NULL) + return (1); + last = NULL; + for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { + if (s->y > sc_x2y(sc, s->x)) + return (0); + last = s; + } + /* last now holds the real last segment */ + if (last == NULL) + return (1); + if (last->m > sc->m2) + return (0); + if (last->x < sc->d && last->m > sc->m1) { + y = last->y + (sc->d - last->x) * last->m; + if (y > sc_x2y(sc, sc->d)) + return (0); + } + return (1); +} + +static void +gsc_destroy(struct gen_sc *gsc) +{ + struct segment *s; + + while ((s = LIST_FIRST(gsc)) != NULL) { + LIST_REMOVE(s, _next); + free(s); + } +} + +/* + * return a segment entry starting at x. + * if gsc has no entry starting at x, a new entry is created at x. + */ +static struct segment * +gsc_getentry(struct gen_sc *gsc, double x) +{ + struct segment *new, *prev, *s; + + prev = NULL; + LIST_FOREACH(s, gsc, _next) { + if (s->x == x) + return (s); /* matching entry found */ + else if (s->x < x) + prev = s; + else + break; + } + + /* we have to create a new entry */ + if ((new = calloc(1, sizeof(struct segment))) == NULL) + return (NULL); + + new->x = x; + if (x == INFINITY || s == NULL) + new->d = 0; + else if (s->x == INFINITY) + new->d = INFINITY; + else + new->d = s->x - x; + if (prev == NULL) { + /* insert the new entry at the head of the list */ + new->y = 0; + new->m = 0; + LIST_INSERT_HEAD(gsc, new, _next); + } else { + /* + * the start point intersects with the segment pointed by + * prev. divide prev into 2 segments + */ + if (x == INFINITY) { + prev->d = INFINITY; + if (prev->m == 0) + new->y = prev->y; + else + new->y = INFINITY; + } else { + prev->d = x - prev->x; + new->y = prev->d * prev->m + prev->y; + } + new->m = prev->m; + LIST_INSERT_AFTER(prev, new, _next); + } + return (new); +} + +/* add a segment to a generalized service curve */ +static int +gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) +{ + struct segment *start, *end, *s; + double x2; + + if (d == INFINITY) + x2 = INFINITY; + else + x2 = x + d; + start = gsc_getentry(gsc, x); + end = gsc_getentry(gsc, x2); + if (start == NULL || end == NULL) + return (-1); + + for (s = start; s != end; s = LIST_NEXT(s, _next)) { + s->m += m; + s->y += y + (s->x - x) * m; + } + + end = gsc_getentry(gsc, INFINITY); + for (; s != end; s = LIST_NEXT(s, _next)) { + s->y += m * d; + } + + return (0); +} + +/* get y-projection of a service curve */ +static double +sc_x2y(struct service_curve *sc, double x) +{ + double y; + + if (x <= (double)sc->d) + /* y belongs to the 1st segment */ + y = x * (double)sc->m1; + else + /* y belongs to the 2nd segment */ + y = (double)sc->d * (double)sc->m1 + + (x - (double)sc->d) * (double)sc->m2; + return (y); +} + +/* + * misc utilities + */ +#define R2S_BUFS 8 +#define RATESTR_MAX 16 + +char * +rate2str(double rate) +{ + char *buf; + static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ + static int idx = 0; + int i; + static const char unit[] = " KMG"; + + buf = r2sbuf[idx++]; + if (idx == R2S_BUFS) + idx = 0; + + for (i = 0; rate >= 1000 && i <= 3; i++) + rate /= 1000; + + if ((int)(rate * 100) % 100) + snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); + else + snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); + + return (buf); +} + +#ifdef __FreeBSD__ +/* + * XXX + * FreeBSD does not have SIOCGIFDATA. + * To emulate this, DIOCGIFSPEED ioctl added to pf. + */ +u_int32_t +getifspeed(int pfdev, char *ifname) +{ + struct pf_ifspeed io; + + bzero(&io, sizeof io); + if (strlcpy(io.ifname, ifname, IFNAMSIZ) >= + sizeof(io.ifname)) + errx(1, "getifspeed: strlcpy"); + if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) + err(1, "DIOCGIFSPEED"); + return ((u_int32_t)io.baudrate); +} +#else +u_int32_t +getifspeed(char *ifname) +{ + int s; + struct ifreq ifr; + struct if_data ifrdat; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + bzero(&ifr, sizeof(ifr)); + if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= + sizeof(ifr.ifr_name)) + errx(1, "getifspeed: strlcpy"); + ifr.ifr_data = (caddr_t)&ifrdat; + if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) + err(1, "SIOCGIFDATA"); + if (close(s)) + err(1, "close"); + return ((u_int32_t)ifrdat.ifi_baudrate); +} +#endif + +u_long +getifmtu(char *ifname) +{ + int s; + struct ifreq ifr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + bzero(&ifr, sizeof(ifr)); + if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= + sizeof(ifr.ifr_name)) + errx(1, "getifmtu: strlcpy"); + if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) +#ifdef __FreeBSD__ + ifr.ifr_mtu = 1500; +#else + err(1, "SIOCGIFMTU"); +#endif + if (close(s)) + err(1, "close"); + if (ifr.ifr_mtu > 0) + return (ifr.ifr_mtu); + else { + warnx("could not get mtu for %s, assuming 1500", ifname); + return (1500); + } +} + +int +eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, + u_int32_t ref_bw) +{ + int errors = 0; + + switch (pa->scheduler) { + case ALTQT_CBQ: + pa->pq_u.cbq_opts = opts->data.cbq_opts; + break; + case ALTQT_PRIQ: + pa->pq_u.priq_opts = opts->data.priq_opts; + break; + case ALTQT_HFSC: + pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; + if (opts->data.hfsc_opts.linkshare.used) { + pa->pq_u.hfsc_opts.lssc_m1 = + eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, + ref_bw); + pa->pq_u.hfsc_opts.lssc_m2 = + eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, + ref_bw); + pa->pq_u.hfsc_opts.lssc_d = + opts->data.hfsc_opts.linkshare.d; + } + if (opts->data.hfsc_opts.realtime.used) { + pa->pq_u.hfsc_opts.rtsc_m1 = + eval_bwspec(&opts->data.hfsc_opts.realtime.m1, + ref_bw); + pa->pq_u.hfsc_opts.rtsc_m2 = + eval_bwspec(&opts->data.hfsc_opts.realtime.m2, + ref_bw); + pa->pq_u.hfsc_opts.rtsc_d = + opts->data.hfsc_opts.realtime.d; + } + if (opts->data.hfsc_opts.upperlimit.used) { + pa->pq_u.hfsc_opts.ulsc_m1 = + eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, + ref_bw); + pa->pq_u.hfsc_opts.ulsc_m2 = + eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, + ref_bw); + pa->pq_u.hfsc_opts.ulsc_d = + opts->data.hfsc_opts.upperlimit.d; + } + break; + default: + warnx("eval_queue_opts: unknown scheduler type %u", + opts->qtype); + errors++; + break; + } + + return (errors); +} + +u_int32_t +eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) +{ + if (bw->bw_absolute > 0) + return (bw->bw_absolute); + + if (bw->bw_percent > 0) + return (ref_bw / 100 * bw->bw_percent); + + return (0); +} + +void +print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, + const struct node_hfsc_sc *sc) +{ + printf(" %s", scname); + + if (d != 0) { + printf("("); + if (sc != NULL && sc->m1.bw_percent > 0) + printf("%u%%", sc->m1.bw_percent); + else + printf("%s", rate2str((double)m1)); + printf(" %u", d); + } + + if (sc != NULL && sc->m2.bw_percent > 0) + printf(" %u%%", sc->m2.bw_percent); + else + printf(" %s", rate2str((double)m2)); + + if (d != 0) + printf(")"); +} diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c new file mode 100644 index 0000000000000..9511720ed67dd --- /dev/null +++ b/sbin/pfctl/pfctl_optimize.c @@ -0,0 +1,1655 @@ +/* $OpenBSD: pfctl_optimize.c,v 1.17 2008/05/06 03:45:21 mpf Exp $ */ + +/* + * Copyright (c) 2004 Mike Frantzen + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +/* The size at which a table becomes faster than individual rules */ +#define TABLE_THRESHOLD 6 + + +/* #define OPT_DEBUG 1 */ +#ifdef OPT_DEBUG +# define DEBUG(str, v...) \ + printf("%s: " str "\n", __FUNCTION__ , ## v) +#else +# define DEBUG(str, v...) ((void)0) +#endif + + +/* + * A container that lets us sort a superblock to optimize the skip step jumps + */ +struct pf_skip_step { + int ps_count; /* number of items */ + TAILQ_HEAD( , pf_opt_rule) ps_rules; + TAILQ_ENTRY(pf_skip_step) ps_entry; +}; + + +/* + * A superblock is a block of adjacent rules of similar action. If there + * are five PASS rules in a row, they all become members of a superblock. + * Once we have a superblock, we are free to re-order any rules within it + * in order to improve performance; if a packet is passed, it doesn't matter + * who passed it. + */ +struct superblock { + TAILQ_HEAD( , pf_opt_rule) sb_rules; + TAILQ_ENTRY(superblock) sb_entry; + struct superblock *sb_profiled_block; + TAILQ_HEAD(skiplist, pf_skip_step) sb_skipsteps[PF_SKIP_COUNT]; +}; +TAILQ_HEAD(superblocks, superblock); + + +/* + * Description of the PF rule structure. + */ +enum { + BARRIER, /* the presence of the field puts the rule in it's own block */ + BREAK, /* the field may not differ between rules in a superblock */ + NOMERGE, /* the field may not differ between rules when combined */ + COMBINED, /* the field may itself be combined with other rules */ + DC, /* we just don't care about the field */ + NEVER}; /* we should never see this field set?!? */ +struct pf_rule_field { + const char *prf_name; + int prf_type; + size_t prf_offset; + size_t prf_size; +} pf_rule_desc[] = { +#define PF_RULE_FIELD(field, ty) \ + {#field, \ + ty, \ + offsetof(struct pf_rule, field), \ + sizeof(((struct pf_rule *)0)->field)} + + + /* + * The presence of these fields in a rule put the rule in it's own + * superblock. Thus it will not be optimized. It also prevents the + * rule from being re-ordered at all. + */ + PF_RULE_FIELD(label, BARRIER), + PF_RULE_FIELD(prob, BARRIER), + PF_RULE_FIELD(max_states, BARRIER), + PF_RULE_FIELD(max_src_nodes, BARRIER), + PF_RULE_FIELD(max_src_states, BARRIER), + PF_RULE_FIELD(max_src_conn, BARRIER), + PF_RULE_FIELD(max_src_conn_rate, BARRIER), + PF_RULE_FIELD(anchor, BARRIER), /* for now */ + + /* + * These fields must be the same between all rules in the same superblock. + * These rules are allowed to be re-ordered but only among like rules. + * For instance we can re-order all 'tag "foo"' rules because they have the + * same tag. But we can not re-order between a 'tag "foo"' and a + * 'tag "bar"' since that would change the meaning of the ruleset. + */ + PF_RULE_FIELD(tagname, BREAK), + PF_RULE_FIELD(keep_state, BREAK), + PF_RULE_FIELD(qname, BREAK), + PF_RULE_FIELD(pqname, BREAK), + PF_RULE_FIELD(rt, BREAK), + PF_RULE_FIELD(allow_opts, BREAK), + PF_RULE_FIELD(rule_flag, BREAK), + PF_RULE_FIELD(action, BREAK), + PF_RULE_FIELD(log, BREAK), + PF_RULE_FIELD(quick, BREAK), + PF_RULE_FIELD(return_ttl, BREAK), + PF_RULE_FIELD(overload_tblname, BREAK), + PF_RULE_FIELD(flush, BREAK), + PF_RULE_FIELD(rpool, BREAK), + PF_RULE_FIELD(logif, BREAK), + + /* + * Any fields not listed in this structure act as BREAK fields + */ + + + /* + * These fields must not differ when we merge two rules together but + * their difference isn't enough to put the rules in different superblocks. + * There are no problems re-ordering any rules with these fields. + */ + PF_RULE_FIELD(af, NOMERGE), + PF_RULE_FIELD(ifnot, NOMERGE), + PF_RULE_FIELD(ifname, NOMERGE), /* hack for IF groups */ + PF_RULE_FIELD(match_tag_not, NOMERGE), + PF_RULE_FIELD(match_tagname, NOMERGE), + PF_RULE_FIELD(os_fingerprint, NOMERGE), + PF_RULE_FIELD(timeout, NOMERGE), + PF_RULE_FIELD(return_icmp, NOMERGE), + PF_RULE_FIELD(return_icmp6, NOMERGE), + PF_RULE_FIELD(uid, NOMERGE), + PF_RULE_FIELD(gid, NOMERGE), + PF_RULE_FIELD(direction, NOMERGE), + PF_RULE_FIELD(proto, NOMERGE), + PF_RULE_FIELD(type, NOMERGE), + PF_RULE_FIELD(code, NOMERGE), + PF_RULE_FIELD(flags, NOMERGE), + PF_RULE_FIELD(flagset, NOMERGE), + PF_RULE_FIELD(tos, NOMERGE), + PF_RULE_FIELD(src.port, NOMERGE), + PF_RULE_FIELD(dst.port, NOMERGE), + PF_RULE_FIELD(src.port_op, NOMERGE), + PF_RULE_FIELD(dst.port_op, NOMERGE), + PF_RULE_FIELD(src.neg, NOMERGE), + PF_RULE_FIELD(dst.neg, NOMERGE), + + /* These fields can be merged */ + PF_RULE_FIELD(src.addr, COMBINED), + PF_RULE_FIELD(dst.addr, COMBINED), + + /* We just don't care about these fields. They're set by the kernel */ + PF_RULE_FIELD(skip, DC), + PF_RULE_FIELD(evaluations, DC), + PF_RULE_FIELD(packets, DC), + PF_RULE_FIELD(bytes, DC), + PF_RULE_FIELD(kif, DC), + PF_RULE_FIELD(states_cur, DC), + PF_RULE_FIELD(states_tot, DC), + PF_RULE_FIELD(src_nodes, DC), + PF_RULE_FIELD(nr, DC), + PF_RULE_FIELD(entries, DC), + PF_RULE_FIELD(qid, DC), + PF_RULE_FIELD(pqid, DC), + PF_RULE_FIELD(anchor_relative, DC), + PF_RULE_FIELD(anchor_wildcard, DC), + PF_RULE_FIELD(tag, DC), + PF_RULE_FIELD(match_tag, DC), + PF_RULE_FIELD(overload_tbl, DC), + + /* These fields should never be set in a PASS/BLOCK rule */ + PF_RULE_FIELD(natpass, NEVER), + PF_RULE_FIELD(max_mss, NEVER), + PF_RULE_FIELD(min_ttl, NEVER), + PF_RULE_FIELD(set_tos, NEVER), +}; + + + +int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t, + struct pf_rule_addr *); +int addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *); +int addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *); +int block_feedback(struct pfctl *, struct superblock *); +int combine_rules(struct pfctl *, struct superblock *); +void comparable_rule(struct pf_rule *, const struct pf_rule *, int); +int construct_superblocks(struct pfctl *, struct pf_opt_queue *, + struct superblocks *); +void exclude_supersets(struct pf_rule *, struct pf_rule *); +int interface_group(const char *); +int load_feedback_profile(struct pfctl *, struct superblocks *); +int optimize_superblock(struct pfctl *, struct superblock *); +int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *); +void remove_from_skipsteps(struct skiplist *, struct superblock *, + struct pf_opt_rule *, struct pf_skip_step *); +int remove_identical_rules(struct pfctl *, struct superblock *); +int reorder_rules(struct pfctl *, struct superblock *, int); +int rules_combineable(struct pf_rule *, struct pf_rule *); +void skip_append(struct superblock *, int, struct pf_skip_step *, + struct pf_opt_rule *); +int skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *); +void skip_init(void); +int skip_cmp_af(struct pf_rule *, struct pf_rule *); +int skip_cmp_dir(struct pf_rule *, struct pf_rule *); +int skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *); +int skip_cmp_dst_port(struct pf_rule *, struct pf_rule *); +int skip_cmp_ifp(struct pf_rule *, struct pf_rule *); +int skip_cmp_proto(struct pf_rule *, struct pf_rule *); +int skip_cmp_src_addr(struct pf_rule *, struct pf_rule *); +int skip_cmp_src_port(struct pf_rule *, struct pf_rule *); +int superblock_inclusive(struct superblock *, struct pf_opt_rule *); +void superblock_free(struct pfctl *, struct superblock *); + + +int (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *); +const char *skip_comparitors_names[PF_SKIP_COUNT]; +#define PF_SKIP_COMPARITORS { \ + { "ifp", PF_SKIP_IFP, skip_cmp_ifp }, \ + { "dir", PF_SKIP_DIR, skip_cmp_dir }, \ + { "af", PF_SKIP_AF, skip_cmp_af }, \ + { "proto", PF_SKIP_PROTO, skip_cmp_proto }, \ + { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \ + { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \ + { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \ + { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \ +} + +struct pfr_buffer table_buffer; +int table_identifier; + + +int +pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs) +{ + struct superblocks superblocks; + struct pf_opt_queue opt_queue; + struct superblock *block; + struct pf_opt_rule *por; + struct pf_rule *r; + struct pf_rulequeue *old_rules; + + DEBUG("optimizing ruleset"); + memset(&table_buffer, 0, sizeof(table_buffer)); + skip_init(); + TAILQ_INIT(&opt_queue); + + old_rules = rs->rules[PF_RULESET_FILTER].active.ptr; + rs->rules[PF_RULESET_FILTER].active.ptr = + rs->rules[PF_RULESET_FILTER].inactive.ptr; + rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules; + + /* + * XXX expanding the pf_opt_rule format throughout pfctl might allow + * us to avoid all this copying. + */ + while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr)) + != NULL) { + TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r, + entries); + if ((por = calloc(1, sizeof(*por))) == NULL) + err(1, "calloc"); + memcpy(&por->por_rule, r, sizeof(*r)); + if (TAILQ_FIRST(&r->rpool.list) != NULL) { + TAILQ_INIT(&por->por_rule.rpool.list); + pfctl_move_pool(&r->rpool, &por->por_rule.rpool); + } else + bzero(&por->por_rule.rpool, + sizeof(por->por_rule.rpool)); + + + TAILQ_INSERT_TAIL(&opt_queue, por, por_entry); + } + + TAILQ_INIT(&superblocks); + if (construct_superblocks(pf, &opt_queue, &superblocks)) + goto error; + + if (pf->optimize & PF_OPTIMIZE_PROFILE) { + if (load_feedback_profile(pf, &superblocks)) + goto error; + } + + TAILQ_FOREACH(block, &superblocks, sb_entry) { + if (optimize_superblock(pf, block)) + goto error; + } + + rs->anchor->refcnt = 0; + while ((block = TAILQ_FIRST(&superblocks))) { + TAILQ_REMOVE(&superblocks, block, sb_entry); + + while ((por = TAILQ_FIRST(&block->sb_rules))) { + TAILQ_REMOVE(&block->sb_rules, por, por_entry); + por->por_rule.nr = rs->anchor->refcnt++; + if ((r = calloc(1, sizeof(*r))) == NULL) + err(1, "calloc"); + memcpy(r, &por->por_rule, sizeof(*r)); + TAILQ_INIT(&r->rpool.list); + pfctl_move_pool(&por->por_rule.rpool, &r->rpool); + TAILQ_INSERT_TAIL( + rs->rules[PF_RULESET_FILTER].active.ptr, + r, entries); + free(por); + } + free(block); + } + + return (0); + +error: + while ((por = TAILQ_FIRST(&opt_queue))) { + TAILQ_REMOVE(&opt_queue, por, por_entry); + if (por->por_src_tbl) { + pfr_buf_clear(por->por_src_tbl->pt_buf); + free(por->por_src_tbl->pt_buf); + free(por->por_src_tbl); + } + if (por->por_dst_tbl) { + pfr_buf_clear(por->por_dst_tbl->pt_buf); + free(por->por_dst_tbl->pt_buf); + free(por->por_dst_tbl); + } + free(por); + } + while ((block = TAILQ_FIRST(&superblocks))) { + TAILQ_REMOVE(&superblocks, block, sb_entry); + superblock_free(pf, block); + } + return (1); +} + + +/* + * Go ahead and optimize a superblock + */ +int +optimize_superblock(struct pfctl *pf, struct superblock *block) +{ +#ifdef OPT_DEBUG + struct pf_opt_rule *por; +#endif /* OPT_DEBUG */ + + /* We have a few optimization passes: + * 1) remove duplicate rules or rules that are a subset of other + * rules + * 2) combine otherwise identical rules with different IP addresses + * into a single rule and put the addresses in a table. + * 3) re-order the rules to improve kernel skip steps + * 4) re-order the 'quick' rules based on feedback from the + * active ruleset statistics + * + * XXX combine_rules() doesn't combine v4 and v6 rules. would just + * have to keep af in the table container, make af 'COMBINE' and + * twiddle the af on the merged rule + * XXX maybe add a weighting to the metric on skipsteps when doing + * reordering. sometimes two sequential tables will be better + * that four consecutive interfaces. + * XXX need to adjust the skipstep count of everything after PROTO, + * since they aren't actually checked on a proto mismatch in + * pf_test_{tcp, udp, icmp}() + * XXX should i treat proto=0, af=0 or dir=0 special in skepstep + * calculation since they are a DC? + * XXX keep last skiplist of last superblock to influence this + * superblock. '5 inet6 log' should make '3 inet6' come before '4 + * inet' in the next superblock. + * XXX would be useful to add tables for ports + * XXX we can also re-order some mutually exclusive superblocks to + * try merging superblocks before any of these optimization passes. + * for instance a single 'log in' rule in the middle of non-logging + * out rules. + */ + + /* shortcut. there will be a lot of 1-rule superblocks */ + if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry)) + return (0); + +#ifdef OPT_DEBUG + printf("--- Superblock ---\n"); + TAILQ_FOREACH(por, &block->sb_rules, por_entry) { + printf(" "); + print_rule(&por->por_rule, por->por_rule.anchor ? + por->por_rule.anchor->name : "", 1, 0); + } +#endif /* OPT_DEBUG */ + + + if (remove_identical_rules(pf, block)) + return (1); + if (combine_rules(pf, block)) + return (1); + if ((pf->optimize & PF_OPTIMIZE_PROFILE) && + TAILQ_FIRST(&block->sb_rules)->por_rule.quick && + block->sb_profiled_block) { + if (block_feedback(pf, block)) + return (1); + } else if (reorder_rules(pf, block, 0)) { + return (1); + } + + /* + * Don't add any optimization passes below reorder_rules(). It will + * have divided superblocks into smaller blocks for further refinement + * and doesn't put them back together again. What once was a true + * superblock might have been split into multiple superblocks. + */ + +#ifdef OPT_DEBUG + printf("--- END Superblock ---\n"); +#endif /* OPT_DEBUG */ + return (0); +} + + +/* + * Optimization pass #1: remove identical rules + */ +int +remove_identical_rules(struct pfctl *pf, struct superblock *block) +{ + struct pf_opt_rule *por1, *por2, *por_next, *por2_next; + struct pf_rule a, a2, b, b2; + + for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) { + por_next = TAILQ_NEXT(por1, por_entry); + for (por2 = por_next; por2; por2 = por2_next) { + por2_next = TAILQ_NEXT(por2, por_entry); + comparable_rule(&a, &por1->por_rule, DC); + comparable_rule(&b, &por2->por_rule, DC); + memcpy(&a2, &a, sizeof(a2)); + memcpy(&b2, &b, sizeof(b2)); + + exclude_supersets(&a, &b); + exclude_supersets(&b2, &a2); + if (memcmp(&a, &b, sizeof(a)) == 0) { + DEBUG("removing identical rule nr%d = *nr%d*", + por1->por_rule.nr, por2->por_rule.nr); + TAILQ_REMOVE(&block->sb_rules, por2, por_entry); + if (por_next == por2) + por_next = TAILQ_NEXT(por1, por_entry); + free(por2); + } else if (memcmp(&a2, &b2, sizeof(a2)) == 0) { + DEBUG("removing identical rule *nr%d* = nr%d", + por1->por_rule.nr, por2->por_rule.nr); + TAILQ_REMOVE(&block->sb_rules, por1, por_entry); + free(por1); + break; + } + } + } + + return (0); +} + + +/* + * Optimization pass #2: combine similar rules with different addresses + * into a single rule and a table + */ +int +combine_rules(struct pfctl *pf, struct superblock *block) +{ + struct pf_opt_rule *p1, *p2, *por_next; + int src_eq, dst_eq; + + if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) { + warnx("Must enable table loading for optimizations"); + return (1); + } + + /* First we make a pass to combine the rules. O(n log n) */ + TAILQ_FOREACH(p1, &block->sb_rules, por_entry) { + for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) { + por_next = TAILQ_NEXT(p2, por_entry); + + src_eq = addrs_equal(&p1->por_rule.src, + &p2->por_rule.src); + dst_eq = addrs_equal(&p1->por_rule.dst, + &p2->por_rule.dst); + + if (src_eq && !dst_eq && p1->por_src_tbl == NULL && + p2->por_dst_tbl == NULL && + p2->por_src_tbl == NULL && + rules_combineable(&p1->por_rule, &p2->por_rule) && + addrs_combineable(&p1->por_rule.dst, + &p2->por_rule.dst)) { + DEBUG("can combine rules nr%d = nr%d", + p1->por_rule.nr, p2->por_rule.nr); + if (p1->por_dst_tbl == NULL && + add_opt_table(pf, &p1->por_dst_tbl, + p1->por_rule.af, &p1->por_rule.dst)) + return (1); + if (add_opt_table(pf, &p1->por_dst_tbl, + p1->por_rule.af, &p2->por_rule.dst)) + return (1); + p2->por_dst_tbl = p1->por_dst_tbl; + if (p1->por_dst_tbl->pt_rulecount >= + TABLE_THRESHOLD) { + TAILQ_REMOVE(&block->sb_rules, p2, + por_entry); + free(p2); + } + } else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL + && p2->por_src_tbl == NULL && + p2->por_dst_tbl == NULL && + rules_combineable(&p1->por_rule, &p2->por_rule) && + addrs_combineable(&p1->por_rule.src, + &p2->por_rule.src)) { + DEBUG("can combine rules nr%d = nr%d", + p1->por_rule.nr, p2->por_rule.nr); + if (p1->por_src_tbl == NULL && + add_opt_table(pf, &p1->por_src_tbl, + p1->por_rule.af, &p1->por_rule.src)) + return (1); + if (add_opt_table(pf, &p1->por_src_tbl, + p1->por_rule.af, &p2->por_rule.src)) + return (1); + p2->por_src_tbl = p1->por_src_tbl; + if (p1->por_src_tbl->pt_rulecount >= + TABLE_THRESHOLD) { + TAILQ_REMOVE(&block->sb_rules, p2, + por_entry); + free(p2); + } + } + } + } + + + /* + * Then we make a final pass to create a valid table name and + * insert the name into the rules. + */ + for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) { + por_next = TAILQ_NEXT(p1, por_entry); + assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL); + + if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >= + TABLE_THRESHOLD) { + if (p1->por_src_tbl->pt_generated) { + /* This rule is included in a table */ + TAILQ_REMOVE(&block->sb_rules, p1, por_entry); + free(p1); + continue; + } + p1->por_src_tbl->pt_generated = 1; + + if ((pf->opts & PF_OPT_NOACTION) == 0 && + pf_opt_create_table(pf, p1->por_src_tbl)) + return (1); + + pf->tdirty = 1; + + if (pf->opts & PF_OPT_VERBOSE) + print_tabledef(p1->por_src_tbl->pt_name, + PFR_TFLAG_CONST, 1, + &p1->por_src_tbl->pt_nodes); + + memset(&p1->por_rule.src.addr, 0, + sizeof(p1->por_rule.src.addr)); + p1->por_rule.src.addr.type = PF_ADDR_TABLE; + strlcpy(p1->por_rule.src.addr.v.tblname, + p1->por_src_tbl->pt_name, + sizeof(p1->por_rule.src.addr.v.tblname)); + + pfr_buf_clear(p1->por_src_tbl->pt_buf); + free(p1->por_src_tbl->pt_buf); + p1->por_src_tbl->pt_buf = NULL; + } + if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >= + TABLE_THRESHOLD) { + if (p1->por_dst_tbl->pt_generated) { + /* This rule is included in a table */ + TAILQ_REMOVE(&block->sb_rules, p1, por_entry); + free(p1); + continue; + } + p1->por_dst_tbl->pt_generated = 1; + + if ((pf->opts & PF_OPT_NOACTION) == 0 && + pf_opt_create_table(pf, p1->por_dst_tbl)) + return (1); + pf->tdirty = 1; + + if (pf->opts & PF_OPT_VERBOSE) + print_tabledef(p1->por_dst_tbl->pt_name, + PFR_TFLAG_CONST, 1, + &p1->por_dst_tbl->pt_nodes); + + memset(&p1->por_rule.dst.addr, 0, + sizeof(p1->por_rule.dst.addr)); + p1->por_rule.dst.addr.type = PF_ADDR_TABLE; + strlcpy(p1->por_rule.dst.addr.v.tblname, + p1->por_dst_tbl->pt_name, + sizeof(p1->por_rule.dst.addr.v.tblname)); + + pfr_buf_clear(p1->por_dst_tbl->pt_buf); + free(p1->por_dst_tbl->pt_buf); + p1->por_dst_tbl->pt_buf = NULL; + } + } + + return (0); +} + + +/* + * Optimization pass #3: re-order rules to improve skip steps + */ +int +reorder_rules(struct pfctl *pf, struct superblock *block, int depth) +{ + struct superblock *newblock; + struct pf_skip_step *skiplist; + struct pf_opt_rule *por; + int i, largest, largest_list, rule_count = 0; + TAILQ_HEAD( , pf_opt_rule) head; + + /* + * Calculate the best-case skip steps. We put each rule in a list + * of other rules with common fields + */ + for (i = 0; i < PF_SKIP_COUNT; i++) { + TAILQ_FOREACH(por, &block->sb_rules, por_entry) { + TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i], + ps_entry) { + if (skip_compare(i, skiplist, por) == 0) + break; + } + if (skiplist == NULL) { + if ((skiplist = calloc(1, sizeof(*skiplist))) == + NULL) + err(1, "calloc"); + TAILQ_INIT(&skiplist->ps_rules); + TAILQ_INSERT_TAIL(&block->sb_skipsteps[i], + skiplist, ps_entry); + } + skip_append(block, i, skiplist, por); + } + } + + TAILQ_FOREACH(por, &block->sb_rules, por_entry) + rule_count++; + + /* + * Now we're going to ignore any fields that are identical between + * all of the rules in the superblock and those fields which differ + * between every rule in the superblock. + */ + largest = 0; + for (i = 0; i < PF_SKIP_COUNT; i++) { + skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]); + if (skiplist->ps_count == rule_count) { + DEBUG("(%d) original skipstep '%s' is all rules", + depth, skip_comparitors_names[i]); + skiplist->ps_count = 0; + } else if (skiplist->ps_count == 1) { + skiplist->ps_count = 0; + } else { + DEBUG("(%d) original skipstep '%s' largest jump is %d", + depth, skip_comparitors_names[i], + skiplist->ps_count); + if (skiplist->ps_count > largest) + largest = skiplist->ps_count; + } + } + if (largest == 0) { + /* Ugh. There is NO commonality in the superblock on which + * optimize the skipsteps optimization. + */ + goto done; + } + + /* + * Now we're going to empty the superblock rule list and re-create + * it based on a more optimal skipstep order. + */ + TAILQ_INIT(&head); + while ((por = TAILQ_FIRST(&block->sb_rules))) { + TAILQ_REMOVE(&block->sb_rules, por, por_entry); + TAILQ_INSERT_TAIL(&head, por, por_entry); + } + + + while (!TAILQ_EMPTY(&head)) { + largest = 1; + + /* + * Find the most useful skip steps remaining + */ + for (i = 0; i < PF_SKIP_COUNT; i++) { + skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]); + if (skiplist->ps_count > largest) { + largest = skiplist->ps_count; + largest_list = i; + } + } + + if (largest <= 1) { + /* + * Nothing useful left. Leave remaining rules in order. + */ + DEBUG("(%d) no more commonality for skip steps", depth); + while ((por = TAILQ_FIRST(&head))) { + TAILQ_REMOVE(&head, por, por_entry); + TAILQ_INSERT_TAIL(&block->sb_rules, por, + por_entry); + } + } else { + /* + * There is commonality. Extract those common rules + * and place them in the ruleset adjacent to each + * other. + */ + skiplist = TAILQ_FIRST(&block->sb_skipsteps[ + largest_list]); + DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d", + depth, skip_comparitors_names[largest_list], + largest, TAILQ_FIRST(&TAILQ_FIRST(&block-> + sb_skipsteps [largest_list])->ps_rules)-> + por_rule.nr); + TAILQ_REMOVE(&block->sb_skipsteps[largest_list], + skiplist, ps_entry); + + + /* + * There may be further commonality inside these + * rules. So we'll split them off into they're own + * superblock and pass it back into the optimizer. + */ + if (skiplist->ps_count > 2) { + if ((newblock = calloc(1, sizeof(*newblock))) + == NULL) { + warn("calloc"); + return (1); + } + TAILQ_INIT(&newblock->sb_rules); + for (i = 0; i < PF_SKIP_COUNT; i++) + TAILQ_INIT(&newblock->sb_skipsteps[i]); + TAILQ_INSERT_BEFORE(block, newblock, sb_entry); + DEBUG("(%d) splitting off %d rules from superblock @ #%d", + depth, skiplist->ps_count, + TAILQ_FIRST(&skiplist->ps_rules)-> + por_rule.nr); + } else { + newblock = block; + } + + while ((por = TAILQ_FIRST(&skiplist->ps_rules))) { + TAILQ_REMOVE(&head, por, por_entry); + TAILQ_REMOVE(&skiplist->ps_rules, por, + por_skip_entry[largest_list]); + TAILQ_INSERT_TAIL(&newblock->sb_rules, por, + por_entry); + + /* Remove this rule from all other skiplists */ + remove_from_skipsteps(&block->sb_skipsteps[ + largest_list], block, por, skiplist); + } + free(skiplist); + if (newblock != block) + if (reorder_rules(pf, newblock, depth + 1)) + return (1); + } + } + +done: + for (i = 0; i < PF_SKIP_COUNT; i++) { + while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) { + TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist, + ps_entry); + free(skiplist); + } + } + + return (0); +} + + +/* + * Optimization pass #4: re-order 'quick' rules based on feedback from the + * currently running ruleset + */ +int +block_feedback(struct pfctl *pf, struct superblock *block) +{ + TAILQ_HEAD( , pf_opt_rule) queue; + struct pf_opt_rule *por1, *por2; + u_int64_t total_count = 0; + struct pf_rule a, b; + + + /* + * Walk through all of the profiled superblock's rules and copy + * the counters onto our rules. + */ + TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) { + comparable_rule(&a, &por1->por_rule, DC); + total_count += por1->por_rule.packets[0] + + por1->por_rule.packets[1]; + TAILQ_FOREACH(por2, &block->sb_rules, por_entry) { + if (por2->por_profile_count) + continue; + comparable_rule(&b, &por2->por_rule, DC); + if (memcmp(&a, &b, sizeof(a)) == 0) { + por2->por_profile_count = + por1->por_rule.packets[0] + + por1->por_rule.packets[1]; + break; + } + } + } + superblock_free(pf, block->sb_profiled_block); + block->sb_profiled_block = NULL; + + /* + * Now we pull all of the rules off the superblock and re-insert them + * in sorted order. + */ + + TAILQ_INIT(&queue); + while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) { + TAILQ_REMOVE(&block->sb_rules, por1, por_entry); + TAILQ_INSERT_TAIL(&queue, por1, por_entry); + } + + while ((por1 = TAILQ_FIRST(&queue)) != NULL) { + TAILQ_REMOVE(&queue, por1, por_entry); +/* XXX I should sort all of the unused rules based on skip steps */ + TAILQ_FOREACH(por2, &block->sb_rules, por_entry) { + if (por1->por_profile_count > por2->por_profile_count) { + TAILQ_INSERT_BEFORE(por2, por1, por_entry); + break; + } + } +#ifdef __FreeBSD__ + if (por2 == NULL) +#else + if (por2 == TAILQ_END(&block->sb_rules)) +#endif + TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry); + } + + return (0); +} + + +/* + * Load the current ruleset from the kernel and try to associate them with + * the ruleset we're optimizing. + */ +int +load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) +{ + struct superblock *block, *blockcur; + struct superblocks prof_superblocks; + struct pf_opt_rule *por; + struct pf_opt_queue queue; + struct pfioc_rule pr; + struct pf_rule a, b; + int nr, mnr; + + TAILQ_INIT(&queue); + TAILQ_INIT(&prof_superblocks); + + memset(&pr, 0, sizeof(pr)); + pr.rule.action = PF_PASS; + if (ioctl(pf->dev, DIOCGETRULES, &pr)) { + warn("DIOCGETRULES"); + return (1); + } + mnr = pr.nr; + + DEBUG("Loading %d active rules for a feedback profile", mnr); + for (nr = 0; nr < mnr; ++nr) { + struct pf_ruleset *rs; + if ((por = calloc(1, sizeof(*por))) == NULL) { + warn("calloc"); + return (1); + } + pr.nr = nr; + if (ioctl(pf->dev, DIOCGETRULE, &pr)) { + warn("DIOCGETRULES"); + return (1); + } + memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule)); + rs = pf_find_or_create_ruleset(pr.anchor_call); + por->por_rule.anchor = rs->anchor; + if (TAILQ_EMPTY(&por->por_rule.rpool.list)) + memset(&por->por_rule.rpool, 0, + sizeof(por->por_rule.rpool)); + TAILQ_INSERT_TAIL(&queue, por, por_entry); + + /* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket, + * PF_PASS, pf->anchor) ??? + * ... pfctl_clear_pool(&pr.rule.rpool) + */ + } + + if (construct_superblocks(pf, &queue, &prof_superblocks)) + return (1); + + + /* + * Now we try to associate the active ruleset's superblocks with + * the superblocks we're compiling. + */ + block = TAILQ_FIRST(superblocks); + blockcur = TAILQ_FIRST(&prof_superblocks); + while (block && blockcur) { + comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, + BREAK); + comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule, + BREAK); + if (memcmp(&a, &b, sizeof(a)) == 0) { + /* The two superblocks lined up */ + block->sb_profiled_block = blockcur; + } else { + DEBUG("superblocks don't line up between #%d and #%d", + TAILQ_FIRST(&block->sb_rules)->por_rule.nr, + TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr); + break; + } + block = TAILQ_NEXT(block, sb_entry); + blockcur = TAILQ_NEXT(blockcur, sb_entry); + } + + + + /* Free any superblocks we couldn't link */ + while (blockcur) { + block = TAILQ_NEXT(blockcur, sb_entry); + superblock_free(pf, blockcur); + blockcur = block; + } + return (0); +} + + +/* + * Compare a rule to a skiplist to see if the rule is a member + */ +int +skip_compare(int skipnum, struct pf_skip_step *skiplist, + struct pf_opt_rule *por) +{ + struct pf_rule *a, *b; + if (skipnum >= PF_SKIP_COUNT || skipnum < 0) + errx(1, "skip_compare() out of bounds"); + a = &por->por_rule; + b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule; + + return ((skip_comparitors[skipnum])(a, b)); +} + + +/* + * Add a rule to a skiplist + */ +void +skip_append(struct superblock *superblock, int skipnum, + struct pf_skip_step *skiplist, struct pf_opt_rule *por) +{ + struct pf_skip_step *prev; + + skiplist->ps_count++; + TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]); + + /* Keep the list of skiplists sorted by whichever is larger */ + while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) && + prev->ps_count < skiplist->ps_count) { + TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum], + skiplist, ps_entry); + TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry); + } +} + + +/* + * Remove a rule from the other skiplist calculations. + */ +void +remove_from_skipsteps(struct skiplist *head, struct superblock *block, + struct pf_opt_rule *por, struct pf_skip_step *active_list) +{ + struct pf_skip_step *sk, *next; + struct pf_opt_rule *p2; + int i, found; + + for (i = 0; i < PF_SKIP_COUNT; i++) { + sk = TAILQ_FIRST(&block->sb_skipsteps[i]); + if (sk == NULL || sk == active_list || sk->ps_count <= 1) + continue; + found = 0; + do { + TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i]) + if (p2 == por) { + TAILQ_REMOVE(&sk->ps_rules, p2, + por_skip_entry[i]); + found = 1; + sk->ps_count--; + break; + } + } while (!found && (sk = TAILQ_NEXT(sk, ps_entry))); + if (found && sk) { + /* Does this change the sorting order? */ + while ((next = TAILQ_NEXT(sk, ps_entry)) && + next->ps_count > sk->ps_count) { + TAILQ_REMOVE(head, sk, ps_entry); + TAILQ_INSERT_AFTER(head, next, sk, ps_entry); + } +#ifdef OPT_DEBUG + next = TAILQ_NEXT(sk, ps_entry); + assert(next == NULL || next->ps_count <= sk->ps_count); +#endif /* OPT_DEBUG */ + } + } +} + + +/* Compare two rules AF field for skiplist construction */ +int +skip_cmp_af(struct pf_rule *a, struct pf_rule *b) +{ + if (a->af != b->af || a->af == 0) + return (1); + return (0); +} + +/* Compare two rules DIRECTION field for skiplist construction */ +int +skip_cmp_dir(struct pf_rule *a, struct pf_rule *b) +{ + if (a->direction == 0 || a->direction != b->direction) + return (1); + return (0); +} + +/* Compare two rules DST Address field for skiplist construction */ +int +skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b) +{ + if (a->dst.neg != b->dst.neg || + a->dst.addr.type != b->dst.addr.type) + return (1); + /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 + * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || + * a->proto == IPPROTO_ICMP + * return (1); + */ + switch (a->dst.addr.type) { + case PF_ADDR_ADDRMASK: + if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr, + sizeof(a->dst.addr.v.a.addr)) || + memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask, + sizeof(a->dst.addr.v.a.mask)) || + (a->dst.addr.v.a.addr.addr32[0] == 0 && + a->dst.addr.v.a.addr.addr32[1] == 0 && + a->dst.addr.v.a.addr.addr32[2] == 0 && + a->dst.addr.v.a.addr.addr32[3] == 0)) + return (1); + return (0); + case PF_ADDR_DYNIFTL: + if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 || + a->dst.addr.iflags != a->dst.addr.iflags || + memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask, + sizeof(a->dst.addr.v.a.mask))) + return (1); + return (0); + case PF_ADDR_NOROUTE: + case PF_ADDR_URPFFAILED: + return (0); + case PF_ADDR_TABLE: + return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname)); + } + return (1); +} + +/* Compare two rules DST port field for skiplist construction */ +int +skip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b) +{ + /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 + * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || + * a->proto == IPPROTO_ICMP + * return (1); + */ + if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op || + a->dst.port[0] != b->dst.port[0] || + a->dst.port[1] != b->dst.port[1]) + return (1); + return (0); +} + +/* Compare two rules IFP field for skiplist construction */ +int +skip_cmp_ifp(struct pf_rule *a, struct pf_rule *b) +{ + if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0') + return (1); + return (a->ifnot != b->ifnot); +} + +/* Compare two rules PROTO field for skiplist construction */ +int +skip_cmp_proto(struct pf_rule *a, struct pf_rule *b) +{ + return (a->proto != b->proto || a->proto == 0); +} + +/* Compare two rules SRC addr field for skiplist construction */ +int +skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b) +{ + if (a->src.neg != b->src.neg || + a->src.addr.type != b->src.addr.type) + return (1); + /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 + * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || + * a->proto == IPPROTO_ICMP + * return (1); + */ + switch (a->src.addr.type) { + case PF_ADDR_ADDRMASK: + if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr, + sizeof(a->src.addr.v.a.addr)) || + memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask, + sizeof(a->src.addr.v.a.mask)) || + (a->src.addr.v.a.addr.addr32[0] == 0 && + a->src.addr.v.a.addr.addr32[1] == 0 && + a->src.addr.v.a.addr.addr32[2] == 0 && + a->src.addr.v.a.addr.addr32[3] == 0)) + return (1); + return (0); + case PF_ADDR_DYNIFTL: + if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 || + a->src.addr.iflags != a->src.addr.iflags || + memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask, + sizeof(a->src.addr.v.a.mask))) + return (1); + return (0); + case PF_ADDR_NOROUTE: + case PF_ADDR_URPFFAILED: + return (0); + case PF_ADDR_TABLE: + return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname)); + } + return (1); +} + +/* Compare two rules SRC port field for skiplist construction */ +int +skip_cmp_src_port(struct pf_rule *a, struct pf_rule *b) +{ + if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op || + a->src.port[0] != b->src.port[0] || + a->src.port[1] != b->src.port[1]) + return (1); + /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0 + * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP || + * a->proto == IPPROTO_ICMP + * return (1); + */ + return (0); +} + + +void +skip_init(void) +{ + struct { + char *name; + int skipnum; + int (*func)(struct pf_rule *, struct pf_rule *); + } comps[] = PF_SKIP_COMPARITORS; + int skipnum, i; + + for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) { + for (i = 0; i < sizeof(comps)/sizeof(*comps); i++) + if (comps[i].skipnum == skipnum) { + skip_comparitors[skipnum] = comps[i].func; + skip_comparitors_names[skipnum] = comps[i].name; + } + } + for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) + if (skip_comparitors[skipnum] == NULL) + errx(1, "Need to add skip step comparitor to pfctl?!"); +} + +/* + * Add a host/netmask to a table + */ +int +add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af, + struct pf_rule_addr *addr) +{ +#ifdef OPT_DEBUG + char buf[128]; +#endif /* OPT_DEBUG */ + static int tablenum = 0; + struct node_host node_host; + + if (*tbl == NULL) { + if ((*tbl = calloc(1, sizeof(**tbl))) == NULL || + ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) == + NULL) + err(1, "calloc"); + (*tbl)->pt_buf->pfrb_type = PFRB_ADDRS; + SIMPLEQ_INIT(&(*tbl)->pt_nodes); + + /* This is just a temporary table name */ + snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d", + PF_OPT_TABLE_PREFIX, tablenum++); + DEBUG("creating table <%s>", (*tbl)->pt_name); + } + + memset(&node_host, 0, sizeof(node_host)); + node_host.af = af; + node_host.addr = addr->addr; + +#ifdef OPT_DEBUG + DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af, + &node_host.addr.v.a.addr, buf, sizeof(buf)), + unmask(&node_host.addr.v.a.mask, af)); +#endif /* OPT_DEBUG */ + + if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) { + warn("failed to add host"); + return (1); + } + if (pf->opts & PF_OPT_VERBOSE) { + struct node_tinit *ti; + + if ((ti = calloc(1, sizeof(*ti))) == NULL) + err(1, "malloc"); + if ((ti->host = malloc(sizeof(*ti->host))) == NULL) + err(1, "malloc"); + memcpy(ti->host, &node_host, sizeof(*ti->host)); + SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries); + } + + (*tbl)->pt_rulecount++; + if ((*tbl)->pt_rulecount == TABLE_THRESHOLD) + DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name); + + return (0); +} + + +/* + * Do the dirty work of choosing an unused table name and creating it. + * (be careful with the table name, it might already be used in another anchor) + */ +int +pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl) +{ + static int tablenum; + struct pfr_table *t; + + if (table_buffer.pfrb_type == 0) { + /* Initialize the list of tables */ + table_buffer.pfrb_type = PFRB_TABLES; + for (;;) { + pfr_buf_grow(&table_buffer, table_buffer.pfrb_size); + table_buffer.pfrb_size = table_buffer.pfrb_msize; + if (pfr_get_tables(NULL, table_buffer.pfrb_caddr, + &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS)) + err(1, "pfr_get_tables"); + if (table_buffer.pfrb_size <= table_buffer.pfrb_msize) + break; + } + table_identifier = arc4random(); + } + + /* XXX would be *really* nice to avoid duplicating identical tables */ + + /* Now we have to pick a table name that isn't used */ +again: + DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name, + PF_OPT_TABLE_PREFIX, table_identifier, tablenum); + snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d", + PF_OPT_TABLE_PREFIX, table_identifier, tablenum); + PFRB_FOREACH(t, &table_buffer) { + if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) { + /* Collision. Try again */ + DEBUG("wow, table <%s> in use. trying again", + tbl->pt_name); + table_identifier = arc4random(); + goto again; + } + } + tablenum++; + + + if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, + pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) { + warn("failed to create table %s in %s", + tbl->pt_name, pf->astack[0]->name); + return (1); + } + return (0); +} + +/* + * Partition the flat ruleset into a list of distinct superblocks + */ +int +construct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue, + struct superblocks *superblocks) +{ + struct superblock *block = NULL; + struct pf_opt_rule *por; + int i; + + while (!TAILQ_EMPTY(opt_queue)) { + por = TAILQ_FIRST(opt_queue); + TAILQ_REMOVE(opt_queue, por, por_entry); + if (block == NULL || !superblock_inclusive(block, por)) { + if ((block = calloc(1, sizeof(*block))) == NULL) { + warn("calloc"); + return (1); + } + TAILQ_INIT(&block->sb_rules); + for (i = 0; i < PF_SKIP_COUNT; i++) + TAILQ_INIT(&block->sb_skipsteps[i]); + TAILQ_INSERT_TAIL(superblocks, block, sb_entry); + } + TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry); + } + + return (0); +} + + +/* + * Compare two rule addresses + */ +int +addrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b) +{ + if (a->neg != b->neg) + return (0); + return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0); +} + + +/* + * The addresses are not equal, but can we combine them into one table? + */ +int +addrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b) +{ + if (a->addr.type != PF_ADDR_ADDRMASK || + b->addr.type != PF_ADDR_ADDRMASK) + return (0); + if (a->neg != b->neg || a->port_op != b->port_op || + a->port[0] != b->port[0] || a->port[1] != b->port[1]) + return (0); + return (1); +} + + +/* + * Are we allowed to combine these two rules + */ +int +rules_combineable(struct pf_rule *p1, struct pf_rule *p2) +{ + struct pf_rule a, b; + + comparable_rule(&a, p1, COMBINED); + comparable_rule(&b, p2, COMBINED); + return (memcmp(&a, &b, sizeof(a)) == 0); +} + + +/* + * Can a rule be included inside a superblock + */ +int +superblock_inclusive(struct superblock *block, struct pf_opt_rule *por) +{ + struct pf_rule a, b; + int i, j; + + /* First check for hard breaks */ + for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) { + if (pf_rule_desc[i].prf_type == BARRIER) { + for (j = 0; j < pf_rule_desc[i].prf_size; j++) + if (((char *)&por->por_rule)[j + + pf_rule_desc[i].prf_offset] != 0) + return (0); + } + } + + /* per-rule src-track is also a hard break */ + if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK) + return (0); + + /* + * Have to handle interface groups separately. Consider the following + * rules: + * block on EXTIFS to any port 22 + * pass on em0 to any port 22 + * (where EXTIFS is an arbitrary interface group) + * The optimizer may decide to re-order the pass rule in front of the + * block rule. But what if EXTIFS includes em0??? Such a reordering + * would change the meaning of the ruleset. + * We can't just lookup the EXTIFS group and check if em0 is a member + * because the user is allowed to add interfaces to a group during + * runtime. + * Ergo interface groups become a defacto superblock break :-( + */ + if (interface_group(por->por_rule.ifname) || + interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) { + if (strcasecmp(por->por_rule.ifname, + TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0) + return (0); + } + + comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE); + comparable_rule(&b, &por->por_rule, NOMERGE); + if (memcmp(&a, &b, sizeof(a)) == 0) + return (1); + +#ifdef OPT_DEBUG + for (i = 0; i < sizeof(por->por_rule); i++) { + int closest = -1; + if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) { + for (j = 0; j < sizeof(pf_rule_desc) / + sizeof(*pf_rule_desc); j++) { + if (i >= pf_rule_desc[j].prf_offset && + i < pf_rule_desc[j].prf_offset + + pf_rule_desc[j].prf_size) { + DEBUG("superblock break @ %d due to %s", + por->por_rule.nr, + pf_rule_desc[j].prf_name); + return (0); + } + if (i > pf_rule_desc[j].prf_offset) { + if (closest == -1 || + i-pf_rule_desc[j].prf_offset < + i-pf_rule_desc[closest].prf_offset) + closest = j; + } + } + + if (closest >= 0) + DEBUG("superblock break @ %d on %s+%xh", + por->por_rule.nr, + pf_rule_desc[closest].prf_name, + i - pf_rule_desc[closest].prf_offset - + pf_rule_desc[closest].prf_size); + else + DEBUG("superblock break @ %d on field @ %d", + por->por_rule.nr, i); + return (0); + } + } +#endif /* OPT_DEBUG */ + + return (0); +} + + +/* + * Figure out if an interface name is an actual interface or actually a + * group of interfaces. + */ +int +interface_group(const char *ifname) +{ + if (ifname == NULL || !ifname[0]) + return (0); + + /* Real interfaces must end in a number, interface groups do not */ + if (isdigit(ifname[strlen(ifname) - 1])) + return (0); + else + return (1); +} + + +/* + * Make a rule that can directly compared by memcmp() + */ +void +comparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type) +{ + int i; + /* + * To simplify the comparison, we just zero out the fields that are + * allowed to be different and then do a simple memcmp() + */ + memcpy(dst, src, sizeof(*dst)); + for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) + if (pf_rule_desc[i].prf_type >= type) { +#ifdef OPT_DEBUG + assert(pf_rule_desc[i].prf_type != NEVER || + *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0); +#endif /* OPT_DEBUG */ + memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0, + pf_rule_desc[i].prf_size); + } +} + + +/* + * Remove superset information from two rules so we can directly compare them + * with memcmp() + */ +void +exclude_supersets(struct pf_rule *super, struct pf_rule *sub) +{ + if (super->ifname[0] == '\0') + memset(sub->ifname, 0, sizeof(sub->ifname)); + if (super->direction == PF_INOUT) + sub->direction = PF_INOUT; + if ((super->proto == 0 || super->proto == sub->proto) && + super->flags == 0 && super->flagset == 0 && (sub->flags || + sub->flagset)) { + sub->flags = super->flags; + sub->flagset = super->flagset; + } + if (super->proto == 0) + sub->proto = 0; + + if (super->src.port_op == 0) { + sub->src.port_op = 0; + sub->src.port[0] = 0; + sub->src.port[1] = 0; + } + if (super->dst.port_op == 0) { + sub->dst.port_op = 0; + sub->dst.port[0] = 0; + sub->dst.port[1] = 0; + } + + if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg && + !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 && + super->src.addr.v.a.mask.addr32[1] == 0 && + super->src.addr.v.a.mask.addr32[2] == 0 && + super->src.addr.v.a.mask.addr32[3] == 0) + memset(&sub->src.addr, 0, sizeof(sub->src.addr)); + else if (super->src.addr.type == PF_ADDR_ADDRMASK && + sub->src.addr.type == PF_ADDR_ADDRMASK && + super->src.neg == sub->src.neg && + super->af == sub->af && + unmask(&super->src.addr.v.a.mask, super->af) < + unmask(&sub->src.addr.v.a.mask, sub->af) && + super->src.addr.v.a.addr.addr32[0] == + (sub->src.addr.v.a.addr.addr32[0] & + super->src.addr.v.a.mask.addr32[0]) && + super->src.addr.v.a.addr.addr32[1] == + (sub->src.addr.v.a.addr.addr32[1] & + super->src.addr.v.a.mask.addr32[1]) && + super->src.addr.v.a.addr.addr32[2] == + (sub->src.addr.v.a.addr.addr32[2] & + super->src.addr.v.a.mask.addr32[2]) && + super->src.addr.v.a.addr.addr32[3] == + (sub->src.addr.v.a.addr.addr32[3] & + super->src.addr.v.a.mask.addr32[3])) { + /* sub->src.addr is a subset of super->src.addr/mask */ + memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr)); + } + + if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg && + !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 && + super->dst.addr.v.a.mask.addr32[1] == 0 && + super->dst.addr.v.a.mask.addr32[2] == 0 && + super->dst.addr.v.a.mask.addr32[3] == 0) + memset(&sub->dst.addr, 0, sizeof(sub->dst.addr)); + else if (super->dst.addr.type == PF_ADDR_ADDRMASK && + sub->dst.addr.type == PF_ADDR_ADDRMASK && + super->dst.neg == sub->dst.neg && + super->af == sub->af && + unmask(&super->dst.addr.v.a.mask, super->af) < + unmask(&sub->dst.addr.v.a.mask, sub->af) && + super->dst.addr.v.a.addr.addr32[0] == + (sub->dst.addr.v.a.addr.addr32[0] & + super->dst.addr.v.a.mask.addr32[0]) && + super->dst.addr.v.a.addr.addr32[1] == + (sub->dst.addr.v.a.addr.addr32[1] & + super->dst.addr.v.a.mask.addr32[1]) && + super->dst.addr.v.a.addr.addr32[2] == + (sub->dst.addr.v.a.addr.addr32[2] & + super->dst.addr.v.a.mask.addr32[2]) && + super->dst.addr.v.a.addr.addr32[3] == + (sub->dst.addr.v.a.addr.addr32[3] & + super->dst.addr.v.a.mask.addr32[3])) { + /* sub->dst.addr is a subset of super->dst.addr/mask */ + memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr)); + } + + if (super->af == 0) + sub->af = 0; +} + + +void +superblock_free(struct pfctl *pf, struct superblock *block) +{ + struct pf_opt_rule *por; + while ((por = TAILQ_FIRST(&block->sb_rules))) { + TAILQ_REMOVE(&block->sb_rules, por, por_entry); + if (por->por_src_tbl) { + if (por->por_src_tbl->pt_buf) { + pfr_buf_clear(por->por_src_tbl->pt_buf); + free(por->por_src_tbl->pt_buf); + } + free(por->por_src_tbl); + } + if (por->por_dst_tbl) { + if (por->por_dst_tbl->pt_buf) { + pfr_buf_clear(por->por_dst_tbl->pt_buf); + free(por->por_dst_tbl->pt_buf); + } + free(por->por_dst_tbl); + } + free(por); + } + if (block->sb_profiled_block) + superblock_free(pf, block->sb_profiled_block); + free(block); +} + diff --git a/sbin/pfctl/pfctl_osfp.c b/sbin/pfctl/pfctl_osfp.c new file mode 100644 index 0000000000000..df789811ddb5e --- /dev/null +++ b/sbin/pfctl/pfctl_osfp.c @@ -0,0 +1,1108 @@ +/* $OpenBSD: pfctl_osfp.c,v 1.14 2006/04/08 02:13:14 ray Exp $ */ + +/* + * Copyright (c) 2003 Mike Frantzen + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif /* MAX */ + + +#if 0 +# define DEBUG(fp, str, v...) \ + fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \ + (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v); +#else +# define DEBUG(fp, str, v...) ((void)0) +#endif + + +struct name_entry; +LIST_HEAD(name_list, name_entry); +struct name_entry { + LIST_ENTRY(name_entry) nm_entry; + int nm_num; + char nm_name[PF_OSFP_LEN]; + + struct name_list nm_sublist; + int nm_sublist_num; +}; +struct name_list classes = LIST_HEAD_INITIALIZER(&classes); +int class_count; +int fingerprint_count; + +void add_fingerprint(int, int, struct pf_osfp_ioctl *); +struct name_entry *fingerprint_name_entry(struct name_list *, char *); +void pfctl_flush_my_fingerprints(struct name_list *); +char *get_field(char **, size_t *, int *); +int get_int(char **, size_t *, int *, int *, const char *, + int, int, const char *, int); +int get_str(char **, size_t *, char **, const char *, int, + const char *, int); +int get_tcpopts(const char *, int, const char *, + pf_tcpopts_t *, int *, int *, int *, int *, int *, + int *); +void import_fingerprint(struct pf_osfp_ioctl *); +const char *print_ioctl(struct pf_osfp_ioctl *); +void print_name_list(int, struct name_list *, const char *); +void sort_name_list(int, struct name_list *); +struct name_entry *lookup_name_list(struct name_list *, const char *); + +/* Load fingerprints from a file */ +int +pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) +{ + FILE *in; + char *line; + size_t len; + int i, lineno = 0; + int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale, + wscale_mod, optcnt, ts0; + pf_tcpopts_t packed_tcpopts; + char *class, *version, *subtype, *desc, *tcpopts; + struct pf_osfp_ioctl fp; + + pfctl_flush_my_fingerprints(&classes); + + if ((in = pfctl_fopen(fp_filename, "r")) == NULL) { + warn("%s", fp_filename); + return (1); + } + class = version = subtype = desc = tcpopts = NULL; + + if ((opts & PF_OPT_NOACTION) == 0) + pfctl_clear_fingerprints(dev, opts); + + while ((line = fgetln(in, &len)) != NULL) { + lineno++; + if (class) + free(class); + if (version) + free(version); + if (subtype) + free(subtype); + if (desc) + free(desc); + if (tcpopts) + free(tcpopts); + class = version = subtype = desc = tcpopts = NULL; + memset(&fp, 0, sizeof(fp)); + + /* Chop off comment */ + for (i = 0; i < len; i++) + if (line[i] == '#') { + len = i; + break; + } + /* Chop off whitespace */ + while (len > 0 && isspace(line[len - 1])) + len--; + while (len > 0 && isspace(line[0])) { + len--; + line++; + } + if (len == 0) + continue; + +#define T_DC 0x01 /* Allow don't care */ +#define T_MSS 0x02 /* Allow MSS multiple */ +#define T_MTU 0x04 /* Allow MTU multiple */ +#define T_MOD 0x08 /* Allow modulus */ + +#define GET_INT(v, mod, n, ty, mx) \ + get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno) +#define GET_STR(v, n, mn) \ + get_str(&line, &len, &v, n, mn, fp_filename, lineno) + + if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU| + T_MOD, 0xffff) || + GET_INT(ttl, NULL, "ttl", 0, 0xff) || + GET_INT(df, NULL, "don't fragment frag", 0, 1) || + GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC, + 8192) || + GET_STR(tcpopts, "TCP Options", 1) || + GET_STR(class, "OS class", 1) || + GET_STR(version, "OS version", 0) || + GET_STR(subtype, "OS subtype", 0) || + GET_STR(desc, "OS description", 2)) + continue; + if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts, + &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0)) + continue; + if (len != 0) { + fprintf(stderr, "%s:%d excess field\n", fp_filename, + lineno); + continue; + } + + fp.fp_ttl = ttl; + if (df) + fp.fp_flags |= PF_OSFP_DF; + switch (w_mod) { + case 0: + break; + case T_DC: + fp.fp_flags |= PF_OSFP_WSIZE_DC; + break; + case T_MSS: + fp.fp_flags |= PF_OSFP_WSIZE_MSS; + break; + case T_MTU: + fp.fp_flags |= PF_OSFP_WSIZE_MTU; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_WSIZE_MOD; + break; + } + fp.fp_wsize = window; + + switch (p_mod) { + case T_DC: + fp.fp_flags |= PF_OSFP_PSIZE_DC; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_PSIZE_MOD; + } + fp.fp_psize = psize; + + + switch (wscale_mod) { + case T_DC: + fp.fp_flags |= PF_OSFP_WSCALE_DC; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_WSCALE_MOD; + } + fp.fp_wscale = wscale; + + switch (mss_mod) { + case T_DC: + fp.fp_flags |= PF_OSFP_MSS_DC; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_MSS_MOD; + break; + } + fp.fp_mss = mss; + + fp.fp_tcpopts = packed_tcpopts; + fp.fp_optcnt = optcnt; + if (ts0) + fp.fp_flags |= PF_OSFP_TS0; + + if (class[0] == '@') + fp.fp_os.fp_enflags |= PF_OSFP_GENERIC; + if (class[0] == '*') + fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL; + + if (class[0] == '@' || class[0] == '*') + strlcpy(fp.fp_os.fp_class_nm, class + 1, + sizeof(fp.fp_os.fp_class_nm)); + else + strlcpy(fp.fp_os.fp_class_nm, class, + sizeof(fp.fp_os.fp_class_nm)); + strlcpy(fp.fp_os.fp_version_nm, version, + sizeof(fp.fp_os.fp_version_nm)); + strlcpy(fp.fp_os.fp_subtype_nm, subtype, + sizeof(fp.fp_os.fp_subtype_nm)); + + add_fingerprint(dev, opts, &fp); + + fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6); + fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip); + add_fingerprint(dev, opts, &fp); + } + + if (class) + free(class); + if (version) + free(version); + if (subtype) + free(subtype); + if (desc) + free(desc); + if (tcpopts) + free(tcpopts); + + fclose(in); + + if (opts & PF_OPT_VERBOSE2) + printf("Loaded %d passive OS fingerprints\n", + fingerprint_count); + return (0); +} + +/* flush the kernel's fingerprints */ +void +pfctl_clear_fingerprints(int dev, int opts) +{ + if (ioctl(dev, DIOCOSFPFLUSH)) + err(1, "DIOCOSFPFLUSH"); +} + +/* flush pfctl's view of the fingerprints */ +void +pfctl_flush_my_fingerprints(struct name_list *list) +{ + struct name_entry *nm; + + while ((nm = LIST_FIRST(list)) != NULL) { + LIST_REMOVE(nm, nm_entry); + pfctl_flush_my_fingerprints(&nm->nm_sublist); + free(nm); + } + fingerprint_count = 0; + class_count = 0; +} + +/* Fetch the active fingerprints from the kernel */ +int +pfctl_load_fingerprints(int dev, int opts) +{ + struct pf_osfp_ioctl io; + int i; + + pfctl_flush_my_fingerprints(&classes); + + for (i = 0; i >= 0; i++) { + memset(&io, 0, sizeof(io)); + io.fp_getnum = i; + if (ioctl(dev, DIOCOSFPGET, &io)) { + if (errno == EBUSY) + break; + warn("DIOCOSFPGET"); + return (1); + } + import_fingerprint(&io); + } + return (0); +} + +/* List the fingerprints */ +void +pfctl_show_fingerprints(int opts) +{ + if (LIST_FIRST(&classes) != NULL) { + if (opts & PF_OPT_SHOWALL) { + pfctl_print_title("OS FINGERPRINTS:"); + printf("%u fingerprints loaded\n", fingerprint_count); + } else { + printf("Class\tVersion\tSubtype(subversion)\n"); + printf("-----\t-------\t-------------------\n"); + sort_name_list(opts, &classes); + print_name_list(opts, &classes, ""); + } + } +} + +/* Lookup a fingerprint */ +pf_osfp_t +pfctl_get_fingerprint(const char *name) +{ + struct name_entry *nm, *class_nm, *version_nm, *subtype_nm; + pf_osfp_t ret = PF_OSFP_NOMATCH; + int class, version, subtype; + int unp_class, unp_version, unp_subtype; + int wr_len, version_len, subtype_len; + char *ptr, *wr_name; + + if (strcasecmp(name, "unknown") == 0) + return (PF_OSFP_UNKNOWN); + + /* Try most likely no version and no subtype */ + if ((nm = lookup_name_list(&classes, name))) { + class = nm->nm_num; + version = PF_OSFP_ANY; + subtype = PF_OSFP_ANY; + goto found; + } else { + + /* Chop it up into class/version/subtype */ + + if ((wr_name = strdup(name)) == NULL) + err(1, "malloc"); + if ((ptr = strchr(wr_name, ' ')) == NULL) { + free(wr_name); + return (PF_OSFP_NOMATCH); + } + *ptr++ = '\0'; + + /* The class is easy to find since it is delimited by a space */ + if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) { + free(wr_name); + return (PF_OSFP_NOMATCH); + } + class = class_nm->nm_num; + + /* Try no subtype */ + if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr))) + { + version = version_nm->nm_num; + subtype = PF_OSFP_ANY; + free(wr_name); + goto found; + } + + + /* + * There must be a version and a subtype. + * We'll do some fuzzy matching to pick up things like: + * Linux 2.2.14 (version=2.2 subtype=14) + * FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE) + * Windows 2000 SP2 (version=2000 subtype=SP2) + */ +#define CONNECTOR(x) ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-') + wr_len = strlen(ptr); + LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) { + version_len = strlen(version_nm->nm_name); + if (wr_len < version_len + 2 || + !CONNECTOR(ptr[version_len])) + continue; + /* first part of the string must be version */ + if (strncasecmp(ptr, version_nm->nm_name, + version_len)) + continue; + + LIST_FOREACH(subtype_nm, &version_nm->nm_sublist, + nm_entry) { + subtype_len = strlen(subtype_nm->nm_name); + if (wr_len != version_len + subtype_len + 1) + continue; + + /* last part of the string must be subtype */ + if (strcasecmp(&ptr[version_len+1], + subtype_nm->nm_name) != 0) + continue; + + /* Found it!! */ + version = version_nm->nm_num; + subtype = subtype_nm->nm_num; + free(wr_name); + goto found; + } + } + + free(wr_name); + return (PF_OSFP_NOMATCH); + } + +found: + PF_OSFP_PACK(ret, class, version, subtype); + if (ret != PF_OSFP_NOMATCH) { + PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype); + if (class != unp_class) { + fprintf(stderr, "warning: fingerprint table overflowed " + "classes\n"); + return (PF_OSFP_NOMATCH); + } + if (version != unp_version) { + fprintf(stderr, "warning: fingerprint table overflowed " + "versions\n"); + return (PF_OSFP_NOMATCH); + } + if (subtype != unp_subtype) { + fprintf(stderr, "warning: fingerprint table overflowed " + "subtypes\n"); + return (PF_OSFP_NOMATCH); + } + } + if (ret == PF_OSFP_ANY) { + /* should never happen */ + fprintf(stderr, "warning: fingerprint packed to 'any'\n"); + return (PF_OSFP_NOMATCH); + } + + return (ret); +} + +/* Lookup a fingerprint name by ID */ +char * +pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len) +{ + int class, version, subtype; + struct name_list *list; + struct name_entry *nm; + + char *class_name, *version_name, *subtype_name; + class_name = version_name = subtype_name = NULL; + + if (fp == PF_OSFP_UNKNOWN) { + strlcpy(buf, "unknown", len); + return (buf); + } + if (fp == PF_OSFP_ANY) { + strlcpy(buf, "any", len); + return (buf); + } + + PF_OSFP_UNPACK(fp, class, version, subtype); + if (class >= (1 << _FP_CLASS_BITS) || + version >= (1 << _FP_VERSION_BITS) || + subtype >= (1 << _FP_SUBTYPE_BITS)) { + warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp); + strlcpy(buf, "nomatch", len); + return (buf); + } + + LIST_FOREACH(nm, &classes, nm_entry) { + if (nm->nm_num == class) { + class_name = nm->nm_name; + if (version == PF_OSFP_ANY) + goto found; + list = &nm->nm_sublist; + LIST_FOREACH(nm, list, nm_entry) { + if (nm->nm_num == version) { + version_name = nm->nm_name; + if (subtype == PF_OSFP_ANY) + goto found; + list = &nm->nm_sublist; + LIST_FOREACH(nm, list, nm_entry) { + if (nm->nm_num == subtype) { + subtype_name = + nm->nm_name; + goto found; + } + } /* foreach subtype */ + strlcpy(buf, "nomatch", len); + return (buf); + } + } /* foreach version */ + strlcpy(buf, "nomatch", len); + return (buf); + } + } /* foreach class */ + + strlcpy(buf, "nomatch", len); + return (buf); + +found: + snprintf(buf, len, "%s", class_name); + if (version_name) { + strlcat(buf, " ", len); + strlcat(buf, version_name, len); + if (subtype_name) { + if (strchr(version_name, ' ')) + strlcat(buf, " ", len); + else if (strchr(version_name, '.') && + isdigit(*subtype_name)) + strlcat(buf, ".", len); + else + strlcat(buf, " ", len); + strlcat(buf, subtype_name, len); + } + } + return (buf); +} + +/* lookup a name in a list */ +struct name_entry * +lookup_name_list(struct name_list *list, const char *name) +{ + struct name_entry *nm; + LIST_FOREACH(nm, list, nm_entry) + if (strcasecmp(name, nm->nm_name) == 0) + return (nm); + + return (NULL); +} + + +void +add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp) +{ + struct pf_osfp_ioctl fptmp; + struct name_entry *nm_class, *nm_version, *nm_subtype; + int class, version, subtype; + +/* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */ +#define EXPAND(field) do { \ + int _dot = -1, _start = -1, _end = -1, _i = 0; \ + /* pick major version out of #.# */ \ + if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') { \ + _dot = fp->field[_i] - '0'; \ + _i += 2; \ + } \ + if (isdigit(fp->field[_i])) \ + _start = fp->field[_i++] - '0'; \ + else \ + break; \ + if (isdigit(fp->field[_i])) \ + _start = (_start * 10) + fp->field[_i++] - '0'; \ + if (fp->field[_i++] != '-') \ + break; \ + if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' && \ + fp->field[_i] - '0' == _dot) \ + _i += 2; \ + else if (_dot != -1) \ + break; \ + if (isdigit(fp->field[_i])) \ + _end = fp->field[_i++] - '0'; \ + else \ + break; \ + if (isdigit(fp->field[_i])) \ + _end = (_end * 10) + fp->field[_i++] - '0'; \ + if (isdigit(fp->field[_i])) \ + _end = (_end * 10) + fp->field[_i++] - '0'; \ + if (fp->field[_i] != '\0') \ + break; \ + memcpy(&fptmp, fp, sizeof(fptmp)); \ + for (;_start <= _end; _start++) { \ + memset(fptmp.field, 0, sizeof(fptmp.field)); \ + fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED; \ + if (_dot == -1) \ + snprintf(fptmp.field, sizeof(fptmp.field), \ + "%d", _start); \ + else \ + snprintf(fptmp.field, sizeof(fptmp.field), \ + "%d.%d", _dot, _start); \ + add_fingerprint(dev, opts, &fptmp); \ + } \ +} while(0) + + /* We allow "#-#" as a version or subtype and we'll expand it */ + EXPAND(fp_os.fp_version_nm); + EXPAND(fp_os.fp_subtype_nm); + + if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0) + errx(1, "fingerprint class \"nomatch\" is reserved"); + + version = PF_OSFP_ANY; + subtype = PF_OSFP_ANY; + + nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); + if (nm_class->nm_num == 0) + nm_class->nm_num = ++class_count; + class = nm_class->nm_num; + + nm_version = fingerprint_name_entry(&nm_class->nm_sublist, + fp->fp_os.fp_version_nm); + if (nm_version) { + if (nm_version->nm_num == 0) + nm_version->nm_num = ++nm_class->nm_sublist_num; + version = nm_version->nm_num; + nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, + fp->fp_os.fp_subtype_nm); + if (nm_subtype) { + if (nm_subtype->nm_num == 0) + nm_subtype->nm_num = + ++nm_version->nm_sublist_num; + subtype = nm_subtype->nm_num; + } + } + + + DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype, + print_ioctl(fp)); + + PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype); + fingerprint_count++; + +#ifdef FAKE_PF_KERNEL + /* Linked to the sys/net/pf_osfp.c. Call pf_osfp_add() */ + if ((errno = pf_osfp_add(fp))) +#else + if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp)) +#endif /* FAKE_PF_KERNEL */ + { + if (errno == EEXIST) { + warn("Duplicate signature for %s %s %s", + fp->fp_os.fp_class_nm, + fp->fp_os.fp_version_nm, + fp->fp_os.fp_subtype_nm); + + } else { + err(1, "DIOCOSFPADD"); + } + } +} + +/* import a fingerprint from the kernel */ +void +import_fingerprint(struct pf_osfp_ioctl *fp) +{ + struct name_entry *nm_class, *nm_version, *nm_subtype; + int class, version, subtype; + + PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype); + + nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); + if (nm_class->nm_num == 0) { + nm_class->nm_num = class; + class_count = MAX(class_count, class); + } + + nm_version = fingerprint_name_entry(&nm_class->nm_sublist, + fp->fp_os.fp_version_nm); + if (nm_version) { + if (nm_version->nm_num == 0) { + nm_version->nm_num = version; + nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num, + version); + } + nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, + fp->fp_os.fp_subtype_nm); + if (nm_subtype) { + if (nm_subtype->nm_num == 0) { + nm_subtype->nm_num = subtype; + nm_version->nm_sublist_num = + MAX(nm_version->nm_sublist_num, subtype); + } + } + } + + + fingerprint_count++; + DEBUG(fp, "import signature %d:%d:%d", class, version, subtype); +} + +/* Find an entry for a fingerprints class/version/subtype */ +struct name_entry * +fingerprint_name_entry(struct name_list *list, char *name) +{ + struct name_entry *nm_entry; + + if (name == NULL || strlen(name) == 0) + return (NULL); + + LIST_FOREACH(nm_entry, list, nm_entry) { + if (strcasecmp(nm_entry->nm_name, name) == 0) { + /* We'll move this to the front of the list later */ + LIST_REMOVE(nm_entry, nm_entry); + break; + } + } + if (nm_entry == NULL) { + nm_entry = calloc(1, sizeof(*nm_entry)); + if (nm_entry == NULL) + err(1, "calloc"); + LIST_INIT(&nm_entry->nm_sublist); + strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name)); + } + LIST_INSERT_HEAD(list, nm_entry, nm_entry); + return (nm_entry); +} + + +void +print_name_list(int opts, struct name_list *nml, const char *prefix) +{ + char newprefix[32]; + struct name_entry *nm; + + LIST_FOREACH(nm, nml, nm_entry) { + snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix, + nm->nm_name); + printf("%s\n", newprefix); + print_name_list(opts, &nm->nm_sublist, newprefix); + } +} + +void +sort_name_list(int opts, struct name_list *nml) +{ + struct name_list new; + struct name_entry *nm, *nmsearch, *nmlast; + + /* yes yes, it's a very slow sort. so sue me */ + + LIST_INIT(&new); + + while ((nm = LIST_FIRST(nml)) != NULL) { + LIST_REMOVE(nm, nm_entry); + nmlast = NULL; + LIST_FOREACH(nmsearch, &new, nm_entry) { + if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) { + LIST_INSERT_BEFORE(nmsearch, nm, nm_entry); + break; + } + nmlast = nmsearch; + } + if (nmsearch == NULL) { + if (nmlast) + LIST_INSERT_AFTER(nmlast, nm, nm_entry); + else + LIST_INSERT_HEAD(&new, nm, nm_entry); + } + + sort_name_list(opts, &nm->nm_sublist); + } + nmlast = NULL; + while ((nm = LIST_FIRST(&new)) != NULL) { + LIST_REMOVE(nm, nm_entry); + if (nmlast == NULL) + LIST_INSERT_HEAD(nml, nm, nm_entry); + else + LIST_INSERT_AFTER(nmlast, nm, nm_entry); + nmlast = nm; + } +} + +/* parse the next integer in a formatted config file line */ +int +get_int(char **line, size_t *len, int *var, int *mod, + const char *name, int flags, int max, const char *filename, int lineno) +{ + int fieldlen, i; + char *field; + long val = 0; + + if (mod) + *mod = 0; + *var = 0; + + field = get_field(line, len, &fieldlen); + if (field == NULL) + return (1); + if (fieldlen == 0) { + fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name); + return (1); + } + + i = 0; + if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*') + && fieldlen >= 1) { + switch (*field) { + case 'S': + if (mod && (flags & T_MSS)) + *mod = T_MSS; + if (fieldlen == 1) + return (0); + break; + case 'T': + if (mod && (flags & T_MTU)) + *mod = T_MTU; + if (fieldlen == 1) + return (0); + break; + case '*': + if (fieldlen != 1) { + fprintf(stderr, "%s:%d long '%c' %s\n", + filename, lineno, *field, name); + return (1); + } + if (mod && (flags & T_DC)) { + *mod = T_DC; + return (0); + } + case '%': + if (mod && (flags & T_MOD)) + *mod = T_MOD; + if (fieldlen == 1) { + fprintf(stderr, "%s:%d modulus %s must have a " + "value\n", filename, lineno, name); + return (1); + } + break; + } + if (mod == NULL || *mod == 0) { + fprintf(stderr, "%s:%d does not allow %c' %s\n", + filename, lineno, *field, name); + return (1); + } + i++; + } + + for (; i < fieldlen; i++) { + if (field[i] < '0' || field[i] > '9') { + fprintf(stderr, "%s:%d non-digit character in %s\n", + filename, lineno, name); + return (1); + } + val = val * 10 + field[i] - '0'; + if (val < 0) { + fprintf(stderr, "%s:%d %s overflowed\n", filename, + lineno, name); + return (1); + } + } + + if (val > max) { + fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno, + name, val, max); + return (1); + } + *var = (int)val; + + return (0); +} + +/* parse the next string in a formatted config file line */ +int +get_str(char **line, size_t *len, char **v, const char *name, int minlen, + const char *filename, int lineno) +{ + int fieldlen; + char *ptr; + + ptr = get_field(line, len, &fieldlen); + if (ptr == NULL) + return (1); + if (fieldlen < minlen) { + fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name); + return (1); + } + if ((*v = malloc(fieldlen + 1)) == NULL) { + perror("malloc()"); + return (1); + } + memcpy(*v, ptr, fieldlen); + (*v)[fieldlen] = '\0'; + + return (0); +} + +/* Parse out the TCP opts */ +int +get_tcpopts(const char *filename, int lineno, const char *tcpopts, + pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale, + int *wscale_mod, int *ts0) +{ + int i, opt; + + *packed = 0; + *optcnt = 0; + *wscale = 0; + *wscale_mod = T_DC; + *mss = 0; + *mss_mod = T_DC; + *ts0 = 0; + if (strcmp(tcpopts, ".") == 0) + return (0); + + for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) { + switch ((opt = toupper(tcpopts[i++]))) { + case 'N': /* FALLTHROUGH */ + case 'S': + *packed = (*packed << PF_OSFP_TCPOPT_BITS) | + (opt == 'N' ? PF_OSFP_TCPOPT_NOP : + PF_OSFP_TCPOPT_SACK); + break; + case 'W': /* FALLTHROUGH */ + case 'M': { + int *this_mod, *this; + + if (opt == 'W') { + this = wscale; + this_mod = wscale_mod; + } else { + this = mss; + this_mod = mss_mod; + } + *this = 0; + *this_mod = 0; + + *packed = (*packed << PF_OSFP_TCPOPT_BITS) | + (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE : + PF_OSFP_TCPOPT_MSS); + if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' || + tcpopts[i + 1] == ',')) { + *this_mod = T_DC; + i++; + break; + } + + if (tcpopts[i] == '%') { + *this_mod = T_MOD; + i++; + } + do { + if (!isdigit(tcpopts[i])) { + fprintf(stderr, "%s:%d unknown " + "character '%c' in %c TCP opt\n", + filename, lineno, tcpopts[i], opt); + return (1); + } + *this = (*this * 10) + tcpopts[i++] - '0'; + } while(tcpopts[i] != ',' && tcpopts[i] != '\0'); + break; + } + case 'T': + if (tcpopts[i] == '0') { + *ts0 = 1; + i++; + } + *packed = (*packed << PF_OSFP_TCPOPT_BITS) | + PF_OSFP_TCPOPT_TS; + break; + } + (*optcnt) ++; + if (tcpopts[i] == '\0') + break; + if (tcpopts[i] != ',') { + fprintf(stderr, "%s:%d unknown option to %c TCP opt\n", + filename, lineno, opt); + return (1); + } + i++; + } + + return (0); +} + +/* rip the next field ouf of a formatted config file line */ +char * +get_field(char **line, size_t *len, int *fieldlen) +{ + char *ret, *ptr = *line; + size_t plen = *len; + + + while (plen && isspace(*ptr)) { + plen--; + ptr++; + } + ret = ptr; + *fieldlen = 0; + + for (; plen > 0 && *ptr != ':'; plen--, ptr++) + (*fieldlen)++; + if (plen) { + *line = ptr + 1; + *len = plen - 1; + } else { + *len = 0; + } + while (*fieldlen && isspace(ret[*fieldlen - 1])) + (*fieldlen)--; + return (ret); +} + + +const char * +print_ioctl(struct pf_osfp_ioctl *fp) +{ + static char buf[1024]; + char tmp[32]; + int i, opt; + + *buf = '\0'; + if (fp->fp_flags & PF_OSFP_WSIZE_DC) + strlcat(buf, "*", sizeof(buf)); + else if (fp->fp_flags & PF_OSFP_WSIZE_MSS) + strlcat(buf, "S", sizeof(buf)); + else if (fp->fp_flags & PF_OSFP_WSIZE_MTU) + strlcat(buf, "T", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_WSIZE_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize); + strlcat(buf, tmp, sizeof(buf)); + } + strlcat(buf, ":", sizeof(buf)); + + snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl); + strlcat(buf, tmp, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + + if (fp->fp_flags & PF_OSFP_DF) + strlcat(buf, "1", sizeof(buf)); + else + strlcat(buf, "0", sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + + if (fp->fp_flags & PF_OSFP_PSIZE_DC) + strlcat(buf, "*", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_PSIZE_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize); + strlcat(buf, tmp, sizeof(buf)); + } + strlcat(buf, ":", sizeof(buf)); + + if (fp->fp_optcnt == 0) + strlcat(buf, ".", sizeof(buf)); + for (i = fp->fp_optcnt - 1; i >= 0; i--) { + opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS); + opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1; + switch (opt) { + case PF_OSFP_TCPOPT_NOP: + strlcat(buf, "N", sizeof(buf)); + break; + case PF_OSFP_TCPOPT_SACK: + strlcat(buf, "S", sizeof(buf)); + break; + case PF_OSFP_TCPOPT_TS: + strlcat(buf, "T", sizeof(buf)); + if (fp->fp_flags & PF_OSFP_TS0) + strlcat(buf, "0", sizeof(buf)); + break; + case PF_OSFP_TCPOPT_MSS: + strlcat(buf, "M", sizeof(buf)); + if (fp->fp_flags & PF_OSFP_MSS_DC) + strlcat(buf, "*", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_MSS_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss); + strlcat(buf, tmp, sizeof(buf)); + } + break; + case PF_OSFP_TCPOPT_WSCALE: + strlcat(buf, "W", sizeof(buf)); + if (fp->fp_flags & PF_OSFP_WSCALE_DC) + strlcat(buf, "*", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_WSCALE_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale); + strlcat(buf, tmp, sizeof(buf)); + } + break; + } + + if (i != 0) + strlcat(buf, ",", sizeof(buf)); + } + strlcat(buf, ":", sizeof(buf)); + + strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + + snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt, + (long long int)fp->fp_tcpopts); + strlcat(buf, tmp, sizeof(buf)); + + return (buf); +} diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c new file mode 100644 index 0000000000000..f2489956daa46 --- /dev/null +++ b/sbin/pfctl/pfctl_parser.c @@ -0,0 +1,1746 @@ +/* $OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002,2003 Henning Brauer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +void print_op (u_int8_t, const char *, const char *); +void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); +void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); +void print_flags (u_int8_t); +void print_fromto(struct pf_rule_addr *, pf_osfp_t, + struct pf_rule_addr *, u_int8_t, u_int8_t, int, int); +int ifa_skip_if(const char *filter, struct node_host *p); + +struct node_host *ifa_grouplookup(const char *, int); +struct node_host *host_if(const char *, int); +struct node_host *host_v4(const char *, int); +struct node_host *host_v6(const char *, int); +struct node_host *host_dns(const char *, int, int); + +const char *tcpflags = "FSRPAUEW"; + +static const struct icmptypeent icmp_type[] = { + { "echoreq", ICMP_ECHO }, + { "echorep", ICMP_ECHOREPLY }, + { "unreach", ICMP_UNREACH }, + { "squench", ICMP_SOURCEQUENCH }, + { "redir", ICMP_REDIRECT }, + { "althost", ICMP_ALTHOSTADDR }, + { "routeradv", ICMP_ROUTERADVERT }, + { "routersol", ICMP_ROUTERSOLICIT }, + { "timex", ICMP_TIMXCEED }, + { "paramprob", ICMP_PARAMPROB }, + { "timereq", ICMP_TSTAMP }, + { "timerep", ICMP_TSTAMPREPLY }, + { "inforeq", ICMP_IREQ }, + { "inforep", ICMP_IREQREPLY }, + { "maskreq", ICMP_MASKREQ }, + { "maskrep", ICMP_MASKREPLY }, + { "trace", ICMP_TRACEROUTE }, + { "dataconv", ICMP_DATACONVERR }, + { "mobredir", ICMP_MOBILE_REDIRECT }, + { "ipv6-where", ICMP_IPV6_WHEREAREYOU }, + { "ipv6-here", ICMP_IPV6_IAMHERE }, + { "mobregreq", ICMP_MOBILE_REGREQUEST }, + { "mobregrep", ICMP_MOBILE_REGREPLY }, + { "skip", ICMP_SKIP }, + { "photuris", ICMP_PHOTURIS } +}; + +static const struct icmptypeent icmp6_type[] = { + { "unreach", ICMP6_DST_UNREACH }, + { "toobig", ICMP6_PACKET_TOO_BIG }, + { "timex", ICMP6_TIME_EXCEEDED }, + { "paramprob", ICMP6_PARAM_PROB }, + { "echoreq", ICMP6_ECHO_REQUEST }, + { "echorep", ICMP6_ECHO_REPLY }, + { "groupqry", ICMP6_MEMBERSHIP_QUERY }, + { "listqry", MLD_LISTENER_QUERY }, + { "grouprep", ICMP6_MEMBERSHIP_REPORT }, + { "listenrep", MLD_LISTENER_REPORT }, + { "groupterm", ICMP6_MEMBERSHIP_REDUCTION }, + { "listendone", MLD_LISTENER_DONE }, + { "routersol", ND_ROUTER_SOLICIT }, + { "routeradv", ND_ROUTER_ADVERT }, + { "neighbrsol", ND_NEIGHBOR_SOLICIT }, + { "neighbradv", ND_NEIGHBOR_ADVERT }, + { "redir", ND_REDIRECT }, + { "routrrenum", ICMP6_ROUTER_RENUMBERING }, + { "wrureq", ICMP6_WRUREQUEST }, + { "wrurep", ICMP6_WRUREPLY }, + { "fqdnreq", ICMP6_FQDN_QUERY }, + { "fqdnrep", ICMP6_FQDN_REPLY }, + { "niqry", ICMP6_NI_QUERY }, + { "nirep", ICMP6_NI_REPLY }, + { "mtraceresp", MLD_MTRACE_RESP }, + { "mtrace", MLD_MTRACE } +}; + +static const struct icmpcodeent icmp_code[] = { + { "net-unr", ICMP_UNREACH, ICMP_UNREACH_NET }, + { "host-unr", ICMP_UNREACH, ICMP_UNREACH_HOST }, + { "proto-unr", ICMP_UNREACH, ICMP_UNREACH_PROTOCOL }, + { "port-unr", ICMP_UNREACH, ICMP_UNREACH_PORT }, + { "needfrag", ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG }, + { "srcfail", ICMP_UNREACH, ICMP_UNREACH_SRCFAIL }, + { "net-unk", ICMP_UNREACH, ICMP_UNREACH_NET_UNKNOWN }, + { "host-unk", ICMP_UNREACH, ICMP_UNREACH_HOST_UNKNOWN }, + { "isolate", ICMP_UNREACH, ICMP_UNREACH_ISOLATED }, + { "net-prohib", ICMP_UNREACH, ICMP_UNREACH_NET_PROHIB }, + { "host-prohib", ICMP_UNREACH, ICMP_UNREACH_HOST_PROHIB }, + { "net-tos", ICMP_UNREACH, ICMP_UNREACH_TOSNET }, + { "host-tos", ICMP_UNREACH, ICMP_UNREACH_TOSHOST }, + { "filter-prohib", ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB }, + { "host-preced", ICMP_UNREACH, ICMP_UNREACH_HOST_PRECEDENCE }, + { "cutoff-preced", ICMP_UNREACH, ICMP_UNREACH_PRECEDENCE_CUTOFF }, + { "redir-net", ICMP_REDIRECT, ICMP_REDIRECT_NET }, + { "redir-host", ICMP_REDIRECT, ICMP_REDIRECT_HOST }, + { "redir-tos-net", ICMP_REDIRECT, ICMP_REDIRECT_TOSNET }, + { "redir-tos-host", ICMP_REDIRECT, ICMP_REDIRECT_TOSHOST }, + { "normal-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NORMAL }, + { "common-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NOROUTE_COMMON }, + { "transit", ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS }, + { "reassemb", ICMP_TIMXCEED, ICMP_TIMXCEED_REASS }, + { "badhead", ICMP_PARAMPROB, ICMP_PARAMPROB_ERRATPTR }, + { "optmiss", ICMP_PARAMPROB, ICMP_PARAMPROB_OPTABSENT }, + { "badlen", ICMP_PARAMPROB, ICMP_PARAMPROB_LENGTH }, + { "unknown-ind", ICMP_PHOTURIS, ICMP_PHOTURIS_UNKNOWN_INDEX }, + { "auth-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_AUTH_FAILED }, + { "decrypt-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_DECRYPT_FAILED } +}; + +static const struct icmpcodeent icmp6_code[] = { + { "admin-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN }, + { "noroute-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE }, + { "notnbr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOTNEIGHBOR }, + { "beyond-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE }, + { "addr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR }, + { "port-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT }, + { "transit", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT }, + { "reassemb", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY }, + { "badhead", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER }, + { "nxthdr", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER }, + { "redironlink", ND_REDIRECT, ND_REDIRECT_ONLINK }, + { "redirrouter", ND_REDIRECT, ND_REDIRECT_ROUTER } +}; + +const struct pf_timeout pf_timeouts[] = { + { "tcp.first", PFTM_TCP_FIRST_PACKET }, + { "tcp.opening", PFTM_TCP_OPENING }, + { "tcp.established", PFTM_TCP_ESTABLISHED }, + { "tcp.closing", PFTM_TCP_CLOSING }, + { "tcp.finwait", PFTM_TCP_FIN_WAIT }, + { "tcp.closed", PFTM_TCP_CLOSED }, + { "tcp.tsdiff", PFTM_TS_DIFF }, + { "udp.first", PFTM_UDP_FIRST_PACKET }, + { "udp.single", PFTM_UDP_SINGLE }, + { "udp.multiple", PFTM_UDP_MULTIPLE }, + { "icmp.first", PFTM_ICMP_FIRST_PACKET }, + { "icmp.error", PFTM_ICMP_ERROR_REPLY }, + { "other.first", PFTM_OTHER_FIRST_PACKET }, + { "other.single", PFTM_OTHER_SINGLE }, + { "other.multiple", PFTM_OTHER_MULTIPLE }, + { "frag", PFTM_FRAG }, + { "interval", PFTM_INTERVAL }, + { "adaptive.start", PFTM_ADAPTIVE_START }, + { "adaptive.end", PFTM_ADAPTIVE_END }, + { "src.track", PFTM_SRC_NODE }, + { NULL, 0 } +}; + +const struct icmptypeent * +geticmptypebynumber(u_int8_t type, sa_family_t af) +{ + unsigned int i; + + if (af != AF_INET6) { + for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0])); + i++) { + if (type == icmp_type[i].type) + return (&icmp_type[i]); + } + } else { + for (i=0; i < (sizeof (icmp6_type) / + sizeof(icmp6_type[0])); i++) { + if (type == icmp6_type[i].type) + return (&icmp6_type[i]); + } + } + return (NULL); +} + +const struct icmptypeent * +geticmptypebyname(char *w, sa_family_t af) +{ + unsigned int i; + + if (af != AF_INET6) { + for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0])); + i++) { + if (!strcmp(w, icmp_type[i].name)) + return (&icmp_type[i]); + } + } else { + for (i=0; i < (sizeof (icmp6_type) / + sizeof(icmp6_type[0])); i++) { + if (!strcmp(w, icmp6_type[i].name)) + return (&icmp6_type[i]); + } + } + return (NULL); +} + +const struct icmpcodeent * +geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af) +{ + unsigned int i; + + if (af != AF_INET6) { + for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0])); + i++) { + if (type == icmp_code[i].type && + code == icmp_code[i].code) + return (&icmp_code[i]); + } + } else { + for (i=0; i < (sizeof (icmp6_code) / + sizeof(icmp6_code[0])); i++) { + if (type == icmp6_code[i].type && + code == icmp6_code[i].code) + return (&icmp6_code[i]); + } + } + return (NULL); +} + +const struct icmpcodeent * +geticmpcodebyname(u_long type, char *w, sa_family_t af) +{ + unsigned int i; + + if (af != AF_INET6) { + for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0])); + i++) { + if (type == icmp_code[i].type && + !strcmp(w, icmp_code[i].name)) + return (&icmp_code[i]); + } + } else { + for (i=0; i < (sizeof (icmp6_code) / + sizeof(icmp6_code[0])); i++) { + if (type == icmp6_code[i].type && + !strcmp(w, icmp6_code[i].name)) + return (&icmp6_code[i]); + } + } + return (NULL); +} + +void +print_op(u_int8_t op, const char *a1, const char *a2) +{ + if (op == PF_OP_IRG) + printf(" %s >< %s", a1, a2); + else if (op == PF_OP_XRG) + printf(" %s <> %s", a1, a2); + else if (op == PF_OP_EQ) + printf(" = %s", a1); + else if (op == PF_OP_NE) + printf(" != %s", a1); + else if (op == PF_OP_LT) + printf(" < %s", a1); + else if (op == PF_OP_LE) + printf(" <= %s", a1); + else if (op == PF_OP_GT) + printf(" > %s", a1); + else if (op == PF_OP_GE) + printf(" >= %s", a1); + else if (op == PF_OP_RRG) + printf(" %s:%s", a1, a2); +} + +void +print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numeric) +{ + char a1[6], a2[6]; + struct servent *s; + + if (!numeric) + s = getservbyport(p1, proto); + else + s = NULL; + p1 = ntohs(p1); + p2 = ntohs(p2); + snprintf(a1, sizeof(a1), "%u", p1); + snprintf(a2, sizeof(a2), "%u", p2); + printf(" port"); + if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE)) + print_op(op, s->s_name, a2); + else + print_op(op, a1, a2); +} + +void +print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) +{ + char a1[11], a2[11]; + + snprintf(a1, sizeof(a1), "%u", u1); + snprintf(a2, sizeof(a2), "%u", u2); + printf(" %s", t); + if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE)) + print_op(op, "unknown", a2); + else + print_op(op, a1, a2); +} + +void +print_flags(u_int8_t f) +{ + int i; + + for (i = 0; tcpflags[i]; ++i) + if (f & (1 << i)) + printf("%c", tcpflags[i]); +} + +void +print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst, + sa_family_t af, u_int8_t proto, int verbose, int numeric) +{ + char buf[PF_OSFP_LEN*3]; + if (src->addr.type == PF_ADDR_ADDRMASK && + dst->addr.type == PF_ADDR_ADDRMASK && + PF_AZERO(&src->addr.v.a.addr, AF_INET6) && + PF_AZERO(&src->addr.v.a.mask, AF_INET6) && + PF_AZERO(&dst->addr.v.a.addr, AF_INET6) && + PF_AZERO(&dst->addr.v.a.mask, AF_INET6) && + !src->neg && !dst->neg && + !src->port_op && !dst->port_op && + osfp == PF_OSFP_ANY) + printf(" all"); + else { + printf(" from "); + if (src->neg) + printf("! "); + print_addr(&src->addr, af, verbose); + if (src->port_op) + print_port(src->port_op, src->port[0], + src->port[1], + proto == IPPROTO_TCP ? "tcp" : "udp", + numeric); + if (osfp != PF_OSFP_ANY) + printf(" os \"%s\"", pfctl_lookup_fingerprint(osfp, buf, + sizeof(buf))); + + printf(" to "); + if (dst->neg) + printf("! "); + print_addr(&dst->addr, af, verbose); + if (dst->port_op) + print_port(dst->port_op, dst->port[0], + dst->port[1], + proto == IPPROTO_TCP ? "tcp" : "udp", + numeric); + } +} + +void +print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2, + sa_family_t af, int id) +{ + struct pf_pooladdr *pooladdr; + + if ((TAILQ_FIRST(&pool->list) != NULL) && + TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) + printf("{ "); + TAILQ_FOREACH(pooladdr, &pool->list, entries){ + switch (id) { + case PF_NAT: + case PF_RDR: + case PF_BINAT: + print_addr(&pooladdr->addr, af, 0); + break; + case PF_PASS: + if (PF_AZERO(&pooladdr->addr.v.a.addr, af)) + printf("%s", pooladdr->ifname); + else { + printf("(%s ", pooladdr->ifname); + print_addr(&pooladdr->addr, af, 0); + printf(")"); + } + break; + default: + break; + } + if (TAILQ_NEXT(pooladdr, entries) != NULL) + printf(", "); + else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) + printf(" }"); + } + switch (id) { + case PF_NAT: + if ((p1 != PF_NAT_PROXY_PORT_LOW || + p2 != PF_NAT_PROXY_PORT_HIGH) && (p1 != 0 || p2 != 0)) { + if (p1 == p2) + printf(" port %u", p1); + else + printf(" port %u:%u", p1, p2); + } + break; + case PF_RDR: + if (p1) { + printf(" port %u", p1); + if (p2 && (p2 != p1)) + printf(":%u", p2); + } + break; + default: + break; + } + switch (pool->opts & PF_POOL_TYPEMASK) { + case PF_POOL_NONE: + break; + case PF_POOL_BITMASK: + printf(" bitmask"); + break; + case PF_POOL_RANDOM: + printf(" random"); + break; + case PF_POOL_SRCHASH: + printf(" source-hash 0x%08x%08x%08x%08x", + pool->key.key32[0], pool->key.key32[1], + pool->key.key32[2], pool->key.key32[3]); + break; + case PF_POOL_ROUNDROBIN: + printf(" round-robin"); + break; + } + if (pool->opts & PF_POOL_STICKYADDR) + printf(" sticky-address"); + if (id == PF_NAT && p1 == 0 && p2 == 0) + printf(" static-port"); +} + +const char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES; +const char *pf_lcounters[LCNT_MAX+1] = LCNT_NAMES; +const char *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES; +const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES; + +void +print_status(struct pf_status *s, int opts) +{ + char statline[80], *running; + time_t runtime; + int i; + char buf[PF_MD5_DIGEST_LENGTH * 2 + 1]; + static const char hex[] = "0123456789abcdef"; + + runtime = time(NULL) - s->since; + running = s->running ? "Enabled" : "Disabled"; + + if (s->since) { + unsigned int sec, min, hrs, day = runtime; + + sec = day % 60; + day /= 60; + min = day % 60; + day /= 60; + hrs = day % 24; + day /= 24; + snprintf(statline, sizeof(statline), + "Status: %s for %u days %.2u:%.2u:%.2u", + running, day, hrs, min, sec); + } else + snprintf(statline, sizeof(statline), "Status: %s", running); + printf("%-44s", statline); + switch (s->debug) { + case PF_DEBUG_NONE: + printf("%15s\n\n", "Debug: None"); + break; + case PF_DEBUG_URGENT: + printf("%15s\n\n", "Debug: Urgent"); + break; + case PF_DEBUG_MISC: + printf("%15s\n\n", "Debug: Misc"); + break; + case PF_DEBUG_NOISY: + printf("%15s\n\n", "Debug: Loud"); + break; + } + + if (opts & PF_OPT_VERBOSE) { + printf("Hostid: 0x%08x\n", ntohl(s->hostid)); + + for (i = 0; i < PF_MD5_DIGEST_LENGTH; i++) { + buf[i + i] = hex[s->pf_chksum[i] >> 4]; + buf[i + i + 1] = hex[s->pf_chksum[i] & 0x0f]; + } + buf[i + i] = '\0'; + printf("Checksum: 0x%s\n\n", buf); + } + + if (s->ifname[0] != 0) { + printf("Interface Stats for %-16s %5s %16s\n", + s->ifname, "IPv4", "IPv6"); + printf(" %-25s %14llu %16llu\n", "Bytes In", + (unsigned long long)s->bcounters[0][0], + (unsigned long long)s->bcounters[1][0]); + printf(" %-25s %14llu %16llu\n", "Bytes Out", + (unsigned long long)s->bcounters[0][1], + (unsigned long long)s->bcounters[1][1]); + printf(" Packets In\n"); + printf(" %-23s %14llu %16llu\n", "Passed", + (unsigned long long)s->pcounters[0][0][PF_PASS], + (unsigned long long)s->pcounters[1][0][PF_PASS]); + printf(" %-23s %14llu %16llu\n", "Blocked", + (unsigned long long)s->pcounters[0][0][PF_DROP], + (unsigned long long)s->pcounters[1][0][PF_DROP]); + printf(" Packets Out\n"); + printf(" %-23s %14llu %16llu\n", "Passed", + (unsigned long long)s->pcounters[0][1][PF_PASS], + (unsigned long long)s->pcounters[1][1][PF_PASS]); + printf(" %-23s %14llu %16llu\n\n", "Blocked", + (unsigned long long)s->pcounters[0][1][PF_DROP], + (unsigned long long)s->pcounters[1][1][PF_DROP]); + } + printf("%-27s %14s %16s\n", "State Table", "Total", "Rate"); + printf(" %-25s %14u %14s\n", "current entries", s->states, ""); + for (i = 0; i < FCNT_MAX; i++) { + printf(" %-25s %14llu ", pf_fcounters[i], + (unsigned long long)s->fcounters[i]); + if (runtime > 0) + printf("%14.1f/s\n", + (double)s->fcounters[i] / (double)runtime); + else + printf("%14s\n", ""); + } + if (opts & PF_OPT_VERBOSE) { + printf("Source Tracking Table\n"); + printf(" %-25s %14u %14s\n", "current entries", + s->src_nodes, ""); + for (i = 0; i < SCNT_MAX; i++) { + printf(" %-25s %14lld ", pf_scounters[i], +#ifdef __FreeBSD__ + (long long)s->scounters[i]); +#else + s->scounters[i]); +#endif + if (runtime > 0) + printf("%14.1f/s\n", + (double)s->scounters[i] / (double)runtime); + else + printf("%14s\n", ""); + } + } + printf("Counters\n"); + for (i = 0; i < PFRES_MAX; i++) { + printf(" %-25s %14llu ", pf_reasons[i], + (unsigned long long)s->counters[i]); + if (runtime > 0) + printf("%14.1f/s\n", + (double)s->counters[i] / (double)runtime); + else + printf("%14s\n", ""); + } + if (opts & PF_OPT_VERBOSE) { + printf("Limit Counters\n"); + for (i = 0; i < LCNT_MAX; i++) { + printf(" %-25s %14lld ", pf_lcounters[i], +#ifdef __FreeBSD__ + (unsigned long long)s->lcounters[i]); +#else + s->lcounters[i]); +#endif + if (runtime > 0) + printf("%14.1f/s\n", + (double)s->lcounters[i] / (double)runtime); + else + printf("%14s\n", ""); + } + } +} + +void +print_src_node(struct pf_src_node *sn, int opts) +{ + struct pf_addr_wrap aw; + int min, sec; + + memset(&aw, 0, sizeof(aw)); + if (sn->af == AF_INET) + aw.v.a.mask.addr32[0] = 0xffffffff; + else + memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); + + aw.v.a.addr = sn->addr; + print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2); + printf(" -> "); + aw.v.a.addr = sn->raddr; + print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2); + printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states, + sn->conn, sn->conn_rate.count / 1000, + (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds); + if (opts & PF_OPT_VERBOSE) { + sec = sn->creation % 60; + sn->creation /= 60; + min = sn->creation % 60; + sn->creation /= 60; + printf(" age %.2u:%.2u:%.2u", sn->creation, min, sec); + if (sn->states == 0) { + sec = sn->expire % 60; + sn->expire /= 60; + min = sn->expire % 60; + sn->expire /= 60; + printf(", expires in %.2u:%.2u:%.2u", + sn->expire, min, sec); + } + printf(", %llu pkts, %llu bytes", +#ifdef __FreeBSD__ + (unsigned long long)(sn->packets[0] + sn->packets[1]), + (unsigned long long)(sn->bytes[0] + sn->bytes[1])); +#else + sn->packets[0] + sn->packets[1], + sn->bytes[0] + sn->bytes[1]); +#endif + switch (sn->ruletype) { + case PF_NAT: + if (sn->rule.nr != -1) + printf(", nat rule %u", sn->rule.nr); + break; + case PF_RDR: + if (sn->rule.nr != -1) + printf(", rdr rule %u", sn->rule.nr); + break; + case PF_PASS: + if (sn->rule.nr != -1) + printf(", filter rule %u", sn->rule.nr); + break; + } + printf("\n"); + } +} + +void +print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) +{ + static const char *actiontypes[] = { "pass", "block", "scrub", + "no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr" }; + static const char *anchortypes[] = { "anchor", "anchor", "anchor", + "anchor", "nat-anchor", "nat-anchor", "binat-anchor", + "binat-anchor", "rdr-anchor", "rdr-anchor" }; + int i, opts; + + if (verbose) + printf("@%d ", r->nr); + if (r->action > PF_NORDR) + printf("action(%d)", r->action); + else if (anchor_call[0]) { + if (anchor_call[0] == '_') { + printf("%s", anchortypes[r->action]); + } else + printf("%s \"%s\"", anchortypes[r->action], + anchor_call); + } else { + printf("%s", actiontypes[r->action]); + if (r->natpass) + printf(" pass"); + } + if (r->action == PF_DROP) { + if (r->rule_flag & PFRULE_RETURN) + printf(" return"); + else if (r->rule_flag & PFRULE_RETURNRST) { + if (!r->return_ttl) + printf(" return-rst"); + else + printf(" return-rst(ttl %d)", r->return_ttl); + } else if (r->rule_flag & PFRULE_RETURNICMP) { + const struct icmpcodeent *ic, *ic6; + + ic = geticmpcodebynumber(r->return_icmp >> 8, + r->return_icmp & 255, AF_INET); + ic6 = geticmpcodebynumber(r->return_icmp6 >> 8, + r->return_icmp6 & 255, AF_INET6); + + switch (r->af) { + case AF_INET: + printf(" return-icmp"); + if (ic == NULL) + printf("(%u)", r->return_icmp & 255); + else + printf("(%s)", ic->name); + break; + case AF_INET6: + printf(" return-icmp6"); + if (ic6 == NULL) + printf("(%u)", r->return_icmp6 & 255); + else + printf("(%s)", ic6->name); + break; + default: + printf(" return-icmp"); + if (ic == NULL) + printf("(%u, ", r->return_icmp & 255); + else + printf("(%s, ", ic->name); + if (ic6 == NULL) + printf("%u)", r->return_icmp6 & 255); + else + printf("%s)", ic6->name); + break; + } + } else + printf(" drop"); + } + if (r->direction == PF_IN) + printf(" in"); + else if (r->direction == PF_OUT) + printf(" out"); + if (r->log) { + printf(" log"); + if (r->log & ~PF_LOG || r->logif) { + int count = 0; + + printf(" ("); + if (r->log & PF_LOG_ALL) + printf("%sall", count++ ? ", " : ""); + if (r->log & PF_LOG_SOCKET_LOOKUP) + printf("%suser", count++ ? ", " : ""); + if (r->logif) + printf("%sto pflog%u", count++ ? ", " : "", + r->logif); + printf(")"); + } + } + if (r->quick) + printf(" quick"); + if (r->ifname[0]) { + if (r->ifnot) + printf(" on ! %s", r->ifname); + else + printf(" on %s", r->ifname); + } + if (r->rt) { + if (r->rt == PF_ROUTETO) + printf(" route-to"); + else if (r->rt == PF_REPLYTO) + printf(" reply-to"); + else if (r->rt == PF_DUPTO) + printf(" dup-to"); + else if (r->rt == PF_FASTROUTE) + printf(" fastroute"); + if (r->rt != PF_FASTROUTE) { + printf(" "); + print_pool(&r->rpool, 0, 0, r->af, PF_PASS); + } + } + if (r->af) { + if (r->af == AF_INET) + printf(" inet"); + else + printf(" inet6"); + } + if (r->proto) { + struct protoent *p; + + if ((p = getprotobynumber(r->proto)) != NULL) + printf(" proto %s", p->p_name); + else + printf(" proto %u", r->proto); + } + print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto, + verbose, numeric); + if (r->uid.op) + print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user", + UID_MAX); + if (r->gid.op) + print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group", + GID_MAX); + if (r->flags || r->flagset) { + printf(" flags "); + print_flags(r->flags); + printf("/"); + print_flags(r->flagset); + } else if (r->action == PF_PASS && + (!r->proto || r->proto == IPPROTO_TCP) && + !(r->rule_flag & PFRULE_FRAGMENT) && + !anchor_call[0] && r->keep_state) + printf(" flags any"); + if (r->type) { + const struct icmptypeent *it; + + it = geticmptypebynumber(r->type-1, r->af); + if (r->af != AF_INET6) + printf(" icmp-type"); + else + printf(" icmp6-type"); + if (it != NULL) + printf(" %s", it->name); + else + printf(" %u", r->type-1); + if (r->code) { + const struct icmpcodeent *ic; + + ic = geticmpcodebynumber(r->type-1, r->code-1, r->af); + if (ic != NULL) + printf(" code %s", ic->name); + else + printf(" code %u", r->code-1); + } + } + if (r->tos) + printf(" tos 0x%2.2x", r->tos); + if (!r->keep_state && r->action == PF_PASS && !anchor_call[0]) + printf(" no state"); + else if (r->keep_state == PF_STATE_NORMAL) + printf(" keep state"); + else if (r->keep_state == PF_STATE_MODULATE) + printf(" modulate state"); + else if (r->keep_state == PF_STATE_SYNPROXY) + printf(" synproxy state"); + if (r->prob) { + char buf[20]; + + snprintf(buf, sizeof(buf), "%f", r->prob*100.0/(UINT_MAX+1.0)); + for (i = strlen(buf)-1; i > 0; i--) { + if (buf[i] == '0') + buf[i] = '\0'; + else { + if (buf[i] == '.') + buf[i] = '\0'; + break; + } + } + printf(" probability %s%%", buf); + } + opts = 0; + if (r->max_states || r->max_src_nodes || r->max_src_states) + opts = 1; + if (r->rule_flag & PFRULE_NOSYNC) + opts = 1; + if (r->rule_flag & PFRULE_SRCTRACK) + opts = 1; + if (r->rule_flag & PFRULE_IFBOUND) + opts = 1; + if (r->rule_flag & PFRULE_STATESLOPPY) + opts = 1; + for (i = 0; !opts && i < PFTM_MAX; ++i) + if (r->timeout[i]) + opts = 1; + if (opts) { + printf(" ("); + if (r->max_states) { + printf("max %u", r->max_states); + opts = 0; + } + if (r->rule_flag & PFRULE_NOSYNC) { + if (!opts) + printf(", "); + printf("no-sync"); + opts = 0; + } + if (r->rule_flag & PFRULE_SRCTRACK) { + if (!opts) + printf(", "); + printf("source-track"); + if (r->rule_flag & PFRULE_RULESRCTRACK) + printf(" rule"); + else + printf(" global"); + opts = 0; + } + if (r->max_src_states) { + if (!opts) + printf(", "); + printf("max-src-states %u", r->max_src_states); + opts = 0; + } + if (r->max_src_conn) { + if (!opts) + printf(", "); + printf("max-src-conn %u", r->max_src_conn); + opts = 0; + } + if (r->max_src_conn_rate.limit) { + if (!opts) + printf(", "); + printf("max-src-conn-rate %u/%u", + r->max_src_conn_rate.limit, + r->max_src_conn_rate.seconds); + opts = 0; + } + if (r->max_src_nodes) { + if (!opts) + printf(", "); + printf("max-src-nodes %u", r->max_src_nodes); + opts = 0; + } + if (r->overload_tblname[0]) { + if (!opts) + printf(", "); + printf("overload <%s>", r->overload_tblname); + if (r->flush) + printf(" flush"); + if (r->flush & PF_FLUSH_GLOBAL) + printf(" global"); + } + if (r->rule_flag & PFRULE_IFBOUND) { + if (!opts) + printf(", "); + printf("if-bound"); + opts = 0; + } + if (r->rule_flag & PFRULE_STATESLOPPY) { + if (!opts) + printf(", "); + printf("sloppy"); + opts = 0; + } + for (i = 0; i < PFTM_MAX; ++i) + if (r->timeout[i]) { + int j; + + if (!opts) + printf(", "); + opts = 0; + for (j = 0; pf_timeouts[j].name != NULL; + ++j) + if (pf_timeouts[j].timeout == i) + break; + printf("%s %u", pf_timeouts[j].name == NULL ? + "inv.timeout" : pf_timeouts[j].name, + r->timeout[i]); + } + printf(")"); + } + if (r->rule_flag & PFRULE_FRAGMENT) + printf(" fragment"); + if (r->rule_flag & PFRULE_NODF) + printf(" no-df"); + if (r->rule_flag & PFRULE_RANDOMID) + printf(" random-id"); + if (r->min_ttl) + printf(" min-ttl %d", r->min_ttl); + if (r->max_mss) + printf(" max-mss %d", r->max_mss); + if (r->rule_flag & PFRULE_SET_TOS) + printf(" set-tos 0x%2.2x", r->set_tos); + if (r->allow_opts) + printf(" allow-opts"); + if (r->action == PF_SCRUB) { + if (r->rule_flag & PFRULE_REASSEMBLE_TCP) + printf(" reassemble tcp"); + + if (r->rule_flag & PFRULE_FRAGDROP) + printf(" fragment drop-ovl"); + else if (r->rule_flag & PFRULE_FRAGCROP) + printf(" fragment crop"); + else + printf(" fragment reassemble"); + } + if (r->label[0]) + printf(" label \"%s\"", r->label); + if (r->qname[0] && r->pqname[0]) + printf(" queue(%s, %s)", r->qname, r->pqname); + else if (r->qname[0]) + printf(" queue %s", r->qname); + if (r->tagname[0]) + printf(" tag %s", r->tagname); + if (r->match_tagname[0]) { + if (r->match_tag_not) + printf(" !"); + printf(" tagged %s", r->match_tagname); + } + if (r->rtableid != -1) + printf(" rtable %u", r->rtableid); + if (r->divert.port) { +#ifdef __FreeBSD__ + printf(" divert-to %u", ntohs(r->divert.port)); +#else + if (PF_AZERO(&r->divert.addr, r->af)) { + printf(" divert-reply"); + } else { + /* XXX cut&paste from print_addr */ + char buf[48]; + + printf(" divert-to "); + if (inet_ntop(r->af, &r->divert.addr, buf, + sizeof(buf)) == NULL) + printf("?"); + else + printf("%s", buf); + printf(" port %u", ntohs(r->divert.port)); + } +#endif + } + if (!anchor_call[0] && (r->action == PF_NAT || + r->action == PF_BINAT || r->action == PF_RDR)) { + printf(" -> "); + print_pool(&r->rpool, r->rpool.proxy_port[0], + r->rpool.proxy_port[1], r->af, r->action); + } +} + +void +print_tabledef(const char *name, int flags, int addrs, + struct node_tinithead *nodes) +{ + struct node_tinit *ti, *nti; + struct node_host *h; + + printf("table <%s>", name); + if (flags & PFR_TFLAG_CONST) + printf(" const"); + if (flags & PFR_TFLAG_PERSIST) + printf(" persist"); + if (flags & PFR_TFLAG_COUNTERS) + printf(" counters"); + SIMPLEQ_FOREACH(ti, nodes, entries) { + if (ti->file) { + printf(" file \"%s\"", ti->file); + continue; + } + printf(" {"); + for (;;) { + for (h = ti->host; h != NULL; h = h->next) { + printf(h->not ? " !" : " "); + print_addr(&h->addr, h->af, 0); + } + nti = SIMPLEQ_NEXT(ti, entries); + if (nti != NULL && nti->file == NULL) + ti = nti; /* merge lists */ + else + break; + } + printf(" }"); + } + if (addrs && SIMPLEQ_EMPTY(nodes)) + printf(" { }"); + printf("\n"); +} + +int +parse_flags(char *s) +{ + char *p, *q; + u_int8_t f = 0; + + for (p = s; *p; p++) { + if ((q = strchr(tcpflags, *p)) == NULL) + return -1; + else + f |= 1 << (q - tcpflags); + } + return (f ? f : PF_TH_ALL); +} + +void +set_ipmask(struct node_host *h, u_int8_t b) +{ + struct pf_addr *m, *n; + int i, j = 0; + + m = &h->addr.v.a.mask; + memset(m, 0, sizeof(*m)); + + while (b >= 32) { + m->addr32[j++] = 0xffffffff; + b -= 32; + } + for (i = 31; i > 31-b; --i) + m->addr32[j] |= (1 << i); + if (b) + m->addr32[j] = htonl(m->addr32[j]); + + /* Mask off bits of the address that will never be used. */ + n = &h->addr.v.a.addr; + if (h->addr.type == PF_ADDR_ADDRMASK) + for (i = 0; i < 4; i++) + n->addr32[i] = n->addr32[i] & m->addr32[i]; +} + +int +check_netmask(struct node_host *h, sa_family_t af) +{ + struct node_host *n = NULL; + struct pf_addr *m; + + for (n = h; n != NULL; n = n->next) { + if (h->addr.type == PF_ADDR_TABLE) + continue; + m = &h->addr.v.a.mask; + /* fix up netmask for dynaddr */ + if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL && + unmask(m, AF_INET6) > 32) + set_ipmask(n, 32); + /* netmasks > 32 bit are invalid on v4 */ + if (af == AF_INET && + (m->addr32[1] || m->addr32[2] || m->addr32[3])) { + fprintf(stderr, "netmask %u invalid for IPv4 address\n", + unmask(m, AF_INET6)); + return (1); + } + } + return (0); +} + +/* interface lookup routines */ + +struct node_host *iftab; + +void +ifa_load(void) +{ + struct ifaddrs *ifap, *ifa; + struct node_host *n = NULL, *h = NULL; + + if (getifaddrs(&ifap) < 0) + err(1, "getifaddrs"); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (!(ifa->ifa_addr->sa_family == AF_INET || + ifa->ifa_addr->sa_family == AF_INET6 || + ifa->ifa_addr->sa_family == AF_LINK)) + continue; + n = calloc(1, sizeof(struct node_host)); + if (n == NULL) + err(1, "address: calloc"); + n->af = ifa->ifa_addr->sa_family; + n->ifa_flags = ifa->ifa_flags; +#ifdef __KAME__ + if (n->af == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *) + ifa->ifa_addr)->sin6_addr) && + ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id == + 0) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + sin6->sin6_scope_id = sin6->sin6_addr.s6_addr[2] << 8 | + sin6->sin6_addr.s6_addr[3]; + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + } +#endif + n->ifindex = 0; + if (n->af == AF_INET) { + memcpy(&n->addr.v.a.addr, &((struct sockaddr_in *) + ifa->ifa_addr)->sin_addr.s_addr, + sizeof(struct in_addr)); + memcpy(&n->addr.v.a.mask, &((struct sockaddr_in *) + ifa->ifa_netmask)->sin_addr.s_addr, + sizeof(struct in_addr)); + if (ifa->ifa_broadaddr != NULL) + memcpy(&n->bcast, &((struct sockaddr_in *) + ifa->ifa_broadaddr)->sin_addr.s_addr, + sizeof(struct in_addr)); + if (ifa->ifa_dstaddr != NULL) + memcpy(&n->peer, &((struct sockaddr_in *) + ifa->ifa_dstaddr)->sin_addr.s_addr, + sizeof(struct in_addr)); + } else if (n->af == AF_INET6) { + memcpy(&n->addr.v.a.addr, &((struct sockaddr_in6 *) + ifa->ifa_addr)->sin6_addr.s6_addr, + sizeof(struct in6_addr)); + memcpy(&n->addr.v.a.mask, &((struct sockaddr_in6 *) + ifa->ifa_netmask)->sin6_addr.s6_addr, + sizeof(struct in6_addr)); + if (ifa->ifa_broadaddr != NULL) + memcpy(&n->bcast, &((struct sockaddr_in6 *) + ifa->ifa_broadaddr)->sin6_addr.s6_addr, + sizeof(struct in6_addr)); + if (ifa->ifa_dstaddr != NULL) + memcpy(&n->peer, &((struct sockaddr_in6 *) + ifa->ifa_dstaddr)->sin6_addr.s6_addr, + sizeof(struct in6_addr)); + n->ifindex = ((struct sockaddr_in6 *) + ifa->ifa_addr)->sin6_scope_id; + } + if ((n->ifname = strdup(ifa->ifa_name)) == NULL) + err(1, "ifa_load: strdup"); + n->next = NULL; + n->tail = n; + if (h == NULL) + h = n; + else { + h->tail->next = n; + h->tail = n; + } + } + + iftab = h; + freeifaddrs(ifap); +} + +struct node_host * +ifa_exists(const char *ifa_name) +{ + struct node_host *n; + struct ifgroupreq ifgr; + int s; + + if (iftab == NULL) + ifa_load(); + + /* check wether this is a group */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + err(1, "socket"); + bzero(&ifgr, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) { + /* fake a node_host */ + if ((n = calloc(1, sizeof(*n))) == NULL) + err(1, "calloc"); + if ((n->ifname = strdup(ifa_name)) == NULL) + err(1, "strdup"); + close(s); + return (n); + } + close(s); + + for (n = iftab; n; n = n->next) { + if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ)) + return (n); + } + + return (NULL); +} + +struct node_host * +ifa_grouplookup(const char *ifa_name, int flags) +{ + struct ifg_req *ifg; + struct ifgroupreq ifgr; + int s, len; + struct node_host *n, *h = NULL; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + err(1, "socket"); + bzero(&ifgr, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { + close(s); + return (NULL); + } + + len = ifgr.ifgr_len; + if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) + err(1, "calloc"); + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) + err(1, "SIOCGIFGMEMB"); + + for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); + ifg++) { + len -= sizeof(struct ifg_req); + if ((n = ifa_lookup(ifg->ifgrq_member, flags)) == NULL) + continue; + if (h == NULL) + h = n; + else { + h->tail->next = n; + h->tail = n->tail; + } + } + free(ifgr.ifgr_groups); + close(s); + + return (h); +} + +struct node_host * +ifa_lookup(const char *ifa_name, int flags) +{ + struct node_host *p = NULL, *h = NULL, *n = NULL; + int got4 = 0, got6 = 0; + const char *last_if = NULL; + + if ((h = ifa_grouplookup(ifa_name, flags)) != NULL) + return (h); + + if (!strncmp(ifa_name, "self", IFNAMSIZ)) + ifa_name = NULL; + + if (iftab == NULL) + ifa_load(); + + for (p = iftab; p; p = p->next) { + if (ifa_skip_if(ifa_name, p)) + continue; + if ((flags & PFI_AFLAG_BROADCAST) && p->af != AF_INET) + continue; + if ((flags & PFI_AFLAG_BROADCAST) && + !(p->ifa_flags & IFF_BROADCAST)) + continue; + if ((flags & PFI_AFLAG_PEER) && + !(p->ifa_flags & IFF_POINTOPOINT)) + continue; + if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0) + continue; + if (last_if == NULL || strcmp(last_if, p->ifname)) + got4 = got6 = 0; + last_if = p->ifname; + if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET && got4) + continue; + if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6) + continue; + if (p->af == AF_INET) + got4 = 1; + else + got6 = 1; + n = calloc(1, sizeof(struct node_host)); + if (n == NULL) + err(1, "address: calloc"); + n->af = p->af; + if (flags & PFI_AFLAG_BROADCAST) + memcpy(&n->addr.v.a.addr, &p->bcast, + sizeof(struct pf_addr)); + else if (flags & PFI_AFLAG_PEER) + memcpy(&n->addr.v.a.addr, &p->peer, + sizeof(struct pf_addr)); + else + memcpy(&n->addr.v.a.addr, &p->addr.v.a.addr, + sizeof(struct pf_addr)); + if (flags & PFI_AFLAG_NETWORK) + set_ipmask(n, unmask(&p->addr.v.a.mask, n->af)); + else { + if (n->af == AF_INET) { + if (p->ifa_flags & IFF_LOOPBACK && + p->ifa_flags & IFF_LINK1) + memcpy(&n->addr.v.a.mask, + &p->addr.v.a.mask, + sizeof(struct pf_addr)); + else + set_ipmask(n, 32); + } else + set_ipmask(n, 128); + } + n->ifindex = p->ifindex; + + n->next = NULL; + n->tail = n; + if (h == NULL) + h = n; + else { + h->tail->next = n; + h->tail = n; + } + } + return (h); +} + +int +ifa_skip_if(const char *filter, struct node_host *p) +{ + int n; + + if (p->af != AF_INET && p->af != AF_INET6) + return (1); + if (filter == NULL || !*filter) + return (0); + if (!strcmp(p->ifname, filter)) + return (0); /* exact match */ + n = strlen(filter); + if (n < 1 || n >= IFNAMSIZ) + return (1); /* sanity check */ + if (filter[n-1] >= '0' && filter[n-1] <= '9') + return (1); /* only do exact match in that case */ + if (strncmp(p->ifname, filter, n)) + return (1); /* prefix doesn't match */ + return (p->ifname[n] < '0' || p->ifname[n] > '9'); +} + + +struct node_host * +host(const char *s) +{ + struct node_host *h = NULL; + int mask, v4mask, v6mask, cont = 1; + char *p, *q, *ps; + + if ((p = strrchr(s, '/')) != NULL) { + mask = strtol(p+1, &q, 0); + if (!q || *q || mask > 128 || q == (p+1)) { + fprintf(stderr, "invalid netmask '%s'\n", p); + return (NULL); + } + if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) + err(1, "host: malloc"); + strlcpy(ps, s, strlen(s) - strlen(p) + 1); + v4mask = v6mask = mask; + } else { + if ((ps = strdup(s)) == NULL) + err(1, "host: strdup"); + v4mask = 32; + v6mask = 128; + mask = -1; + } + + /* interface with this name exists? */ + if (cont && (h = host_if(ps, mask)) != NULL) + cont = 0; + + /* IPv4 address? */ + if (cont && (h = host_v4(s, mask)) != NULL) + cont = 0; + + /* IPv6 address? */ + if (cont && (h = host_v6(ps, v6mask)) != NULL) + cont = 0; + + /* dns lookup */ + if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL) + cont = 0; + free(ps); + + if (h == NULL || cont == 1) { + fprintf(stderr, "no IP address found for %s\n", s); + return (NULL); + } + return (h); +} + +struct node_host * +host_if(const char *s, int mask) +{ + struct node_host *n, *h = NULL; + char *p, *ps; + int flags = 0; + + if ((ps = strdup(s)) == NULL) + err(1, "host_if: strdup"); + while ((p = strrchr(ps, ':')) != NULL) { + if (!strcmp(p+1, "network")) + flags |= PFI_AFLAG_NETWORK; + else if (!strcmp(p+1, "broadcast")) + flags |= PFI_AFLAG_BROADCAST; + else if (!strcmp(p+1, "peer")) + flags |= PFI_AFLAG_PEER; + else if (!strcmp(p+1, "0")) + flags |= PFI_AFLAG_NOALIAS; + else { + free(ps); + return (NULL); + } + *p = '\0'; + } + if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */ + fprintf(stderr, "illegal combination of interface modifiers\n"); + free(ps); + return (NULL); + } + if ((flags & (PFI_AFLAG_NETWORK|PFI_AFLAG_BROADCAST)) && mask > -1) { + fprintf(stderr, "network or broadcast lookup, but " + "extra netmask given\n"); + free(ps); + return (NULL); + } + if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) { + /* interface with this name exists */ + h = ifa_lookup(ps, flags); + for (n = h; n != NULL && mask > -1; n = n->next) + set_ipmask(n, mask); + } + + free(ps); + return (h); +} + +struct node_host * +host_v4(const char *s, int mask) +{ + struct node_host *h = NULL; + struct in_addr ina; + int bits = 32; + + memset(&ina, 0, sizeof(struct in_addr)); + if (strrchr(s, '/') != NULL) { + if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) + return (NULL); + } else { + if (inet_pton(AF_INET, s, &ina) != 1) + return (NULL); + } + + h = calloc(1, sizeof(struct node_host)); + if (h == NULL) + err(1, "address: calloc"); + h->ifname = NULL; + h->af = AF_INET; + h->addr.v.a.addr.addr32[0] = ina.s_addr; + set_ipmask(h, bits); + h->next = NULL; + h->tail = h; + + return (h); +} + +struct node_host * +host_v6(const char *s, int mask) +{ + struct addrinfo hints, *res; + struct node_host *h = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(s, "0", &hints, &res) == 0) { + h = calloc(1, sizeof(struct node_host)); + if (h == NULL) + err(1, "address: calloc"); + h->ifname = NULL; + h->af = AF_INET6; + memcpy(&h->addr.v.a.addr, + &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, + sizeof(h->addr.v.a.addr)); + h->ifindex = + ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; + set_ipmask(h, mask); + freeaddrinfo(res); + h->next = NULL; + h->tail = h; + } + + return (h); +} + +struct node_host * +host_dns(const char *s, int v4mask, int v6mask) +{ + struct addrinfo hints, *res0, *res; + struct node_host *n, *h = NULL; + int error, noalias = 0; + int got4 = 0, got6 = 0; + char *p, *ps; + + if ((ps = strdup(s)) == NULL) + err(1, "host_dns: strdup"); + if ((p = strrchr(ps, ':')) != NULL && !strcmp(p, ":0")) { + noalias = 1; + *p = '\0'; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; /* DUMMY */ + error = getaddrinfo(ps, NULL, &hints, &res0); + if (error) { + free(ps); + return (h); + } + + for (res = res0; res; res = res->ai_next) { + if (res->ai_family != AF_INET && + res->ai_family != AF_INET6) + continue; + if (noalias) { + if (res->ai_family == AF_INET) { + if (got4) + continue; + got4 = 1; + } else { + if (got6) + continue; + got6 = 1; + } + } + n = calloc(1, sizeof(struct node_host)); + if (n == NULL) + err(1, "host_dns: calloc"); + n->ifname = NULL; + n->af = res->ai_family; + if (res->ai_family == AF_INET) { + memcpy(&n->addr.v.a.addr, + &((struct sockaddr_in *) + res->ai_addr)->sin_addr.s_addr, + sizeof(struct in_addr)); + set_ipmask(n, v4mask); + } else { + memcpy(&n->addr.v.a.addr, + &((struct sockaddr_in6 *) + res->ai_addr)->sin6_addr.s6_addr, + sizeof(struct in6_addr)); + n->ifindex = + ((struct sockaddr_in6 *) + res->ai_addr)->sin6_scope_id; + set_ipmask(n, v6mask); + } + n->next = NULL; + n->tail = n; + if (h == NULL) + h = n; + else { + h->tail->next = n; + h->tail = n; + } + } + freeaddrinfo(res0); + free(ps); + + return (h); +} + +/* + * convert a hostname to a list of addresses and put them in the given buffer. + * test: + * if set to 1, only simple addresses are accepted (no netblock, no "!"). + */ +int +append_addr(struct pfr_buffer *b, char *s, int test) +{ + char *r; + struct node_host *h, *n; + int rv, not = 0; + + for (r = s; *r == '!'; r++) + not = !not; + if ((n = host(r)) == NULL) { + errno = 0; + return (-1); + } + rv = append_addr_host(b, n, test, not); + do { + h = n; + n = n->next; + free(h); + } while (n != NULL); + return (rv); +} + +/* + * same as previous function, but with a pre-parsed input and the ability + * to "negate" the result. Does not free the node_host list. + * not: + * setting it to 1 is equivalent to adding "!" in front of parameter s. + */ +int +append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not) +{ + int bits; + struct pfr_addr addr; + + do { + bzero(&addr, sizeof(addr)); + addr.pfra_not = n->not ^ not; + addr.pfra_af = n->af; + addr.pfra_net = unmask(&n->addr.v.a.mask, n->af); + switch (n->af) { + case AF_INET: + addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0]; + bits = 32; + break; + case AF_INET6: + memcpy(&addr.pfra_ip6addr, &n->addr.v.a.addr.v6, + sizeof(struct in6_addr)); + bits = 128; + break; + default: + errno = EINVAL; + return (-1); + } + if ((test && (not || addr.pfra_net != bits)) || + addr.pfra_net > bits) { + errno = EINVAL; + return (-1); + } + if (pfr_buf_add(b, &addr)) + return (-1); + } while ((n = n->next) != NULL); + + return (0); +} + +int +pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor) +{ + struct pfioc_trans_e trans; + + bzero(&trans, sizeof(trans)); + trans.rs_num = rs_num; + if (strlcpy(trans.anchor, anchor, + sizeof(trans.anchor)) >= sizeof(trans.anchor)) + errx(1, "pfctl_add_trans: strlcpy"); + + return pfr_buf_add(buf, &trans); +} + +u_int32_t +pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor) +{ + struct pfioc_trans_e *p; + + PFRB_FOREACH(p, buf) + if (rs_num == p->rs_num && !strcmp(anchor, p->anchor)) + return (p->ticket); + errx(1, "pfctl_get_ticket: assertion failed"); +} + +int +pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from) +{ + struct pfioc_trans trans; + + bzero(&trans, sizeof(trans)); + trans.size = buf->pfrb_size - from; + trans.esize = sizeof(struct pfioc_trans_e); + trans.array = ((struct pfioc_trans_e *)buf->pfrb_caddr) + from; + return ioctl(dev, cmd, &trans); +} diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h new file mode 100644 index 0000000000000..4560d66bb0edc --- /dev/null +++ b/sbin/pfctl/pfctl_parser.h @@ -0,0 +1,305 @@ +/* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PFCTL_PARSER_H_ +#define _PFCTL_PARSER_H_ + +#define PF_OSFP_FILE "/etc/pf.os" + +#define PF_OPT_DISABLE 0x0001 +#define PF_OPT_ENABLE 0x0002 +#define PF_OPT_VERBOSE 0x0004 +#define PF_OPT_NOACTION 0x0008 +#define PF_OPT_QUIET 0x0010 +#define PF_OPT_CLRRULECTRS 0x0020 +#define PF_OPT_USEDNS 0x0040 +#define PF_OPT_VERBOSE2 0x0080 +#define PF_OPT_DUMMYACTION 0x0100 +#define PF_OPT_DEBUG 0x0200 +#define PF_OPT_SHOWALL 0x0400 +#define PF_OPT_OPTIMIZE 0x0800 +#define PF_OPT_NUMERIC 0x1000 +#define PF_OPT_MERGE 0x2000 +#define PF_OPT_RECURSE 0x4000 + +#define PF_TH_ALL 0xFF + +#define PF_NAT_PROXY_PORT_LOW 50001 +#define PF_NAT_PROXY_PORT_HIGH 65535 + +#define PF_OPTIMIZE_BASIC 0x0001 +#define PF_OPTIMIZE_PROFILE 0x0002 + +#define FCNT_NAMES { \ + "searches", \ + "inserts", \ + "removals", \ + NULL \ +} + +struct pfr_buffer; /* forward definition */ + + +struct pfctl { + int dev; + int opts; + int optimize; + int loadopt; + int asd; /* anchor stack depth */ + int bn; /* brace number */ + int brace; + int tdirty; /* kernel dirty */ +#define PFCTL_ANCHOR_STACK_DEPTH 64 + struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH]; + struct pfioc_pooladdr paddr; + struct pfioc_altq *paltq; + struct pfioc_queue *pqueue; + struct pfr_buffer *trans; + struct pf_anchor *anchor, *alast; + const char *ruleset; + + /* 'set foo' options */ + u_int32_t timeout[PFTM_MAX]; + u_int32_t limit[PF_LIMIT_MAX]; + u_int32_t debug; + u_int32_t hostid; + char *ifname; + + u_int8_t timeout_set[PFTM_MAX]; + u_int8_t limit_set[PF_LIMIT_MAX]; + u_int8_t debug_set; + u_int8_t hostid_set; + u_int8_t ifname_set; +}; + +struct node_if { + char ifname[IFNAMSIZ]; + u_int8_t not; + u_int8_t dynamic; /* antispoof */ + u_int ifa_flags; + struct node_if *next; + struct node_if *tail; +}; + +struct node_host { + struct pf_addr_wrap addr; + struct pf_addr bcast; + struct pf_addr peer; + sa_family_t af; + u_int8_t not; + u_int32_t ifindex; /* link-local IPv6 addrs */ + char *ifname; + u_int ifa_flags; + struct node_host *next; + struct node_host *tail; +}; + +struct node_os { + char *os; + pf_osfp_t fingerprint; + struct node_os *next; + struct node_os *tail; +}; + +struct node_queue_bw { + u_int32_t bw_absolute; + u_int16_t bw_percent; +}; + +struct node_hfsc_sc { + struct node_queue_bw m1; /* slope of 1st segment; bps */ + u_int d; /* x-projection of m1; msec */ + struct node_queue_bw m2; /* slope of 2nd segment; bps */ + u_int8_t used; +}; + +struct node_hfsc_opts { + struct node_hfsc_sc realtime; + struct node_hfsc_sc linkshare; + struct node_hfsc_sc upperlimit; + int flags; +}; + +struct node_queue_opt { + int qtype; + union { + struct cbq_opts cbq_opts; + struct priq_opts priq_opts; + struct node_hfsc_opts hfsc_opts; + } data; +}; + +#ifdef __FreeBSD__ +/* + * XXX + * Absolutely this is not correct location to define this. + * Should we use an another sperate header file? + */ +#define SIMPLEQ_HEAD STAILQ_HEAD +#define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER +#define SIMPLEQ_ENTRY STAILQ_ENTRY +#define SIMPLEQ_FIRST STAILQ_FIRST +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY STAILQ_EMPTY +#define SIMPLEQ_NEXT STAILQ_NEXT +/*#define SIMPLEQ_FOREACH STAILQ_FOREACH*/ +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) +#define SIMPLEQ_INIT STAILQ_INIT +#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD +#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL +#define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER +#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD +#endif +SIMPLEQ_HEAD(node_tinithead, node_tinit); +struct node_tinit { /* table initializer */ + SIMPLEQ_ENTRY(node_tinit) entries; + struct node_host *host; + char *file; +}; + + +/* optimizer created tables */ +struct pf_opt_tbl { + char pt_name[PF_TABLE_NAME_SIZE]; + int pt_rulecount; + int pt_generated; + struct node_tinithead pt_nodes; + struct pfr_buffer *pt_buf; +}; +#define PF_OPT_TABLE_PREFIX "__automatic_" + +/* optimizer pf_rule container */ +struct pf_opt_rule { + struct pf_rule por_rule; + struct pf_opt_tbl *por_src_tbl; + struct pf_opt_tbl *por_dst_tbl; + u_int64_t por_profile_count; + TAILQ_ENTRY(pf_opt_rule) por_entry; + TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT]; +}; + +TAILQ_HEAD(pf_opt_queue, pf_opt_rule); + +int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *); +int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *); + +int pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *); +int pfctl_add_altq(struct pfctl *, struct pf_altq *); +int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t); +void pfctl_move_pool(struct pf_pool *, struct pf_pool *); +void pfctl_clear_pool(struct pf_pool *); + +int pfctl_set_timeout(struct pfctl *, const char *, int, int); +int pfctl_set_optimization(struct pfctl *, const char *); +int pfctl_set_limit(struct pfctl *, const char *, unsigned int); +int pfctl_set_logif(struct pfctl *, char *); +int pfctl_set_hostid(struct pfctl *, u_int32_t); +int pfctl_set_debug(struct pfctl *, char *); +int pfctl_set_interface_flags(struct pfctl *, char *, int, int); + +int parse_config(char *, struct pfctl *); +int parse_flags(char *); +int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); + +void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int); +void print_src_node(struct pf_src_node *, int); +void print_rule(struct pf_rule *, const char *, int, int); +void print_tabledef(const char *, int, int, struct node_tinithead *); +void print_status(struct pf_status *, int); + +int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *, + struct node_queue_opt *); +int eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *, + struct node_queue_opt *); + +void print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *, + struct node_queue_opt *); +void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *, + int, struct node_queue_opt *); + +int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *, + u_int32_t); + +void pfctl_clear_fingerprints(int, int); +int pfctl_file_fingerprints(int, int, const char *); +pf_osfp_t pfctl_get_fingerprint(const char *); +int pfctl_load_fingerprints(int, int); +char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t); +void pfctl_show_fingerprints(int); + + +struct icmptypeent { + const char *name; + u_int8_t type; +}; + +struct icmpcodeent { + const char *name; + u_int8_t type; + u_int8_t code; +}; + +const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t); +const struct icmptypeent *geticmptypebyname(char *, u_int8_t); +const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t); +const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t); + +struct pf_timeout { + const char *name; + int timeout; +}; + +#define PFCTL_FLAG_FILTER 0x02 +#define PFCTL_FLAG_NAT 0x04 +#define PFCTL_FLAG_OPTION 0x08 +#define PFCTL_FLAG_ALTQ 0x10 +#define PFCTL_FLAG_TABLE 0x20 + +extern const struct pf_timeout pf_timeouts[]; + +void set_ipmask(struct node_host *, u_int8_t); +int check_netmask(struct node_host *, sa_family_t); +int unmask(struct pf_addr *, sa_family_t); +void ifa_load(void); +struct node_host *ifa_exists(const char *); +struct node_host *ifa_lookup(const char *, int); +struct node_host *host(const char *); + +int append_addr(struct pfr_buffer *, char *, int); +int append_addr_host(struct pfr_buffer *, + struct node_host *, int, int); + +#endif /* _PFCTL_PARSER_H_ */ diff --git a/sbin/pfctl/pfctl_qstats.c b/sbin/pfctl/pfctl_qstats.c new file mode 100644 index 0000000000000..95371e47af441 --- /dev/null +++ b/sbin/pfctl/pfctl_qstats.c @@ -0,0 +1,449 @@ +/* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */ + +/* + * Copyright (c) Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pfctl.h" +#include "pfctl_parser.h" + +union class_stats { + class_stats_t cbq_stats; + struct priq_classstats priq_stats; + struct hfsc_classstats hfsc_stats; +}; + +#define AVGN_MAX 8 +#define STAT_INTERVAL 5 + +struct queue_stats { + union class_stats data; + int avgn; + double avg_bytes; + double avg_packets; + u_int64_t prev_bytes; + u_int64_t prev_packets; +}; + +struct pf_altq_node { + struct pf_altq altq; + struct pf_altq_node *next; + struct pf_altq_node *children; + struct queue_stats qstats; +}; + +int pfctl_update_qstats(int, struct pf_altq_node **); +void pfctl_insert_altq_node(struct pf_altq_node **, + const struct pf_altq, const struct queue_stats); +struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, + const char *, const char *); +void pfctl_print_altq_node(int, const struct pf_altq_node *, + unsigned, int); +void print_cbqstats(struct queue_stats); +void print_priqstats(struct queue_stats); +void print_hfscstats(struct queue_stats); +void pfctl_free_altq_node(struct pf_altq_node *); +void pfctl_print_altq_nodestat(int, + const struct pf_altq_node *); + +void update_avg(struct pf_altq_node *); + +int +pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) +{ + struct pf_altq_node *root = NULL, *node; + int nodes, dotitle = (opts & PF_OPT_SHOWALL); + +#ifdef __FreeBSD__ + if (!altqsupport) + return (-1); +#endif + + if ((nodes = pfctl_update_qstats(dev, &root)) < 0) + return (-1); + + if (nodes == 0) + printf("No queue in use\n"); + for (node = root; node != NULL; node = node->next) { + if (iface != NULL && strcmp(node->altq.ifname, iface)) + continue; + if (dotitle) { + pfctl_print_title("ALTQ:"); + dotitle = 0; + } + pfctl_print_altq_node(dev, node, 0, opts); + } + + while (verbose2 && nodes > 0) { + printf("\n"); + fflush(stdout); + sleep(STAT_INTERVAL); + if ((nodes = pfctl_update_qstats(dev, &root)) == -1) + return (-1); + for (node = root; node != NULL; node = node->next) { + if (iface != NULL && strcmp(node->altq.ifname, iface)) + continue; +#ifdef __FreeBSD__ + if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) + continue; +#endif + pfctl_print_altq_node(dev, node, 0, opts); + } + } + pfctl_free_altq_node(root); + return (0); +} + +int +pfctl_update_qstats(int dev, struct pf_altq_node **root) +{ + struct pf_altq_node *node; + struct pfioc_altq pa; + struct pfioc_qstats pq; + u_int32_t mnr, nr; + struct queue_stats qstats; + static u_int32_t last_ticket; + + memset(&pa, 0, sizeof(pa)); + memset(&pq, 0, sizeof(pq)); + memset(&qstats, 0, sizeof(qstats)); + if (ioctl(dev, DIOCGETALTQS, &pa)) { + warn("DIOCGETALTQS"); + return (-1); + } + + /* if a new set is found, start over */ + if (pa.ticket != last_ticket && *root != NULL) { + pfctl_free_altq_node(*root); + *root = NULL; + } + last_ticket = pa.ticket; + + mnr = pa.nr; + for (nr = 0; nr < mnr; ++nr) { + pa.nr = nr; + if (ioctl(dev, DIOCGETALTQ, &pa)) { + warn("DIOCGETALTQ"); + return (-1); + } +#ifdef __FreeBSD__ + if (pa.altq.qid > 0 && + !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { +#else + if (pa.altq.qid > 0) { +#endif + pq.nr = nr; + pq.ticket = pa.ticket; + pq.buf = &qstats.data; + pq.nbytes = sizeof(qstats.data); + if (ioctl(dev, DIOCGETQSTATS, &pq)) { + warn("DIOCGETQSTATS"); + return (-1); + } + if ((node = pfctl_find_altq_node(*root, pa.altq.qname, + pa.altq.ifname)) != NULL) { + memcpy(&node->qstats.data, &qstats.data, + sizeof(qstats.data)); + update_avg(node); + } else { + pfctl_insert_altq_node(root, pa.altq, qstats); + } + } +#ifdef __FreeBSD__ + else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) { + memset(&qstats.data, 0, sizeof(qstats.data)); + if ((node = pfctl_find_altq_node(*root, pa.altq.qname, + pa.altq.ifname)) != NULL) { + memcpy(&node->qstats.data, &qstats.data, + sizeof(qstats.data)); + update_avg(node); + } else { + pfctl_insert_altq_node(root, pa.altq, qstats); + } + } +#endif + } + return (mnr); +} + +void +pfctl_insert_altq_node(struct pf_altq_node **root, + const struct pf_altq altq, const struct queue_stats qstats) +{ + struct pf_altq_node *node; + + node = calloc(1, sizeof(struct pf_altq_node)); + if (node == NULL) + err(1, "pfctl_insert_altq_node: calloc"); + memcpy(&node->altq, &altq, sizeof(struct pf_altq)); + memcpy(&node->qstats, &qstats, sizeof(qstats)); + node->next = node->children = NULL; + + if (*root == NULL) + *root = node; + else if (!altq.parent[0]) { + struct pf_altq_node *prev = *root; + + while (prev->next != NULL) + prev = prev->next; + prev->next = node; + } else { + struct pf_altq_node *parent; + + parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); + if (parent == NULL) + errx(1, "parent %s not found", altq.parent); + if (parent->children == NULL) + parent->children = node; + else { + struct pf_altq_node *prev = parent->children; + + while (prev->next != NULL) + prev = prev->next; + prev->next = node; + } + } + update_avg(node); +} + +struct pf_altq_node * +pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, + const char *ifname) +{ + struct pf_altq_node *node, *child; + + for (node = root; node != NULL; node = node->next) { + if (!strcmp(node->altq.qname, qname) + && !(strcmp(node->altq.ifname, ifname))) + return (node); + if (node->children != NULL) { + child = pfctl_find_altq_node(node->children, qname, + ifname); + if (child != NULL) + return (child); + } + } + return (NULL); +} + +void +pfctl_print_altq_node(int dev, const struct pf_altq_node *node, + unsigned int level, int opts) +{ + const struct pf_altq_node *child; + + if (node == NULL) + return; + + print_altq(&node->altq, level, NULL, NULL); + + if (node->children != NULL) { + printf("{"); + for (child = node->children; child != NULL; + child = child->next) { + printf("%s", child->altq.qname); + if (child->next != NULL) + printf(", "); + } + printf("}"); + } + printf("\n"); + + if (opts & PF_OPT_VERBOSE) + pfctl_print_altq_nodestat(dev, node); + + if (opts & PF_OPT_DEBUG) + printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", + node->altq.qid, node->altq.ifname, + rate2str((double)(node->altq.ifbandwidth))); + + for (child = node->children; child != NULL; + child = child->next) + pfctl_print_altq_node(dev, child, level + 1, opts); +} + +void +pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) +{ + if (a->altq.qid == 0) + return; + +#ifdef __FreeBSD__ + if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) + return; +#endif + switch (a->altq.scheduler) { + case ALTQT_CBQ: + print_cbqstats(a->qstats); + break; + case ALTQT_PRIQ: + print_priqstats(a->qstats); + break; + case ALTQT_HFSC: + print_hfscstats(a->qstats); + break; + } +} + +void +print_cbqstats(struct queue_stats cur) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, + (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, + (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, + (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); + printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", + cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, + cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); + + if (cur.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + cur.avg_packets / STAT_INTERVAL, + rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); +} + +void +print_priqstats(struct queue_stats cur) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)cur.data.priq_stats.xmitcnt.packets, + (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, + (unsigned long long)cur.data.priq_stats.dropcnt.packets, + (unsigned long long)cur.data.priq_stats.dropcnt.bytes); + printf(" [ qlength: %3d/%3d ]\n", + cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); + + if (cur.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + cur.avg_packets / STAT_INTERVAL, + rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); +} + +void +print_hfscstats(struct queue_stats cur) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, + (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, + (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, + (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); + printf(" [ qlength: %3d/%3d ]\n", + cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); + + if (cur.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + cur.avg_packets / STAT_INTERVAL, + rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); +} + +void +pfctl_free_altq_node(struct pf_altq_node *node) +{ + while (node != NULL) { + struct pf_altq_node *prev; + + if (node->children != NULL) + pfctl_free_altq_node(node->children); + prev = node; + node = node->next; + free(prev); + } +} + +void +update_avg(struct pf_altq_node *a) +{ + struct queue_stats *qs; + u_int64_t b, p; + int n; + + if (a->altq.qid == 0) + return; + + qs = &a->qstats; + n = qs->avgn; + + switch (a->altq.scheduler) { + case ALTQT_CBQ: + b = qs->data.cbq_stats.xmit_cnt.bytes; + p = qs->data.cbq_stats.xmit_cnt.packets; + break; + case ALTQT_PRIQ: + b = qs->data.priq_stats.xmitcnt.bytes; + p = qs->data.priq_stats.xmitcnt.packets; + break; + case ALTQT_HFSC: + b = qs->data.hfsc_stats.xmit_cnt.bytes; + p = qs->data.hfsc_stats.xmit_cnt.packets; + break; + default: + b = 0; + p = 0; + break; + } + + if (n == 0) { + qs->prev_bytes = b; + qs->prev_packets = p; + qs->avgn++; + return; + } + + if (b >= qs->prev_bytes) + qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + + (b - qs->prev_bytes)) / n; + + if (p >= qs->prev_packets) + qs->avg_packets = ((qs->avg_packets * (n - 1)) + + (p - qs->prev_packets)) / n; + + qs->prev_bytes = b; + qs->prev_packets = p; + if (n < AVGN_MAX) + qs->avgn++; +} diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c new file mode 100644 index 0000000000000..38f16c4b72a97 --- /dev/null +++ b/sbin/pfctl/pfctl_radix.c @@ -0,0 +1,585 @@ +/* $OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */ + +/* + * Copyright (c) 2002 Cedric Berger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pfctl.h" + +#define BUF_SIZE 256 + +extern int dev; + +static int pfr_next_token(char buf[], FILE *); + + +int +pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) +{ + struct pfioc_table io; + + bzero(&io, sizeof io); + io.pfrio_flags = flags; + if (filter != NULL) + io.pfrio_table = *filter; + if (ioctl(dev, DIOCRCLRTABLES, &io)) + return (-1); + if (ndel != NULL) + *ndel = io.pfrio_ndel; + return (0); +} + +int +pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) +{ + struct pfioc_table io; + + if (size < 0 || (size && tbl == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_buffer = tbl; + io.pfrio_esize = sizeof(*tbl); + io.pfrio_size = size; + if (ioctl(dev, DIOCRADDTABLES, &io)) + return (-1); + if (nadd != NULL) + *nadd = io.pfrio_nadd; + return (0); +} + +int +pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) +{ + struct pfioc_table io; + + if (size < 0 || (size && tbl == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_buffer = tbl; + io.pfrio_esize = sizeof(*tbl); + io.pfrio_size = size; + if (ioctl(dev, DIOCRDELTABLES, &io)) + return (-1); + if (ndel != NULL) + *ndel = io.pfrio_ndel; + return (0); +} + +int +pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, + int flags) +{ + struct pfioc_table io; + + if (size == NULL || *size < 0 || (*size && tbl == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + if (filter != NULL) + io.pfrio_table = *filter; + io.pfrio_buffer = tbl; + io.pfrio_esize = sizeof(*tbl); + io.pfrio_size = *size; + if (ioctl(dev, DIOCRGETTABLES, &io)) + return (-1); + *size = io.pfrio_size; + return (0); +} + +int +pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, + int flags) +{ + struct pfioc_table io; + + if (size == NULL || *size < 0 || (*size && tbl == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + if (filter != NULL) + io.pfrio_table = *filter; + io.pfrio_buffer = tbl; + io.pfrio_esize = sizeof(*tbl); + io.pfrio_size = *size; + if (ioctl(dev, DIOCRGETTSTATS, &io)) + return (-1); + *size = io.pfrio_size; + return (0); +} + +int +pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) +{ + struct pfioc_table io; + + if (tbl == NULL) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + if (ioctl(dev, DIOCRCLRADDRS, &io)) + return (-1); + if (ndel != NULL) + *ndel = io.pfrio_ndel; + return (0); +} + +int +pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nadd, int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size < 0 || (size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = size; + if (ioctl(dev, DIOCRADDADDRS, &io)) + return (-1); + if (nadd != NULL) + *nadd = io.pfrio_nadd; + return (0); +} + +int +pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *ndel, int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size < 0 || (size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = size; + if (ioctl(dev, DIOCRDELADDRS, &io)) + return (-1); + if (ndel != NULL) + *ndel = io.pfrio_ndel; + return (0); +} + +int +pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *size2, int *nadd, int *ndel, int *nchange, int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size < 0 || (size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = size; + io.pfrio_size2 = (size2 != NULL) ? *size2 : 0; + if (ioctl(dev, DIOCRSETADDRS, &io)) + return (-1); + if (nadd != NULL) + *nadd = io.pfrio_nadd; + if (ndel != NULL) + *ndel = io.pfrio_ndel; + if (nchange != NULL) + *nchange = io.pfrio_nchange; + if (size2 != NULL) + *size2 = io.pfrio_size2; + return (0); +} + +int +pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, + int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size == NULL || *size < 0 || + (*size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = *size; + if (ioctl(dev, DIOCRGETADDRS, &io)) + return (-1); + *size = io.pfrio_size; + return (0); +} + +int +pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, + int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size == NULL || *size < 0 || + (*size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = *size; + if (ioctl(dev, DIOCRGETASTATS, &io)) + return (-1); + *size = io.pfrio_size; + return (0); +} + +int +pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) +{ + struct pfioc_table io; + + if (size < 0 || (size && !tbl)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_buffer = tbl; + io.pfrio_esize = sizeof(*tbl); + io.pfrio_size = size; + if (ioctl(dev, DIOCRCLRTSTATS, &io)) + return (-1); + if (nzero) + *nzero = io.pfrio_nzero; + return (0); +} + +int +pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nmatch, int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size < 0 || (size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = size; + if (ioctl(dev, DIOCRTSTADDRS, &io)) + return (-1); + if (nmatch) + *nmatch = io.pfrio_nmatch; + return (0); +} + +int +pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nadd, int *naddr, int ticket, int flags) +{ + struct pfioc_table io; + + if (tbl == NULL || size < 0 || (size && addr == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + io.pfrio_flags = flags; + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); + io.pfrio_size = size; + io.pfrio_ticket = ticket; + if (ioctl(dev, DIOCRINADEFINE, &io)) + return (-1); + if (nadd != NULL) + *nadd = io.pfrio_nadd; + if (naddr != NULL) + *naddr = io.pfrio_naddr; + return (0); +} + +/* interface management code */ + +int +pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size) +{ + struct pfioc_iface io; + + if (size == NULL || *size < 0 || (*size && buf == NULL)) { + errno = EINVAL; + return (-1); + } + bzero(&io, sizeof io); + if (filter != NULL) + if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >= + sizeof(io.pfiio_name)) { + errno = EINVAL; + return (-1); + } + io.pfiio_buffer = buf; + io.pfiio_esize = sizeof(*buf); + io.pfiio_size = *size; + if (ioctl(dev, DIOCIGETIFACES, &io)) + return (-1); + *size = io.pfiio_size; + return (0); +} + +/* buffer management code */ + +size_t buf_esize[PFRB_MAX] = { 0, + sizeof(struct pfr_table), sizeof(struct pfr_tstats), + sizeof(struct pfr_addr), sizeof(struct pfr_astats), + sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e) +}; + +/* + * add one element to the buffer + */ +int +pfr_buf_add(struct pfr_buffer *b, const void *e) +{ + size_t bs; + + if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX || + e == NULL) { + errno = EINVAL; + return (-1); + } + bs = buf_esize[b->pfrb_type]; + if (b->pfrb_size == b->pfrb_msize) + if (pfr_buf_grow(b, 0)) + return (-1); + memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs); + b->pfrb_size++; + return (0); +} + +/* + * return next element of the buffer (or first one if prev is NULL) + * see PFRB_FOREACH macro + */ +void * +pfr_buf_next(struct pfr_buffer *b, const void *prev) +{ + size_t bs; + + if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) + return (NULL); + if (b->pfrb_size == 0) + return (NULL); + if (prev == NULL) + return (b->pfrb_caddr); + bs = buf_esize[b->pfrb_type]; + if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1) + return (NULL); + return (((caddr_t)prev) + bs); +} + +/* + * minsize: + * 0: make the buffer somewhat bigger + * n: make room for "n" entries in the buffer + */ +int +pfr_buf_grow(struct pfr_buffer *b, int minsize) +{ + caddr_t p; + size_t bs; + + if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) { + errno = EINVAL; + return (-1); + } + if (minsize != 0 && minsize <= b->pfrb_msize) + return (0); + bs = buf_esize[b->pfrb_type]; + if (!b->pfrb_msize) { + if (minsize < 64) + minsize = 64; + b->pfrb_caddr = calloc(bs, minsize); + if (b->pfrb_caddr == NULL) + return (-1); + b->pfrb_msize = minsize; + } else { + if (minsize == 0) + minsize = b->pfrb_msize * 2; + if (minsize < 0 || minsize >= SIZE_T_MAX / bs) { + /* msize overflow */ + errno = ENOMEM; + return (-1); + } + p = realloc(b->pfrb_caddr, minsize * bs); + if (p == NULL) + return (-1); + bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs); + b->pfrb_caddr = p; + b->pfrb_msize = minsize; + } + return (0); +} + +/* + * reset buffer and free memory. + */ +void +pfr_buf_clear(struct pfr_buffer *b) +{ + if (b == NULL) + return; + if (b->pfrb_caddr != NULL) + free(b->pfrb_caddr); + b->pfrb_caddr = NULL; + b->pfrb_size = b->pfrb_msize = 0; +} + +int +pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, + int (*append_addr)(struct pfr_buffer *, char *, int)) +{ + FILE *fp; + char buf[BUF_SIZE]; + int rv; + + if (file == NULL) + return (0); + if (!strcmp(file, "-")) + fp = stdin; + else { + fp = pfctl_fopen(file, "r"); + if (fp == NULL) + return (-1); + } + while ((rv = pfr_next_token(buf, fp)) == 1) + if (append_addr(b, buf, nonetwork)) { + rv = -1; + break; + } + if (fp != stdin) + fclose(fp); + return (rv); +} + +int +pfr_next_token(char buf[BUF_SIZE], FILE *fp) +{ + static char next_ch = ' '; + int i = 0; + + for (;;) { + /* skip spaces */ + while (isspace(next_ch) && !feof(fp)) + next_ch = fgetc(fp); + /* remove from '#' until end of line */ + if (next_ch == '#') + while (!feof(fp)) { + next_ch = fgetc(fp); + if (next_ch == '\n') + break; + } + else + break; + } + if (feof(fp)) { + next_ch = ' '; + return (0); + } + do { + if (i < BUF_SIZE) + buf[i++] = next_ch; + next_ch = fgetc(fp); + } while (!feof(fp) && !isspace(next_ch)); + if (i >= BUF_SIZE) { + errno = EINVAL; + return (-1); + } + buf[i] = '\0'; + return (1); +} + +char * +pfr_strerror(int errnum) +{ + switch (errnum) { + case ESRCH: + return "Table does not exist"; + case ENOENT: + return "Anchor or Ruleset does not exist"; + default: + return strerror(errnum); + } +} diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c new file mode 100644 index 0000000000000..f3a1efdd06ba3 --- /dev/null +++ b/sbin/pfctl/pfctl_table.c @@ -0,0 +1,634 @@ +/* $OpenBSD: pfctl_table.c,v 1.67 2008/06/10 20:55:02 mcbride Exp $ */ + +/* + * Copyright (c) 2002 Cedric Berger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfctl_parser.h" +#include "pfctl.h" + +extern void usage(void); +static int pfctl_table(int, char *[], char *, const char *, char *, + const char *, int); +static void print_table(struct pfr_table *, int, int); +static void print_tstats(struct pfr_tstats *, int); +static int load_addr(struct pfr_buffer *, int, char *[], char *, int); +static void print_addrx(struct pfr_addr *, struct pfr_addr *, int); +static void print_astats(struct pfr_astats *, int); +static void radix_perror(void); +static void xprintf(int, const char *, ...); +static void print_iface(struct pfi_kif *, int); + +static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = { + { "In/Block:", "In/Pass:", "In/XPass:" }, + { "Out/Block:", "Out/Pass:", "Out/XPass:" } +}; + +static const char *istats_text[2][2][2] = { + { { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } }, + { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } } +}; + +#define RVTEST(fct) do { \ + if ((!(opts & PF_OPT_NOACTION) || \ + (opts & PF_OPT_DUMMYACTION)) && \ + (fct)) { \ + radix_perror(); \ + goto _error; \ + } \ + } while (0) + +#define CREATE_TABLE do { \ + table.pfrt_flags |= PFR_TFLAG_PERSIST; \ + if ((!(opts & PF_OPT_NOACTION) || \ + (opts & PF_OPT_DUMMYACTION)) && \ + (pfr_add_tables(&table, 1, &nadd, flags)) && \ + (errno != EPERM)) { \ + radix_perror(); \ + goto _error; \ + } \ + if (nadd) { \ + warn_namespace_collision(table.pfrt_name); \ + xprintf(opts, "%d table created", nadd); \ + if (opts & PF_OPT_NOACTION) \ + return (0); \ + } \ + table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \ + } while(0) + +int +pfctl_clear_tables(const char *anchor, int opts) +{ + return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts); +} + +int +pfctl_show_tables(const char *anchor, int opts) +{ + return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts); +} + +int +pfctl_command_tables(int argc, char *argv[], char *tname, + const char *command, char *file, const char *anchor, int opts) +{ + if (tname == NULL || command == NULL) + usage(); + return pfctl_table(argc, argv, tname, command, file, anchor, opts); +} + +int +pfctl_table(int argc, char *argv[], char *tname, const char *command, + char *file, const char *anchor, int opts) +{ + struct pfr_table table; + struct pfr_buffer b, b2; + struct pfr_addr *a, *a2; + int nadd = 0, ndel = 0, nchange = 0, nzero = 0; + int rv = 0, flags = 0, nmatch = 0; + void *p; + + if (command == NULL) + usage(); + if (opts & PF_OPT_NOACTION) + flags |= PFR_FLAG_DUMMY; + + bzero(&b, sizeof(b)); + bzero(&b2, sizeof(b2)); + bzero(&table, sizeof(table)); + if (tname != NULL) { + if (strlen(tname) >= PF_TABLE_NAME_SIZE) + usage(); + if (strlcpy(table.pfrt_name, tname, + sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name)) + errx(1, "pfctl_table: strlcpy"); + } + if (strlcpy(table.pfrt_anchor, anchor, + sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor)) + errx(1, "pfctl_table: strlcpy"); + + if (!strcmp(command, "-F")) { + if (argc || file != NULL) + usage(); + RVTEST(pfr_clr_tables(&table, &ndel, flags)); + xprintf(opts, "%d tables deleted", ndel); + } else if (!strcmp(command, "-s")) { + b.pfrb_type = (opts & PF_OPT_VERBOSE2) ? + PFRB_TSTATS : PFRB_TABLES; + if (argc || file != NULL) + usage(); + for (;;) { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + if (opts & PF_OPT_VERBOSE2) + RVTEST(pfr_get_tstats(&table, + b.pfrb_caddr, &b.pfrb_size, flags)); + else + RVTEST(pfr_get_tables(&table, + b.pfrb_caddr, &b.pfrb_size, flags)); + if (b.pfrb_size <= b.pfrb_msize) + break; + } + + if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0) + pfctl_print_title("TABLES:"); + + PFRB_FOREACH(p, &b) + if (opts & PF_OPT_VERBOSE2) + print_tstats(p, opts & PF_OPT_DEBUG); + else + print_table(p, opts & PF_OPT_VERBOSE, + opts & PF_OPT_DEBUG); + } else if (!strcmp(command, "kill")) { + if (argc || file != NULL) + usage(); + RVTEST(pfr_del_tables(&table, 1, &ndel, flags)); + xprintf(opts, "%d table deleted", ndel); + } else if (!strcmp(command, "flush")) { + if (argc || file != NULL) + usage(); + RVTEST(pfr_clr_addrs(&table, &ndel, flags)); + xprintf(opts, "%d addresses deleted", ndel); + } else if (!strcmp(command, "add")) { + b.pfrb_type = PFRB_ADDRS; + if (load_addr(&b, argc, argv, file, 0)) + goto _error; + CREATE_TABLE; + if (opts & PF_OPT_VERBOSE) + flags |= PFR_FLAG_FEEDBACK; + RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size, + &nadd, flags)); + xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size); + if (opts & PF_OPT_VERBOSE) + PFRB_FOREACH(a, &b) + if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); + } else if (!strcmp(command, "delete")) { + b.pfrb_type = PFRB_ADDRS; + if (load_addr(&b, argc, argv, file, 0)) + goto _error; + if (opts & PF_OPT_VERBOSE) + flags |= PFR_FLAG_FEEDBACK; + RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size, + &ndel, flags)); + xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size); + if (opts & PF_OPT_VERBOSE) + PFRB_FOREACH(a, &b) + if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); + } else if (!strcmp(command, "replace")) { + b.pfrb_type = PFRB_ADDRS; + if (load_addr(&b, argc, argv, file, 0)) + goto _error; + CREATE_TABLE; + if (opts & PF_OPT_VERBOSE) + flags |= PFR_FLAG_FEEDBACK; + for (;;) { + int sz2 = b.pfrb_msize; + + RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size, + &sz2, &nadd, &ndel, &nchange, flags)); + if (sz2 <= b.pfrb_msize) { + b.pfrb_size = sz2; + break; + } else + pfr_buf_grow(&b, sz2); + } + if (nadd) + xprintf(opts, "%d addresses added", nadd); + if (ndel) + xprintf(opts, "%d addresses deleted", ndel); + if (nchange) + xprintf(opts, "%d addresses changed", nchange); + if (!nadd && !ndel && !nchange) + xprintf(opts, "no changes"); + if (opts & PF_OPT_VERBOSE) + PFRB_FOREACH(a, &b) + if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); + } else if (!strcmp(command, "expire")) { + const char *errstr; + u_int lifetime; + + b.pfrb_type = PFRB_ASTATS; + b2.pfrb_type = PFRB_ADDRS; + if (argc != 1 || file != NULL) + usage(); + lifetime = strtonum(*argv, 0, UINT_MAX, &errstr); + if (errstr) + errx(1, "expiry time: %s", errstr); + for (;;) { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + RVTEST(pfr_get_astats(&table, b.pfrb_caddr, + &b.pfrb_size, flags)); + if (b.pfrb_size <= b.pfrb_msize) + break; + } + PFRB_FOREACH(p, &b) { + ((struct pfr_astats *)p)->pfras_a.pfra_fback = 0; + if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero > + lifetime) + if (pfr_buf_add(&b2, + &((struct pfr_astats *)p)->pfras_a)) + err(1, "duplicate buffer"); + } + + if (opts & PF_OPT_VERBOSE) + flags |= PFR_FLAG_FEEDBACK; + RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size, + &ndel, flags)); + xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size); + if (opts & PF_OPT_VERBOSE) + PFRB_FOREACH(a, &b2) + if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); + } else if (!strcmp(command, "show")) { + b.pfrb_type = (opts & PF_OPT_VERBOSE) ? + PFRB_ASTATS : PFRB_ADDRS; + if (argc || file != NULL) + usage(); + for (;;) { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + if (opts & PF_OPT_VERBOSE) + RVTEST(pfr_get_astats(&table, b.pfrb_caddr, + &b.pfrb_size, flags)); + else + RVTEST(pfr_get_addrs(&table, b.pfrb_caddr, + &b.pfrb_size, flags)); + if (b.pfrb_size <= b.pfrb_msize) + break; + } + PFRB_FOREACH(p, &b) + if (opts & PF_OPT_VERBOSE) + print_astats(p, opts & PF_OPT_USEDNS); + else + print_addrx(p, NULL, opts & PF_OPT_USEDNS); + } else if (!strcmp(command, "test")) { + b.pfrb_type = PFRB_ADDRS; + b2.pfrb_type = PFRB_ADDRS; + + if (load_addr(&b, argc, argv, file, 1)) + goto _error; + if (opts & PF_OPT_VERBOSE2) { + flags |= PFR_FLAG_REPLACE; + PFRB_FOREACH(a, &b) + if (pfr_buf_add(&b2, a)) + err(1, "duplicate buffer"); + } + RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size, + &nmatch, flags)); + xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size); + if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2)) + PFRB_FOREACH(a, &b) + if (a->pfra_fback == PFR_FB_MATCH) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); + if (opts & PF_OPT_VERBOSE2) { + a2 = NULL; + PFRB_FOREACH(a, &b) { + a2 = pfr_buf_next(&b2, a2); + print_addrx(a2, a, opts & PF_OPT_USEDNS); + } + } + if (nmatch < b.pfrb_size) + rv = 2; + } else if (!strcmp(command, "zero")) { + if (argc || file != NULL) + usage(); + flags |= PFR_FLAG_ADDRSTOO; + RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags)); + xprintf(opts, "%d table/stats cleared", nzero); + } else + warnx("pfctl_table: unknown command '%s'", command); + goto _cleanup; + +_error: + rv = -1; +_cleanup: + pfr_buf_clear(&b); + pfr_buf_clear(&b2); + return (rv); +} + +void +print_table(struct pfr_table *ta, int verbose, int debug) +{ + if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE)) + return; + if (verbose) { + printf("%c%c%c%c%c%c%c\t%s", + (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-', + (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-', + (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-', + (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-', + (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-', + (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-', + (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-', + ta->pfrt_name); + if (ta->pfrt_anchor[0]) + printf("\t%s", ta->pfrt_anchor); + puts(""); + } else + puts(ta->pfrt_name); +} + +void +print_tstats(struct pfr_tstats *ts, int debug) +{ + time_t time = ts->pfrts_tzero; + int dir, op; + + if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE)) + return; + print_table(&ts->pfrts_t, 1, debug); + printf("\tAddresses: %d\n", ts->pfrts_cnt); + printf("\tCleared: %s", ctime(&time)); + printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n", + ts->pfrts_refcnt[PFR_REFCNT_ANCHOR], + ts->pfrts_refcnt[PFR_REFCNT_RULE]); + printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n", + (unsigned long long)ts->pfrts_nomatch, + (unsigned long long)ts->pfrts_match); + for (dir = 0; dir < PFR_DIR_MAX; dir++) + for (op = 0; op < PFR_OP_TABLE_MAX; op++) + printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", + stats_text[dir][op], + (unsigned long long)ts->pfrts_packets[dir][op], + (unsigned long long)ts->pfrts_bytes[dir][op]); +} + +int +load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file, + int nonetwork) +{ + while (argc--) + if (append_addr(b, *argv++, nonetwork)) { + if (errno) + warn("cannot decode %s", argv[-1]); + return (-1); + } + if (pfr_buf_load(b, file, nonetwork, append_addr)) { + warn("cannot load %s", file); + return (-1); + } + return (0); +} + +void +print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns) +{ + char ch, buf[256] = "{error}"; + char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' }; + unsigned int fback, hostnet; + + fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback; + ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?'; + hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32; + inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf)); + printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf); + if (ad->pfra_net < hostnet) + printf("/%d", ad->pfra_net); + if (rad != NULL && fback != PFR_FB_NONE) { + if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf)) + errx(1, "print_addrx: strlcpy"); + inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf)); + printf("\t%c%s", (rad->pfra_not?'!':' '), buf); + if (rad->pfra_net < hostnet) + printf("/%d", rad->pfra_net); + } + if (rad != NULL && fback == PFR_FB_NONE) + printf("\t nomatch"); + if (dns && ad->pfra_net == hostnet) { + char host[NI_MAXHOST]; + union sockaddr_union sa; + + strlcpy(host, "?", sizeof(host)); + bzero(&sa, sizeof(sa)); + sa.sa.sa_family = ad->pfra_af; + if (sa.sa.sa_family == AF_INET) { + sa.sa.sa_len = sizeof(sa.sin); + sa.sin.sin_addr = ad->pfra_ip4addr; + } else { + sa.sa.sa_len = sizeof(sa.sin6); + sa.sin6.sin6_addr = ad->pfra_ip6addr; + } + if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host), + NULL, 0, NI_NAMEREQD) == 0) + printf("\t(%s)", host); + } + printf("\n"); +} + +void +print_astats(struct pfr_astats *as, int dns) +{ + time_t time = as->pfras_tzero; + int dir, op; + + print_addrx(&as->pfras_a, NULL, dns); + printf("\tCleared: %s", ctime(&time)); + if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT) + return; + for (dir = 0; dir < PFR_DIR_MAX; dir++) + for (op = 0; op < PFR_OP_ADDR_MAX; op++) + printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", + stats_text[dir][op], + (unsigned long long)as->pfras_packets[dir][op], + (unsigned long long)as->pfras_bytes[dir][op]); +} + +void +radix_perror(void) +{ + extern char *__progname; + fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno)); +} + +int +pfctl_define_table(char *name, int flags, int addrs, const char *anchor, + struct pfr_buffer *ab, u_int32_t ticket) +{ + struct pfr_table tbl; + + bzero(&tbl, sizeof(tbl)); + if (strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)) >= + sizeof(tbl.pfrt_name) || strlcpy(tbl.pfrt_anchor, anchor, + sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor)) + errx(1, "pfctl_define_table: strlcpy"); + tbl.pfrt_flags = flags; + + return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL, + NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0); +} + +void +warn_namespace_collision(const char *filter) +{ + struct pfr_buffer b; + struct pfr_table *t; + const char *name = NULL, *lastcoll; + int coll = 0; + + bzero(&b, sizeof(b)); + b.pfrb_type = PFRB_TABLES; + for (;;) { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + if (pfr_get_tables(NULL, b.pfrb_caddr, + &b.pfrb_size, PFR_FLAG_ALLRSETS)) + err(1, "pfr_get_tables"); + if (b.pfrb_size <= b.pfrb_msize) + break; + } + PFRB_FOREACH(t, &b) { + if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE)) + continue; + if (filter != NULL && strcmp(filter, t->pfrt_name)) + continue; + if (!t->pfrt_anchor[0]) + name = t->pfrt_name; + else if (name != NULL && !strcmp(name, t->pfrt_name)) { + coll++; + lastcoll = name; + name = NULL; + } + } + if (coll == 1) + warnx("warning: namespace collision with <%s> global table.", + lastcoll); + else if (coll > 1) + warnx("warning: namespace collisions with %d global tables.", + coll); + pfr_buf_clear(&b); +} + +void +xprintf(int opts, const char *fmt, ...) +{ + va_list args; + + if (opts & PF_OPT_QUIET) + return; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if (opts & PF_OPT_DUMMYACTION) + fprintf(stderr, " (dummy).\n"); + else if (opts & PF_OPT_NOACTION) + fprintf(stderr, " (syntax only).\n"); + else + fprintf(stderr, ".\n"); +} + + +/* interface stuff */ + +int +pfctl_show_ifaces(const char *filter, int opts) +{ + struct pfr_buffer b; + struct pfi_kif *p; + int i = 0; + + bzero(&b, sizeof(b)); + b.pfrb_type = PFRB_IFACES; + for (;;) { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) { + radix_perror(); + return (1); + } + if (b.pfrb_size <= b.pfrb_msize) + break; + i++; + } + if (opts & PF_OPT_SHOWALL) + pfctl_print_title("INTERFACES:"); + PFRB_FOREACH(p, &b) + print_iface(p, opts); + return (0); +} + +void +print_iface(struct pfi_kif *p, int opts) +{ + time_t tzero = p->pfik_tzero; + int i, af, dir, act; + + printf("%s", p->pfik_name); + if (opts & PF_OPT_VERBOSE) { + if (p->pfik_flags & PFI_IFLAG_SKIP) + printf(" (skip)"); + } + printf("\n"); + + if (!(opts & PF_OPT_VERBOSE2)) + return; + printf("\tCleared: %s", ctime(&tzero)); + printf("\tReferences: %-18d\n", p->pfik_rulerefs); + for (i = 0; i < 8; i++) { + af = (i>>2) & 1; + dir = (i>>1) &1; + act = i & 1; + printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n", + istats_text[af][dir][act], + (unsigned long long)p->pfik_packets[af][dir][act], + (unsigned long long)p->pfik_bytes[af][dir][act]); + } +} diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index dded119d6d4d9..7f01768f24cb4 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -354,6 +354,9 @@ MAN= aac.4 \ pcii.4 \ pcm.4 \ pcn.4 \ + pf.4 \ + pflog.4 \ + pfsync.4 \ pim.4 \ polling.4 \ ppbus.4 \ diff --git a/share/man/man4/pf.4 b/share/man/man4/pf.4 new file mode 100644 index 0000000000000..635078dbee249 --- /dev/null +++ b/share/man/man4/pf.4 @@ -0,0 +1,1175 @@ +.\" $OpenBSD: pf.4,v 1.62 2008/09/10 14:57:37 jmc Exp $ +.\" +.\" Copyright (C) 2001, Kjell Wooding. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 29 2012 +.Dt PF 4 +.Os +.Sh NAME +.Nm pf +.Nd packet filter +.Sh SYNOPSIS +.Cd "device pf" +.Sh DESCRIPTION +Packet filtering takes place in the kernel. +A pseudo-device, +.Pa /dev/pf , +allows userland processes to control the +behavior of the packet filter through an +.Xr ioctl 2 +interface. +There are commands to enable and disable the filter, load rulesets, +add and remove individual rules or state table entries, +and retrieve statistics. +The most commonly used functions are covered by +.Xr pfctl 8 . +.Pp +Manipulations like loading a ruleset that involve more than a single +.Xr ioctl 2 +call require a so-called +.Em ticket , +which prevents the occurrence of +multiple concurrent manipulations. +.Pp +Fields of +.Xr ioctl 2 +parameter structures that refer to packet data (like +addresses and ports) are generally expected in network byte-order. +.Pp +Rules and address tables are contained in so-called +.Em anchors . +When servicing an +.Xr ioctl 2 +request, if the anchor field of the argument structure is empty, +the kernel will use the default anchor (i.e., the main ruleset) +in operations. +Anchors are specified by name and may be nested, with components +separated by +.Sq / +characters, similar to how file system hierarchies are laid out. +The final component of the anchor path is the anchor under which +operations will be performed. +.Sh SYSCTL VARIABLES AND LOADER TUNABLES +The following +.Xr loader 8 +tunables are available. +.Bl -tag -width indent +.It Va net.pf.states_hashsize +Size of hash tables that store states. +Should be power of 2. +Default value is 32768. +.It Va net.pf.source_nodes_hashsize +Size of hash table that store source nodes. +Should be power of 2. +Default value is 8192. +.El +.Pp +Read only +.Xr sysctl 8 +variables with matching names are provided to obtain current values +at runtime. +.Sh IOCTL INTERFACE +.Nm +supports the following +.Xr ioctl 2 +commands, available through +.Aq Pa net/pfvar.h : +.Bl -tag -width xxxxxx +.It Dv DIOCSTART +Start the packet filter. +.It Dv DIOCSTOP +Stop the packet filter. +.It Dv DIOCSTARTALTQ +Start the ALTQ bandwidth control system (see +.Xr altq 9 ) . +.It Dv DIOCSTOPALTQ +Stop the ALTQ bandwidth control system. +.It Dv DIOCBEGINADDRS Fa "struct pfioc_pooladdr *pp" +.Bd -literal +struct pfioc_pooladdr { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + u_int32_t r_num; + u_int8_t r_action; + u_int8_t r_last; + u_int8_t af; + char anchor[MAXPATHLEN]; + struct pf_pooladdr addr; +}; +.Ed +.Pp +Clear the buffer address pool and get a +.Va ticket +for subsequent +.Dv DIOCADDADDR , +.Dv DIOCADDRULE , +and +.Dv DIOCCHANGERULE +calls. +.It Dv DIOCADDADDR Fa "struct pfioc_pooladdr *pp" +.Pp +Add the pool address +.Va addr +to the buffer address pool to be used in the following +.Dv DIOCADDRULE +or +.Dv DIOCCHANGERULE +call. +All other members of the structure are ignored. +.It Dv DIOCADDRULE Fa "struct pfioc_rule *pr" +.Bd -literal +struct pfioc_rule { + u_int32_t action; + u_int32_t ticket; + u_int32_t pool_ticket; + u_int32_t nr; + char anchor[MAXPATHLEN]; + char anchor_call[MAXPATHLEN]; + struct pf_rule rule; +}; +.Ed +.Pp +Add +.Va rule +at the end of the inactive ruleset. +This call requires a +.Va ticket +obtained through a preceding +.Dv DIOCXBEGIN +call and a +.Va pool_ticket +obtained through a +.Dv DIOCBEGINADDRS +call. +.Dv DIOCADDADDR +must also be called if any pool addresses are required. +The optional +.Va anchor +name indicates the anchor in which to append the rule. +.Va nr +and +.Va action +are ignored. +.It Dv DIOCADDALTQ Fa "struct pfioc_altq *pa" +Add an ALTQ discipline or queue. +.Bd -literal +struct pfioc_altq { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + struct pf_altq altq; +}; +.Ed +.It Dv DIOCGETRULES Fa "struct pfioc_rule *pr" +Get a +.Va ticket +for subsequent +.Dv DIOCGETRULE +calls and the number +.Va nr +of rules in the active ruleset. +.It Dv DIOCGETRULE Fa "struct pfioc_rule *pr" +Get a +.Va rule +by its number +.Va nr +using the +.Va ticket +obtained through a preceding +.Dv DIOCGETRULES +call. +If +.Va action +is set to +.Dv PF_GET_CLR_CNTR , +the per-rule statistics on the requested rule are cleared. +.It Dv DIOCGETADDRS Fa "struct pfioc_pooladdr *pp" +Get a +.Va ticket +for subsequent +.Dv DIOCGETADDR +calls and the number +.Va nr +of pool addresses in the rule specified with +.Va r_action , +.Va r_num , +and +.Va anchor . +.It Dv DIOCGETADDR Fa "struct pfioc_pooladdr *pp" +Get the pool address +.Va addr +by its number +.Va nr +from the rule specified with +.Va r_action , +.Va r_num , +and +.Va anchor +using the +.Va ticket +obtained through a preceding +.Dv DIOCGETADDRS +call. +.It Dv DIOCGETALTQS Fa "struct pfioc_altq *pa" +Get a +.Va ticket +for subsequent +.Dv DIOCGETALTQ +calls and the number +.Va nr +of queues in the active list. +.It Dv DIOCGETALTQ Fa "struct pfioc_altq *pa" +Get the queueing discipline +.Va altq +by its number +.Va nr +using the +.Va ticket +obtained through a preceding +.Dv DIOCGETALTQS +call. +.It Dv DIOCGETQSTATS Fa "struct pfioc_qstats *pq" +Get the statistics on a queue. +.Bd -literal +struct pfioc_qstats { + u_int32_t ticket; + u_int32_t nr; + void *buf; + int nbytes; + u_int8_t scheduler; +}; +.Ed +.Pp +This call fills in a pointer to the buffer of statistics +.Va buf , +of length +.Va nbytes , +for the queue specified by +.Va nr . +.It Dv DIOCGETRULESETS Fa "struct pfioc_ruleset *pr" +.Bd -literal +struct pfioc_ruleset { + u_int32_t nr; + char path[MAXPATHLEN]; + char name[PF_ANCHOR_NAME_SIZE]; +}; +.Ed +.Pp +Get the number +.Va nr +of rulesets (i.e., anchors) directly attached to the anchor named by +.Va path +for use in subsequent +.Dv DIOCGETRULESET +calls. +Nested anchors, since they are not directly attached to the given +anchor, will not be included. +This ioctl returns +.Er EINVAL +if the given anchor does not exist. +.It Dv DIOCGETRULESET Fa "struct pfioc_ruleset *pr" +Get a ruleset (i.e., an anchor) +.Va name +by its number +.Va nr +from the given anchor +.Va path , +the maximum number of which can be obtained from a preceding +.Dv DIOCGETRULESETS +call. +This ioctl returns +.Er EINVAL +if the given anchor does not exist or +.Er EBUSY +if another process is concurrently updating a ruleset. +.It Dv DIOCADDSTATE Fa "struct pfioc_state *ps" +Add a state entry. +.Bd -literal +struct pfioc_state { + struct pfsync_state state; +}; +.Ed +.It Dv DIOCGETSTATE Fa "struct pfioc_state *ps" +Extract the entry identified by the +.Va id +and +.Va creatorid +fields of the +.Va state +structure from the state table. +.It Dv DIOCKILLSTATES Fa "struct pfioc_state_kill *psk" +Remove matching entries from the state table. +This ioctl returns the number of killed states in +.Va psk_killed . +.Bd -literal +struct pfioc_state_kill { + struct pf_state_cmp psk_pfcmp; + sa_family_t psk_af; + int psk_proto; + struct pf_rule_addr psk_src; + struct pf_rule_addr psk_dst; + char psk_ifname[IFNAMSIZ]; + char psk_label[PF_RULE_LABEL_SIZE]; + u_int psk_killed; +}; +.Ed +.It Dv DIOCCLRSTATES Fa "struct pfioc_state_kill *psk" +Clear all states. +It works like +.Dv DIOCKILLSTATES , +but ignores the +.Va psk_af , +.Va psk_proto , +.Va psk_src , +and +.Va psk_dst +fields of the +.Vt pfioc_state_kill +structure. +.It Dv DIOCSETSTATUSIF Fa "struct pfioc_if *pi" +Specify the interface for which statistics are accumulated. +.Bd -literal +struct pfioc_if { + char ifname[IFNAMSIZ]; +}; +.Ed +.It Dv DIOCGETSTATUS Fa "struct pf_status *s" +Get the internal packet filter statistics. +.Bd -literal +struct pf_status { + u_int64_t counters[PFRES_MAX]; + u_int64_t lcounters[LCNT_MAX]; + u_int64_t fcounters[FCNT_MAX]; + u_int64_t scounters[SCNT_MAX]; + u_int64_t pcounters[2][2][3]; + u_int64_t bcounters[2][2]; + u_int32_t running; + u_int32_t states; + u_int32_t src_nodes; + u_int32_t since; + u_int32_t debug; + u_int32_t hostid; + char ifname[IFNAMSIZ]; + u_int8_t pf_chksum[MD5_DIGEST_LENGTH]; +}; +.Ed +.It Dv DIOCCLRSTATUS +Clear the internal packet filter statistics. +.It Dv DIOCNATLOOK Fa "struct pfioc_natlook *pnl" +Look up a state table entry by source and destination addresses and ports. +.Bd -literal +struct pfioc_natlook { + struct pf_addr saddr; + struct pf_addr daddr; + struct pf_addr rsaddr; + struct pf_addr rdaddr; + u_int16_t sport; + u_int16_t dport; + u_int16_t rsport; + u_int16_t rdport; + sa_family_t af; + u_int8_t proto; + u_int8_t direction; +}; +.Ed +.It Dv DIOCSETDEBUG Fa "u_int32_t *level" +Set the debug level. +.Bd -literal +enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, + PF_DEBUG_NOISY }; +.Ed +.It Dv DIOCGETSTATES Fa "struct pfioc_states *ps" +Get state table entries. +.Bd -literal +struct pfioc_states { + int ps_len; + union { + caddr_t psu_buf; + struct pf_state *psu_states; + } ps_u; +#define ps_buf ps_u.psu_buf +#define ps_states ps_u.psu_states +}; +.Ed +.Pp +If +.Va ps_len +is non-zero on entry, as many states as possible that can fit into this +size will be copied into the supplied buffer +.Va ps_states . +On exit, +.Va ps_len +is always set to the total size required to hold all state table entries +(i.e., it is set to +.Li sizeof(struct pf_state) * nr ) . +.It Dv DIOCCHANGERULE Fa "struct pfioc_rule *pcr" +Add or remove the +.Va rule +in the ruleset specified by +.Va rule.action . +.Pp +The type of operation to be performed is indicated by +.Va action , +which can be any of the following: +.Bd -literal +enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, + PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, + PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; +.Ed +.Pp +.Va ticket +must be set to the value obtained with +.Dv PF_CHANGE_GET_TICKET +for all actions except +.Dv PF_CHANGE_GET_TICKET . +.Va pool_ticket +must be set to the value obtained with the +.Dv DIOCBEGINADDRS +call for all actions except +.Dv PF_CHANGE_REMOVE +and +.Dv PF_CHANGE_GET_TICKET . +.Va anchor +indicates to which anchor the operation applies. +.Va nr +indicates the rule number against which +.Dv PF_CHANGE_ADD_BEFORE , +.Dv PF_CHANGE_ADD_AFTER , +or +.Dv PF_CHANGE_REMOVE +actions are applied. +.\" It Dv DIOCCHANGEALTQ Fa "struct pfioc_altq *pcr" +.It Dv DIOCCHANGEADDR Fa "struct pfioc_pooladdr *pca" +Add or remove the pool address +.Va addr +from the rule specified by +.Va r_action , +.Va r_num , +and +.Va anchor . +.It Dv DIOCSETTIMEOUT Fa "struct pfioc_tm *pt" +.Bd -literal +struct pfioc_tm { + int timeout; + int seconds; +}; +.Ed +.Pp +Set the state timeout of +.Va timeout +to +.Va seconds . +The old value will be placed into +.Va seconds . +For possible values of +.Va timeout , +consult the +.Dv PFTM_* +values in +.Aq Pa net/pfvar.h . +.It Dv DIOCGETTIMEOUT Fa "struct pfioc_tm *pt" +Get the state timeout of +.Va timeout . +The value will be placed into the +.Va seconds +field. +.It Dv DIOCCLRRULECTRS +Clear per-rule statistics. +.It Dv DIOCSETLIMIT Fa "struct pfioc_limit *pl" +Set the hard limits on the memory pools used by the packet filter. +.Bd -literal +struct pfioc_limit { + int index; + unsigned limit; +}; + +enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, + PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; +.Ed +.It Dv DIOCGETLIMIT Fa "struct pfioc_limit *pl" +Get the hard +.Va limit +for the memory pool indicated by +.Va index . +.It Dv DIOCRCLRTABLES Fa "struct pfioc_table *io" +Clear all tables. +All the ioctls that manipulate radix tables +use the same structure described below. +For +.Dv DIOCRCLRTABLES , +.Va pfrio_ndel +contains on exit the number of tables deleted. +.Bd -literal +struct pfioc_table { + struct pfr_table pfrio_table; + void *pfrio_buffer; + int pfrio_esize; + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; +#define pfrio_exists pfrio_nadd +#define pfrio_nzero pfrio_nadd +#define pfrio_nmatch pfrio_nadd +#define pfrio_naddr pfrio_size2 +#define pfrio_setflag pfrio_size2 +#define pfrio_clrflag pfrio_nadd +.Ed +.It Dv DIOCRADDTABLES Fa "struct pfioc_table *io" +Create one or more tables. +On entry, +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_table +containing at least +.Vt pfrio_size +elements. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_table . +On exit, +.Va pfrio_nadd +contains the number of tables effectively created. +.Bd -literal +struct pfr_table { + char pfrt_anchor[MAXPATHLEN]; + char pfrt_name[PF_TABLE_NAME_SIZE]; + u_int32_t pfrt_flags; + u_int8_t pfrt_fback; +}; +.Ed +.It Dv DIOCRDELTABLES Fa "struct pfioc_table *io" +Delete one or more tables. +On entry, +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_table +containing at least +.Vt pfrio_size +elements. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_table . +On exit, +.Va pfrio_ndel +contains the number of tables effectively deleted. +.It Dv DIOCRGETTABLES Fa "struct pfioc_table *io" +Get the list of all tables. +On entry, +.Va pfrio_buffer[pfrio_size] +contains a valid writeable buffer for +.Vt pfr_table +structures. +On exit, +.Va pfrio_size +contains the number of tables written into the buffer. +If the buffer is too small, the kernel does not store anything but just +returns the required buffer size, without error. +.It Dv DIOCRGETTSTATS Fa "struct pfioc_table *io" +This call is like +.Dv DIOCRGETTABLES +but is used to get an array of +.Vt pfr_tstats +structures. +.Bd -literal +struct pfr_tstats { + struct pfr_table pfrts_t; + u_int64_t pfrts_packets + [PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_bytes + [PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_match; + u_int64_t pfrts_nomatch; + long pfrts_tzero; + int pfrts_cnt; + int pfrts_refcnt[PFR_REFCNT_MAX]; +}; +#define pfrts_name pfrts_t.pfrt_name +#define pfrts_flags pfrts_t.pfrt_flags +.Ed +.It Dv DIOCRCLRTSTATS Fa "struct pfioc_table *io" +Clear the statistics of one or more tables. +On entry, +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_table +containing at least +.Vt pfrio_size +elements. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_table . +On exit, +.Va pfrio_nzero +contains the number of tables effectively cleared. +.It Dv DIOCRCLRADDRS Fa "struct pfioc_table *io" +Clear all addresses in a table. +On entry, +.Va pfrio_table +contains the table to clear. +On exit, +.Va pfrio_ndel +contains the number of addresses removed. +.It Dv DIOCRADDADDRS Fa "struct pfioc_table *io" +Add one or more addresses to a table. +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_addr +containing at least +.Vt pfrio_size +elements to add to the table. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_addr . +On exit, +.Va pfrio_nadd +contains the number of addresses effectively added. +.Bd -literal +struct pfr_addr { + union { + struct in_addr _pfra_ip4addr; + struct in6_addr _pfra_ip6addr; + } pfra_u; + u_int8_t pfra_af; + u_int8_t pfra_net; + u_int8_t pfra_not; + u_int8_t pfra_fback; +}; +#define pfra_ip4addr pfra_u._pfra_ip4addr +#define pfra_ip6addr pfra_u._pfra_ip6addr +.Ed +.It Dv DIOCRDELADDRS Fa "struct pfioc_table *io" +Delete one or more addresses from a table. +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_addr +containing at least +.Vt pfrio_size +elements to delete from the table. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_addr . +On exit, +.Va pfrio_ndel +contains the number of addresses effectively deleted. +.It Dv DIOCRSETADDRS Fa "struct pfioc_table *io" +Replace the content of a table by a new address list. +This is the most complicated command, which uses all the structure members. +.Pp +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_addr +containing at least +.Vt pfrio_size +elements which become the new contents of the table. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_addr . +Additionally, if +.Va pfrio_size2 +is non-zero, +.Va pfrio_buffer[pfrio_size..pfrio_size2] +must be a writeable buffer, into which the kernel can copy the +addresses that have been deleted during the replace operation. +On exit, +.Va pfrio_ndel , +.Va pfrio_nadd , +and +.Va pfrio_nchange +contain the number of addresses deleted, added, and changed by the +kernel. +If +.Va pfrio_size2 +was set on entry, +.Va pfrio_size2 +will point to the size of the buffer used, exactly like +.Dv DIOCRGETADDRS . +.It Dv DIOCRGETADDRS Fa "struct pfioc_table *io" +Get all the addresses of a table. +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer[pfrio_size] +contains a valid writeable buffer for +.Vt pfr_addr +structures. +On exit, +.Va pfrio_size +contains the number of addresses written into the buffer. +If the buffer was too small, the kernel does not store anything but just +returns the required buffer size, without returning an error. +.It Dv DIOCRGETASTATS Fa "struct pfioc_table *io" +This call is like +.Dv DIOCRGETADDRS +but is used to get an array of +.Vt pfr_astats +structures. +.Bd -literal +struct pfr_astats { + struct pfr_addr pfras_a; + u_int64_t pfras_packets + [PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_bytes + [PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + long pfras_tzero; +}; +.Ed +.It Dv DIOCRCLRASTATS Fa "struct pfioc_table *io" +Clear the statistics of one or more addresses. +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_addr +containing at least +.Vt pfrio_size +elements to be cleared from the table. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_addr . +On exit, +.Va pfrio_nzero +contains the number of addresses effectively cleared. +.It Dv DIOCRTSTADDRS Fa "struct pfioc_table *io" +Test if the given addresses match a table. +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_addr +containing at least +.Vt pfrio_size +elements, each of which will be tested for a match in the table. +.Vt pfrio_esize +must be the size of +.Vt struct pfr_addr . +On exit, the kernel updates the +.Vt pfr_addr +array by setting the +.Va pfra_fback +member appropriately. +.It Dv DIOCRSETTFLAGS Fa "struct pfioc_table *io" +Change the +.Dv PFR_TFLAG_CONST +or +.Dv PFR_TFLAG_PERSIST +flags of a table. +On entry, +.Va pfrio_buffer +must point to an array of +.Vt struct pfr_table +containing at least +.Vt pfrio_size +elements. +.Va pfrio_esize +must be the size of +.Vt struct pfr_table . +.Va pfrio_setflag +must contain the flags to add, while +.Va pfrio_clrflag +must contain the flags to remove. +On exit, +.Va pfrio_nchange +and +.Va pfrio_ndel +contain the number of tables altered or deleted by the kernel. +Yes, tables can be deleted if one removes the +.Dv PFR_TFLAG_PERSIST +flag of an unreferenced table. +.It Dv DIOCRINADEFINE Fa "struct pfioc_table *io" +Defines a table in the inactive set. +On entry, +.Va pfrio_table +contains the table ID and +.Va pfrio_buffer[pfrio_size] +contains an array of +.Vt pfr_addr +structures to put in the table. +A valid ticket must also be supplied to +.Va pfrio_ticket . +On exit, +.Va pfrio_nadd +contains 0 if the table was already defined in the inactive list +or 1 if a new table has been created. +.Va pfrio_naddr +contains the number of addresses effectively put in the table. +.It Dv DIOCXBEGIN Fa "struct pfioc_trans *io" +.Bd -literal +struct pfioc_trans { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + struct pfioc_trans_e { + int rs_num; + char anchor[MAXPATHLEN]; + u_int32_t ticket; + } *array; +}; +.Ed +.Pp +Clear all the inactive rulesets specified in the +.Vt pfioc_trans_e +array. +For each ruleset, a ticket is returned for subsequent "add rule" ioctls, +as well as for the +.Dv DIOCXCOMMIT +and +.Dv DIOCXROLLBACK +calls. +.Pp +Ruleset types, identified by +.Va rs_num , +include the following: +.Pp +.Bl -tag -width PF_RULESET_FILTER -offset ind -compact +.It Dv PF_RULESET_SCRUB +Scrub (packet normalization) rules. +.It Dv PF_RULESET_FILTER +Filter rules. +.It Dv PF_RULESET_NAT +NAT (Network Address Translation) rules. +.It Dv PF_RULESET_BINAT +Bidirectional NAT rules. +.It Dv PF_RULESET_RDR +Redirect rules. +.It Dv PF_RULESET_ALTQ +ALTQ disciplines. +.It Dv PF_RULESET_TABLE +Address tables. +.El +.It Dv DIOCXCOMMIT Fa "struct pfioc_trans *io" +Atomically switch a vector of inactive rulesets to the active rulesets. +This call is implemented as a standard two-phase commit, which will either +fail for all rulesets or completely succeed. +All tickets need to be valid. +This ioctl returns +.Er EBUSY +if another process is concurrently updating some of the same rulesets. +.It Dv DIOCXROLLBACK Fa "struct pfioc_trans *io" +Clean up the kernel by undoing all changes that have taken place on the +inactive rulesets since the last +.Dv DIOCXBEGIN . +.Dv DIOCXROLLBACK +will silently ignore rulesets for which the ticket is invalid. +.It Dv DIOCSETHOSTID Fa "u_int32_t *hostid" +Set the host ID, which is used by +.Xr pfsync 4 +to identify which host created state table entries. +.It Dv DIOCOSFPFLUSH +Flush the passive OS fingerprint table. +.It Dv DIOCOSFPADD Fa "struct pf_osfp_ioctl *io" +.Bd -literal +struct pf_osfp_ioctl { + struct pf_osfp_entry { + SLIST_ENTRY(pf_osfp_entry) fp_entry; + pf_osfp_t fp_os; + char fp_class_nm[PF_OSFP_LEN]; + char fp_version_nm[PF_OSFP_LEN]; + char fp_subtype_nm[PF_OSFP_LEN]; + } fp_os; + pf_tcpopts_t fp_tcpopts; + u_int16_t fp_wsize; + u_int16_t fp_psize; + u_int16_t fp_mss; + u_int16_t fp_flags; + u_int8_t fp_optcnt; + u_int8_t fp_wscale; + u_int8_t fp_ttl; + int fp_getnum; +}; +.Ed +.Pp +Add a passive OS fingerprint to the table. +Set +.Va fp_os.fp_os +to the packed fingerprint, +.Va fp_os.fp_class_nm +to the name of the class (Linux, Windows, etc), +.Va fp_os.fp_version_nm +to the name of the version (NT, 95, 98), and +.Va fp_os.fp_subtype_nm +to the name of the subtype or patchlevel. +The members +.Va fp_mss , +.Va fp_wsize , +.Va fp_psize , +.Va fp_ttl , +.Va fp_optcnt , +and +.Va fp_wscale +are set to the TCP MSS, the TCP window size, the IP length, the IP TTL, +the number of TCP options, and the TCP window scaling constant of the +TCP SYN packet, respectively. +.Pp +The +.Va fp_flags +member is filled according to the +.Aq Pa net/pfvar.h +include file +.Dv PF_OSFP_* +defines. +The +.Va fp_tcpopts +member contains packed TCP options. +Each option uses +.Dv PF_OSFP_TCPOPT_BITS +bits in the packed value. +Options include any of +.Dv PF_OSFP_TCPOPT_NOP , +.Dv PF_OSFP_TCPOPT_SACK , +.Dv PF_OSFP_TCPOPT_WSCALE , +.Dv PF_OSFP_TCPOPT_MSS , +or +.Dv PF_OSFP_TCPOPT_TS . +.Pp +The +.Va fp_getnum +member is not used with this ioctl. +.Pp +The structure's slack space must be zeroed for correct operation; +.Xr memset 3 +the whole structure to zero before filling and sending to the kernel. +.It Dv DIOCOSFPGET Fa "struct pf_osfp_ioctl *io" +Get the passive OS fingerprint number +.Va fp_getnum +from the kernel's fingerprint list. +The rest of the structure members will come back filled. +Get the whole list by repeatedly incrementing the +.Va fp_getnum +number until the ioctl returns +.Er EBUSY . +.It Dv DIOCGETSRCNODES Fa "struct pfioc_src_nodes *psn" +.Bd -literal +struct pfioc_src_nodes { + int psn_len; + union { + caddr_t psu_buf; + struct pf_src_node *psu_src_nodes; + } psn_u; +#define psn_buf psn_u.psu_buf +#define psn_src_nodes psn_u.psu_src_nodes +}; +.Ed +.Pp +Get the list of source nodes kept by sticky addresses and source +tracking. +The ioctl must be called once with +.Va psn_len +set to 0. +If the ioctl returns without error, +.Va psn_len +will be set to the size of the buffer required to hold all the +.Va pf_src_node +structures held in the table. +A buffer of this size should then be allocated, and a pointer to this buffer +placed in +.Va psn_buf . +The ioctl must then be called again to fill this buffer with the actual +source node data. +After that call, +.Va psn_len +will be set to the length of the buffer actually used. +.It Dv DIOCCLRSRCNODES +Clear the tree of source tracking nodes. +.It Dv DIOCIGETIFACES Fa "struct pfioc_iface *io" +Get the list of interfaces and interface drivers known to +.Nm . +All the ioctls that manipulate interfaces +use the same structure described below: +.Bd -literal +struct pfioc_iface { + char pfiio_name[IFNAMSIZ]; + void *pfiio_buffer; + int pfiio_esize; + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; +.Ed +.Pp +If not empty, +.Va pfiio_name +can be used to restrict the search to a specific interface or driver. +.Va pfiio_buffer[pfiio_size] +is the user-supplied buffer for returning the data. +On entry, +.Va pfiio_size +contains the number of +.Vt pfi_kif +entries that can fit into the buffer. +The kernel will replace this value by the real number of entries it wants +to return. +.Va pfiio_esize +should be set to +.Li sizeof(struct pfi_kif) . +.Pp +The data is returned in the +.Vt pfi_kif +structure described below: +.Bd -literal +struct pfi_kif { + RB_ENTRY(pfi_kif) pfik_tree; + char pfik_name[IFNAMSIZ]; + u_int64_t pfik_packets[2][2][2]; + u_int64_t pfik_bytes[2][2][2]; + u_int32_t pfik_tzero; + int pfik_flags; + struct pf_state_tree_lan_ext pfik_lan_ext; + struct pf_state_tree_ext_gwy pfik_ext_gwy; + TAILQ_ENTRY(pfi_kif) pfik_w_states; + void *pfik_ah_cookie; + struct ifnet *pfik_ifp; + struct ifg_group *pfik_group; + int pfik_states; + int pfik_rules; + TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; +}; +.Ed +.It Dv DIOCSETIFFLAG Fa "struct pfioc_iface *io" +Set the user setable flags (described above) of the +.Nm +internal interface description. +The filtering process is the same as for +.Dv DIOCIGETIFACES . +.Bd -literal +#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ +.Ed +.It Dv DIOCCLRIFFLAG Fa "struct pfioc_iface *io" +Works as +.Dv DIOCSETIFFLAG +above but clears the flags. +.It Dv DIOCKILLSRCNODES Fa "struct pfioc_iface *io" +Explicitly remove source tracking nodes. +.El +.Sh FILES +.Bl -tag -width /dev/pf -compact +.It Pa /dev/pf +packet filtering device. +.El +.Sh EXAMPLES +The following example demonstrates how to use the +.Dv DIOCNATLOOK +command to find the internal host/port of a NATed connection: +.Bd -literal +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u_int32_t +read_address(const char *s) +{ + int a, b, c, d; + + sscanf(s, "%i.%i.%i.%i", &a, &b, &c, &d); + return htonl(a << 24 | b << 16 | c << 8 | d); +} + +void +print_address(u_int32_t a) +{ + a = ntohl(a); + printf("%d.%d.%d.%d", a >> 24 & 255, a >> 16 & 255, + a >> 8 & 255, a & 255); +} + +int +main(int argc, char *argv[]) +{ + struct pfioc_natlook nl; + int dev; + + if (argc != 5) { + printf("%s \\n", + argv[0]); + return 1; + } + + dev = open("/dev/pf", O_RDWR); + if (dev == -1) + err(1, "open(\\"/dev/pf\\") failed"); + + memset(&nl, 0, sizeof(struct pfioc_natlook)); + nl.saddr.v4.s_addr = read_address(argv[1]); + nl.sport = htons(atoi(argv[2])); + nl.daddr.v4.s_addr = read_address(argv[3]); + nl.dport = htons(atoi(argv[4])); + nl.af = AF_INET; + nl.proto = IPPROTO_TCP; + nl.direction = PF_IN; + + if (ioctl(dev, DIOCNATLOOK, &nl)) + err(1, "DIOCNATLOOK"); + + printf("internal host "); + print_address(nl.rsaddr.v4.s_addr); + printf(":%u\\n", ntohs(nl.rsport)); + return 0; +} +.Ed +.Sh SEE ALSO +.Xr ioctl 2 , +.Xr altq 4 , +.Xr if_bridge 4 , +.Xr pflog 4 , +.Xr pflow 4 , +.Xr pfsync 4 , +.Xr pfctl 8 , +.Xr altq 9 +.Sh HISTORY +The +.Nm +packet filtering mechanism first appeared in +.Ox 3.0 +and then +.Fx 5.2 . +.Pp +This implementation matches +.Ox 4.5 . diff --git a/share/man/man4/pflog.4 b/share/man/man4/pflog.4 new file mode 100644 index 0000000000000..c1039a3ed1428 --- /dev/null +++ b/share/man/man4/pflog.4 @@ -0,0 +1,107 @@ +.\" $OpenBSD: pflog.4,v 1.10 2007/05/31 19:19:51 jmc Exp $ +.\" +.\" Copyright (c) 2001 Tobias Weingartner +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 31 2007 +.Dt PFLOG 4 +.Os +.Sh NAME +.Nm pflog +.Nd packet filter logging interface +.Sh SYNOPSIS +.Cd "device pflog" +.Sh DESCRIPTION +The +.Nm pflog +interface is a device which makes visible all packets logged by +the packet filter, +.Xr pf 4 . +Logged packets can easily be monitored in real +time by invoking +.Xr tcpdump 1 +on the +.Nm +interface, or stored to disk using +.Xr pflogd 8 . +.Pp +The pflog0 interface is created automatically at boot if both +.Xr pf 4 +and +.Xr pflogd 8 +are enabled; +further instances can be created using +.Xr ifconfig 8 . +.Pp +Each packet retrieved on this interface has a header associated +with it of length +.Dv PFLOG_HDRLEN . +This header documents the address family, interface name, rule +number, reason, action, and direction of the packet that was logged. +This structure, defined in +.Aq Pa net/if_pflog.h +looks like +.Bd -literal -offset indent +struct pfloghdr { + u_int8_t length; + sa_family_t af; + u_int8_t action; + u_int8_t reason; + char ifname[IFNAMSIZ]; + char ruleset[PF_RULESET_NAME_SIZE]; + u_int32_t rulenr; + u_int32_t subrulenr; + uid_t uid; + pid_t pid; + uid_t rule_uid; + pid_t rule_pid; + u_int8_t dir; + u_int8_t pad[3]; +}; +.Ed +.Sh EXAMPLES +Create a +.Nm +interface +and monitor all packets logged on it: +.Bd -literal -offset indent +# ifconfig pflog1 up +# tcpdump -n -e -ttt -i pflog1 +.Ed +.Sh SEE ALSO +.Xr inet 4 , +.Xr inet6 4 , +.Xr netintro 4 , +.Xr pf 4 , +.Xr ifconfig 8 , +.Xr pflogd 8 , +.Xr tcpdump 1 +.Sh HISTORY +The +.Nm +device first appeared in +.Ox 3.0 . +.\" .Sh BUGS +.\" Anything here? diff --git a/share/man/man4/pfsync.4 b/share/man/man4/pfsync.4 new file mode 100644 index 0000000000000..b00bf9d0fc789 --- /dev/null +++ b/share/man/man4/pfsync.4 @@ -0,0 +1,229 @@ +.\" $OpenBSD: pfsync.4,v 1.28 2009/02/17 10:05:18 dlg Exp $ +.\" +.\" Copyright (c) 2002 Michael Shalayeff +.\" Copyright (c) 2003-2004 Ryan McBride +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND, +.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 20 2011 +.Dt PFSYNC 4 +.Os +.Sh NAME +.Nm pfsync +.Nd packet filter state table sychronisation interface +.Sh SYNOPSIS +.Cd "device pfsync" +.Sh DESCRIPTION +The +.Nm +interface is a pseudo-device which exposes certain changes to the state +table used by +.Xr pf 4 . +State changes can be viewed by invoking +.Xr tcpdump 1 +on the +.Nm +interface. +If configured with a physical synchronisation interface, +.Nm +will also send state changes out on that interface, +and insert state changes received on that interface from other systems +into the state table. +.Pp +By default, all local changes to the state table are exposed via +.Nm . +State changes from packets received by +.Nm +over the network are not rebroadcast. +Updates to states created by a rule marked with the +.Ar no-sync +keyword are ignored by the +.Nm +interface (see +.Xr pf.conf 5 +for details). +.Pp +The +.Nm +interface will attempt to collapse multiple state updates into a single +packet where possible. +The maximum number of times a single state can be updated before a +.Nm +packet will be sent out is controlled by the +.Ar maxupd +parameter to ifconfig +(see +.Xr ifconfig 8 +and the example below for more details). +The sending out of a +.Nm +packet will be delayed by a maximum of one second. +.Sh NETWORK SYNCHRONISATION +States can be synchronised between two or more firewalls using this +interface, by specifying a synchronisation interface using +.Xr ifconfig 8 . +For example, the following command sets fxp0 as the synchronisation +interface: +.Bd -literal -offset indent +# ifconfig pfsync0 syncdev fxp0 +.Ed +.Pp +By default, state change messages are sent out on the synchronisation +interface using IP multicast packets to the 244.0.0.240 group address. +An alternative destination address for +.Nm +packets can be specified using the +.Ic syncpeer +keyword. +This can be used in combination with +.Xr ipsec 4 +to protect the synchronisation traffic. +In such a configuration, the syncdev should be set to the +.Xr enc 4 +interface, as this is where the traffic arrives when it is decapsulated, +e.g.: +.Bd -literal -offset indent +# ifconfig pfsync0 syncpeer 10.0.0.2 syncdev enc0 +.Ed +.Pp +It is important that the pfsync traffic be well secured +as there is no authentication on the protocol and it would +be trivial to spoof packets which create states, bypassing the pf ruleset. +Either run the pfsync protocol on a trusted network \- ideally a network +dedicated to pfsync messages such as a crossover cable between two firewalls, +or specify a peer address and protect the traffic with +.Xr ipsec 4 . +.Pp +.Nm +has the following +.Xr sysctl 8 +tunables: +.Bl -tag -width ".Va net.pfsync" +.It Va net.pfsync.carp_demotion_factor +Value added to +.Va net.inet.carp.demotion +while +.Nm +tries to perform its bulk update. +See +.Xr carp 4 +for more information. +Default value is 240. +.El +.Sh EXAMPLES +.Nm +and +.Xr carp 4 +can be used together to provide automatic failover of a pair of firewalls +configured in parallel. +One firewall will handle all traffic until it dies, is shut down, or is +manually demoted, at which point the second firewall will take over +automatically. +.Pp +Both firewalls in this example have three +.Xr sis 4 +interfaces. +sis0 is the external interface, on the 10.0.0.0/24 subnet; sis1 is the +internal interface, on the 192.168.0.0/24 subnet; and sis2 is the +.Nm +interface, using the 192.168.254.0/24 subnet. +A crossover cable connects the two firewalls via their sis2 interfaces. +On all three interfaces, firewall A uses the .254 address, while firewall B +uses .253. +The interfaces are configured as follows (firewall A unless otherwise +indicated): +.Pp +Interfaces configuration in +.Pa /etc/rc.conf : +.Bd -literal -offset indent +network_interfaces="lo0 sis0 sis1 sis2" +ifconfig_sis0="10.0.0.254/24" +ifconfig_sis0_alias0="inet 10.0.0.1/24 vhid 1 pass foo" +ifconfig_sis1="192.168.0.254/24" +ifconfig_sis1_alias0="inet 192.168.0.1/24 vhid 2 pass bar" +ifconfig_sis2="192.168.254.254/24" +pfsync_enable="YES" +pfsync_syncdev="sis2" +.Ed +.Pp +.Xr pf 4 +must also be configured to allow +.Nm +and +.Xr carp 4 +traffic through. +The following should be added to the top of +.Pa /etc/pf.conf : +.Bd -literal -offset indent +pass quick on { sis2 } proto pfsync keep state (no-sync) +pass on { sis0 sis1 } proto carp keep state (no-sync) +.Ed +.Pp +It is preferable that one firewall handle the forwarding of all the traffic, +therefore the +.Ar advskew +on the backup firewall's +.Xr carp 4 +vhids should be set to something higher than +the primary's. +For example, if firewall B is the backup, its +carp1 configuration would look like this: +would look like this: +.Bd -literal -offset indent +ifconfig_sis1_alias0="inet 192.168.0.1/24 vhid 2 pass bar advskew 100" +.Ed +.Pp +The following must also be added to +.Pa /etc/sysctl.conf : +.Bd -literal -offset indent +net.inet.carp.preempt=1 +.Ed +.Sh SEE ALSO +.Xr bpf 4 , +.Xr carp 4 , +.Xr enc 4 , +.Xr inet 4 , +.Xr inet6 4 , +.Xr ipsec 4 , +.Xr netintro 4 , +.Xr pf 4 , +.Xr pf.conf 5 , +.Xr protocols 5 , +.Xr rc.conf 5 , +.Xr ifconfig 8 , +.Xr tcpdump 1 +.Sh HISTORY +The +.Nm +device first appeared in +.Ox 3.3 . +It was first imported to +.Fx 5.3 . +.Pp +The +.Nm +protocol and kernel implementation were significantly modified in +.Fx 9.0 . +The newer protocol is not compatible with older one and will not interoperate +with it. diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile index c7a3c592d04b8..2c56fc89b2a12 100644 --- a/share/man/man5/Makefile +++ b/share/man/man5/Makefile @@ -50,6 +50,8 @@ MAN= acct.5 \ passwd.5 \ pbm.5 \ periodic.conf.5 \ + pf.conf.5 \ + pf.os.5 \ phones.5 \ portindex.5 \ portsnap.conf.5 \ diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5 new file mode 100644 index 0000000000000..fc86111372138 --- /dev/null +++ b/share/man/man5/pf.conf.5 @@ -0,0 +1,3066 @@ +.\" $FreeBSD$ +.\" $OpenBSD: pf.conf.5,v 1.406 2009/01/31 19:37:12 sobrado Exp $ +.\" +.\" Copyright (c) 2002, Daniel Hartmeier +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" - Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" - Redistributions in binary form must reproduce the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer in the documentation and/or other materials provided +.\" with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 29 2012 +.Dt PF.CONF 5 +.Os +.Sh NAME +.Nm pf.conf +.Nd packet filter configuration file +.Sh DESCRIPTION +The +.Xr pf 4 +packet filter modifies, drops or passes packets according to rules or +definitions specified in +.Nm pf.conf . +.Sh STATEMENT ORDER +There are seven types of statements in +.Nm pf.conf : +.Bl -tag -width xxxx +.It Cm Macros +User-defined variables may be defined and used later, simplifying +the configuration file. +Macros must be defined before they are referenced in +.Nm pf.conf . +.It Cm Tables +Tables provide a mechanism for increasing the performance and flexibility of +rules with large numbers of source or destination addresses. +.It Cm Options +Options tune the behaviour of the packet filtering engine. +.It Cm Traffic Normalization Li (e.g. Em scrub ) +Traffic normalization protects internal machines against inconsistencies +in Internet protocols and implementations. +.It Cm Queueing +Queueing provides rule-based bandwidth control. +.It Cm Translation Li (Various forms of NAT) +Translation rules specify how addresses are to be mapped or redirected to +other addresses. +.It Cm Packet Filtering +Packet filtering provides rule-based blocking or passing of packets. +.El +.Pp +With the exception of +.Cm macros +and +.Cm tables , +the types of statements should be grouped and appear in +.Nm pf.conf +in the order shown above, as this matches the operation of the underlying +packet filtering engine. +By default +.Xr pfctl 8 +enforces this order (see +.Ar set require-order +below). +.Pp +Comments can be put anywhere in the file using a hash mark +.Pq Sq # , +and extend to the end of the current line. +.Pp +Additional configuration files can be included with the +.Ic include +keyword, for example: +.Bd -literal -offset indent +include "/etc/pf/sub.filter.conf" +.Ed +.Sh MACROS +Macros can be defined that will later be expanded in context. +Macro names must start with a letter, and may contain letters, digits +and underscores. +Macro names may not be reserved words (for example +.Ar pass , +.Ar in , +.Ar out ) . +Macros are not expanded inside quotes. +.Pp +For example, +.Bd -literal -offset indent +ext_if = \&"kue0\&" +all_ifs = \&"{\&" $ext_if lo0 \&"}\&" +pass out on $ext_if from any to any +pass in on $ext_if proto tcp from any to any port 25 +.Ed +.Sh TABLES +Tables are named structures which can hold a collection of addresses and +networks. +Lookups against tables in +.Xr pf 4 +are relatively fast, making a single rule with tables much more efficient, +in terms of +processor usage and memory consumption, than a large number of rules which +differ only in IP address (either created explicitly or automatically by rule +expansion). +.Pp +Tables can be used as the source or destination of filter rules, +.Ar scrub +rules +or +translation rules such as +.Ar nat +or +.Ar rdr +(see below for details on the various rule types). +Tables can also be used for the redirect address of +.Ar nat +and +.Ar rdr +rules and in the routing options of filter rules, but only for +.Ar round-robin +pools. +.Pp +Tables can be defined with any of the following +.Xr pfctl 8 +mechanisms. +As with macros, reserved words may not be used as table names. +.Bl -tag -width "manually" +.It Ar manually +Persistent tables can be manually created with the +.Ar add +or +.Ar replace +option of +.Xr pfctl 8 , +before or after the ruleset has been loaded. +.It Pa pf.conf +Table definitions can be placed directly in this file, and loaded at the +same time as other rules are loaded, atomically. +Table definitions inside +.Nm pf.conf +use the +.Ar table +statement, and are especially useful to define non-persistent tables. +The contents of a pre-existing table defined without a list of addresses +to initialize it is not altered when +.Nm pf.conf +is loaded. +A table initialized with the empty list, +.Li { } , +will be cleared on load. +.El +.Pp +Tables may be defined with the following attributes: +.Bl -tag -width persist +.It Ar persist +The +.Ar persist +flag forces the kernel to keep the table even when no rules refer to it. +If the flag is not set, the kernel will automatically remove the table +when the last rule referring to it is flushed. +.It Ar const +The +.Ar const +flag prevents the user from altering the contents of the table once it +has been created. +Without that flag, +.Xr pfctl 8 +can be used to add or remove addresses from the table at any time, even +when running with +.Xr securelevel 7 += 2. +.It Ar counters +The +.Ar counters +flag enables per-address packet and byte counters which can be displayed with +.Xr pfctl 8 . +.El +.Pp +For example, +.Bd -literal -offset indent +table \*(Ltprivate\*(Gt const { 10/8, 172.16/12, 192.168/16 } +table \*(Ltbadhosts\*(Gt persist +block on fxp0 from { \*(Ltprivate\*(Gt, \*(Ltbadhosts\*(Gt } to any +.Ed +.Pp +creates a table called private, to hold RFC 1918 private network +blocks, and a table called badhosts, which is initially empty. +A filter rule is set up to block all traffic coming from addresses listed in +either table. +The private table cannot have its contents changed and the badhosts table +will exist even when no active filter rules reference it. +Addresses may later be added to the badhosts table, so that traffic from +these hosts can be blocked by using +.Bd -literal -offset indent +# pfctl -t badhosts -Tadd 204.92.77.111 +.Ed +.Pp +A table can also be initialized with an address list specified in one or more +external files, using the following syntax: +.Bd -literal -offset indent +table \*(Ltspam\*(Gt persist file \&"/etc/spammers\&" file \&"/etc/openrelays\&" +block on fxp0 from \*(Ltspam\*(Gt to any +.Ed +.Pp +The files +.Pa /etc/spammers +and +.Pa /etc/openrelays +list IP addresses, one per line. +Any lines beginning with a # are treated as comments and ignored. +In addition to being specified by IP address, hosts may also be +specified by their hostname. +When the resolver is called to add a hostname to a table, +.Em all +resulting IPv4 and IPv6 addresses are placed into the table. +IP addresses can also be entered in a table by specifying a valid interface +name, a valid interface group or the +.Em self +keyword, in which case all addresses assigned to the interface(s) will be +added to the table. +.Sh OPTIONS +.Xr pf 4 +may be tuned for various situations using the +.Ar set +command. +.Bl -tag -width xxxx +.It Ar set timeout +.Pp +.Bl -tag -width "src.track" -compact +.It Ar interval +Interval between purging expired states and fragments. +.It Ar frag +Seconds before an unassembled fragment is expired. +.It Ar src.track +Length of time to retain a source tracking entry after the last state +expires. +.El +.Pp +When a packet matches a stateful connection, the seconds to live for the +connection will be updated to that of the +.Ar proto.modifier +which corresponds to the connection state. +Each packet which matches this state will reset the TTL. +Tuning these values may improve the performance of the +firewall at the risk of dropping valid idle connections. +.Pp +.Bl -tag -width xxxx -compact +.It Ar tcp.first +The state after the first packet. +.It Ar tcp.opening +The state before the destination host ever sends a packet. +.It Ar tcp.established +The fully established state. +.It Ar tcp.closing +The state after the first FIN has been sent. +.It Ar tcp.finwait +The state after both FINs have been exchanged and the connection is closed. +Some hosts (notably web servers on Solaris) send TCP packets even after closing +the connection. +Increasing +.Ar tcp.finwait +(and possibly +.Ar tcp.closing ) +can prevent blocking of such packets. +.It Ar tcp.closed +The state after one endpoint sends an RST. +.El +.Pp +ICMP and UDP are handled in a fashion similar to TCP, but with a much more +limited set of states: +.Pp +.Bl -tag -width xxxx -compact +.It Ar udp.first +The state after the first packet. +.It Ar udp.single +The state if the source host sends more than one packet but the destination +host has never sent one back. +.It Ar udp.multiple +The state if both hosts have sent packets. +.It Ar icmp.first +The state after the first packet. +.It Ar icmp.error +The state after an ICMP error came back in response to an ICMP packet. +.El +.Pp +Other protocols are handled similarly to UDP: +.Pp +.Bl -tag -width xxxx -compact +.It Ar other.first +.It Ar other.single +.It Ar other.multiple +.El +.Pp +Timeout values can be reduced adaptively as the number of state table +entries grows. +.Pp +.Bl -tag -width xxxx -compact +.It Ar adaptive.start +When the number of state entries exceeds this value, adaptive scaling +begins. +All timeout values are scaled linearly with factor +(adaptive.end - number of states) / (adaptive.end - adaptive.start). +.It Ar adaptive.end +When reaching this number of state entries, all timeout values become +zero, effectively purging all state entries immediately. +This value is used to define the scale factor, it should not actually +be reached (set a lower state limit, see below). +.El +.Pp +Adaptive timeouts are enabled by default, with an adaptive.start value +equal to 60% of the state limit, and an adaptive.end value equal to +120% of the state limit. +They can be disabled by setting both adaptive.start and adaptive.end to 0. +.Pp +The adaptive timeout values can be defined both globally and for each rule. +When used on a per-rule basis, the values relate to the number of +states created by the rule, otherwise to the total number of +states. +.Pp +For example: +.Bd -literal -offset indent +set timeout tcp.first 120 +set timeout tcp.established 86400 +set timeout { adaptive.start 6000, adaptive.end 12000 } +set limit states 10000 +.Ed +.Pp +With 9000 state table entries, the timeout values are scaled to 50% +(tcp.first 60, tcp.established 43200). +.Pp +.It Ar set loginterface +Enable collection of packet and byte count statistics for the given +interface or interface group. +These statistics can be viewed using +.Bd -literal -offset indent +# pfctl -s info +.Ed +.Pp +In this example +.Xr pf 4 +collects statistics on the interface named dc0: +.Bd -literal -offset indent +set loginterface dc0 +.Ed +.Pp +One can disable the loginterface using: +.Bd -literal -offset indent +set loginterface none +.Ed +.Pp +.It Ar set limit +Sets hard limits on the memory pools used by the packet filter. +See +.Xr zone 9 +for an explanation of memory pools. +.Pp +For example, +.Bd -literal -offset indent +set limit states 20000 +.Ed +.Pp +sets the maximum number of entries in the memory pool used by state table +entries (generated by +.Ar pass +rules which do not specify +.Ar no state ) +to 20000. +Using +.Bd -literal -offset indent +set limit frags 20000 +.Ed +.Pp +sets the maximum number of entries in the memory pool used for fragment +reassembly (generated by +.Ar scrub +rules) to 20000. +Using +.Bd -literal -offset indent +set limit src-nodes 2000 +.Ed +.Pp +sets the maximum number of entries in the memory pool used for tracking +source IP addresses (generated by the +.Ar sticky-address +and +.Ar src.track +options) to 2000. +Using +.Bd -literal -offset indent +set limit tables 1000 +set limit table-entries 100000 +.Ed +.Pp +sets limits on the memory pools used by tables. +The first limits the number of tables that can exist to 1000. +The second limits the overall number of addresses that can be stored +in tables to 100000. +.Pp +Various limits can be combined on a single line: +.Bd -literal -offset indent +set limit { states 20000, frags 20000, src-nodes 2000 } +.Ed +.Pp +.It Ar set ruleset-optimization +.Bl -tag -width xxxxxxxx -compact +.It Ar none +Disable the ruleset optimizer. +.It Ar basic +Enable basic ruleset optimization. +This is the default behaviour. +Basic ruleset optimization does four things to improve the +performance of ruleset evaluations: +.Pp +.Bl -enum -compact +.It +remove duplicate rules +.It +remove rules that are a subset of another rule +.It +combine multiple rules into a table when advantageous +.It +re-order the rules to improve evaluation performance +.El +.Pp +.It Ar profile +Uses the currently loaded ruleset as a feedback profile to tailor the +ordering of quick rules to actual network traffic. +.El +.Pp +It is important to note that the ruleset optimizer will modify the ruleset +to improve performance. +A side effect of the ruleset modification is that per-rule accounting +statistics will have different meanings than before. +If per-rule accounting is important for billing purposes or whatnot, +either the ruleset optimizer should not be used or a label field should +be added to all of the accounting rules to act as optimization barriers. +.Pp +Optimization can also be set as a command-line argument to +.Xr pfctl 8 , +overriding the settings in +.Nm . +.It Ar set optimization +Optimize state timeouts for one of the following network environments: +.Pp +.Bl -tag -width xxxx -compact +.It Ar normal +A normal network environment. +Suitable for almost all networks. +.It Ar high-latency +A high-latency environment (such as a satellite connection). +.It Ar satellite +Alias for +.Ar high-latency . +.It Ar aggressive +Aggressively expire connections. +This can greatly reduce the memory usage of the firewall at the cost of +dropping idle connections early. +.It Ar conservative +Extremely conservative settings. +Avoid dropping legitimate connections at the +expense of greater memory utilization (possibly much greater on a busy +network) and slightly increased processor utilization. +.El +.Pp +For example: +.Bd -literal -offset indent +set optimization aggressive +.Ed +.Pp +.It Ar set block-policy +The +.Ar block-policy +option sets the default behaviour for the packet +.Ar block +action: +.Pp +.Bl -tag -width xxxxxxxx -compact +.It Ar drop +Packet is silently dropped. +.It Ar return +A TCP RST is returned for blocked TCP packets, +an ICMP UNREACHABLE is returned for blocked UDP packets, +and all other packets are silently dropped. +.El +.Pp +For example: +.Bd -literal -offset indent +set block-policy return +.Ed +.It Ar set state-policy +The +.Ar state-policy +option sets the default behaviour for states: +.Pp +.Bl -tag -width group-bound -compact +.It Ar if-bound +States are bound to interface. +.It Ar floating +States can match packets on any interfaces (the default). +.El +.Pp +For example: +.Bd -literal -offset indent +set state-policy if-bound +.Ed +.It Ar set state-defaults +The +.Ar state-defaults +option sets the state options for states created from rules +without an explicit +.Ar keep state . +For example: +.Bd -literal -offset indent +set state-defaults pflow, no-sync +.Ed +.It Ar set hostid +The 32-bit +.Ar hostid +identifies this firewall's state table entries to other firewalls +in a +.Xr pfsync 4 +failover cluster. +By default the hostid is set to a pseudo-random value, however it may be +desirable to manually configure it, for example to more easily identify the +source of state table entries. +.Bd -literal -offset indent +set hostid 1 +.Ed +.Pp +The hostid may be specified in either decimal or hexadecimal. +.It Ar set require-order +By default +.Xr pfctl 8 +enforces an ordering of the statement types in the ruleset to: +.Em options , +.Em normalization , +.Em queueing , +.Em translation , +.Em filtering . +Setting this option to +.Ar no +disables this enforcement. +There may be non-trivial and non-obvious implications to an out of +order ruleset. +Consider carefully before disabling the order enforcement. +.It Ar set fingerprints +Load fingerprints of known operating systems from the given filename. +By default fingerprints of known operating systems are automatically +loaded from +.Xr pf.os 5 +in +.Pa /etc +but can be overridden via this option. +Setting this option may leave a small period of time where the fingerprints +referenced by the currently active ruleset are inconsistent until the new +ruleset finishes loading. +.Pp +For example: +.Pp +.Dl set fingerprints \&"/etc/pf.os.devel\&" +.Pp +.It Ar set skip on Aq Ar ifspec +List interfaces for which packets should not be filtered. +Packets passing in or out on such interfaces are passed as if pf was +disabled, i.e. pf does not process them in any way. +This can be useful on loopback and other virtual interfaces, when +packet filtering is not desired and can have unexpected effects. +For example: +.Pp +.Dl set skip on lo0 +.Pp +.It Ar set debug +Set the debug +.Ar level +to one of the following: +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Ar none +Don't generate debug messages. +.It Ar urgent +Generate debug messages only for serious errors. +.It Ar misc +Generate debug messages for various errors. +.It Ar loud +Generate debug messages for common conditions. +.El +.El +.Sh TRAFFIC NORMALIZATION +Traffic normalization is used to sanitize packet content in such +a way that there are no ambiguities in packet interpretation on +the receiving side. +The normalizer does IP fragment reassembly to prevent attacks +that confuse intrusion detection systems by sending overlapping +IP fragments. +Packet normalization is invoked with the +.Ar scrub +directive. +.Pp +.Ar scrub +has the following options: +.Bl -tag -width xxxx +.It Ar no-df +Clears the +.Ar dont-fragment +bit from a matching IP packet. +Some operating systems are known to generate fragmented packets with the +.Ar dont-fragment +bit set. +This is particularly true with NFS. +.Ar Scrub +will drop such fragmented +.Ar dont-fragment +packets unless +.Ar no-df +is specified. +.Pp +Unfortunately some operating systems also generate their +.Ar dont-fragment +packets with a zero IP identification field. +Clearing the +.Ar dont-fragment +bit on packets with a zero IP ID may cause deleterious results if an +upstream router later fragments the packet. +Using the +.Ar random-id +modifier (see below) is recommended in combination with the +.Ar no-df +modifier to ensure unique IP identifiers. +.It Ar min-ttl Aq Ar number +Enforces a minimum TTL for matching IP packets. +.It Ar max-mss Aq Ar number +Enforces a maximum MSS for matching TCP packets. +.It Xo Ar set-tos Aq Ar string +.No \*(Ba Aq Ar number +.Xc +Enforces a +.Em TOS +for matching IP packets. +.Em TOS +may be +given as one of +.Ar lowdelay , +.Ar throughput , +.Ar reliability , +or as either hex or decimal. +.It Ar random-id +Replaces the IP identification field with random values to compensate +for predictable values generated by many hosts. +This option only applies to packets that are not fragmented +after the optional fragment reassembly. +.It Ar fragment reassemble +Using +.Ar scrub +rules, fragments can be reassembled by normalization. +In this case, fragments are buffered until they form a complete +packet, and only the completed packet is passed on to the filter. +The advantage is that filter rules have to deal only with complete +packets, and can ignore fragments. +The drawback of caching fragments is the additional memory cost. +But the full reassembly method is the only method that currently works +with NAT. +This is the default behavior of a +.Ar scrub +rule if no fragmentation modifier is supplied. +.It Ar fragment crop +The default fragment reassembly method is expensive, hence the option +to crop is provided. +In this case, +.Xr pf 4 +will track the fragments and cache a small range descriptor. +Duplicate fragments are dropped and overlaps are cropped. +Thus data will only occur once on the wire with ambiguities resolving to +the first occurrence. +Unlike the +.Ar fragment reassemble +modifier, fragments are not buffered, they are passed as soon as they +are received. +The +.Ar fragment crop +reassembly mechanism does not yet work with NAT. +.Pp +.It Ar fragment drop-ovl +This option is similar to the +.Ar fragment crop +modifier except that all overlapping or duplicate fragments will be +dropped, and all further corresponding fragments will be +dropped as well. +.It Ar reassemble tcp +Statefully normalizes TCP connections. +.Ar scrub reassemble tcp +rules may not have the direction (in/out) specified. +.Ar reassemble tcp +performs the following normalizations: +.Pp +.Bl -tag -width timeout -compact +.It ttl +Neither side of the connection is allowed to reduce their IP TTL. +An attacker may send a packet such that it reaches the firewall, affects +the firewall state, and expires before reaching the destination host. +.Ar reassemble tcp +will raise the TTL of all packets back up to the highest value seen on +the connection. +.It timestamp modulation +Modern TCP stacks will send a timestamp on every TCP packet and echo +the other endpoint's timestamp back to them. +Many operating systems will merely start the timestamp at zero when +first booted, and increment it several times a second. +The uptime of the host can be deduced by reading the timestamp and multiplying +by a constant. +Also observing several different timestamps can be used to count hosts +behind a NAT device. +And spoofing TCP packets into a connection requires knowing or guessing +valid timestamps. +Timestamps merely need to be monotonically increasing and not derived off a +guessable base time. +.Ar reassemble tcp +will cause +.Ar scrub +to modulate the TCP timestamps with a random number. +.It extended PAWS checks +There is a problem with TCP on long fat pipes, in that a packet might get +delayed for longer than it takes the connection to wrap its 32-bit sequence +space. +In such an occurrence, the old packet would be indistinguishable from a +new packet and would be accepted as such. +The solution to this is called PAWS: Protection Against Wrapped Sequence +numbers. +It protects against it by making sure the timestamp on each packet does +not go backwards. +.Ar reassemble tcp +also makes sure the timestamp on the packet does not go forward more +than the RFC allows. +By doing this, +.Xr pf 4 +artificially extends the security of TCP sequence numbers by 10 to 18 +bits when the host uses appropriately randomized timestamps, since a +blind attacker would have to guess the timestamp as well. +.El +.El +.Pp +For example, +.Bd -literal -offset indent +scrub in on $ext_if all fragment reassemble +.Ed +.Pp +The +.Ar no +option prefixed to a scrub rule causes matching packets to remain unscrubbed, +much in the same way as +.Ar drop quick +works in the packet filter (see below). +This mechanism should be used when it is necessary to exclude specific packets +from broader scrub rules. +.Sh QUEUEING +The ALTQ system is currently not available in the GENERIC kernel nor as +loadable modules. +In order to use the herein after called queueing options one has to use a +custom built kernel. +Please refer to +.Xr altq 4 +to learn about the related kernel options. +.Pp +Packets can be assigned to queues for the purpose of bandwidth +control. +At least two declarations are required to configure queues, and later +any packet filtering rule can reference the defined queues by name. +During the filtering component of +.Nm pf.conf , +the last referenced +.Ar queue +name is where any packets from +.Ar pass +rules will be queued, while for +.Ar block +rules it specifies where any resulting ICMP or TCP RST +packets should be queued. +The +.Ar scheduler +defines the algorithm used to decide which packets get delayed, dropped, or +sent out immediately. +There are three +.Ar schedulers +currently supported. +.Bl -tag -width xxxx +.It Ar cbq +Class Based Queueing. +.Ar Queues +attached to an interface build a tree, thus each +.Ar queue +can have further child +.Ar queues . +Each queue can have a +.Ar priority +and a +.Ar bandwidth +assigned. +.Ar Priority +mainly controls the time packets take to get sent out, while +.Ar bandwidth +has primarily effects on throughput. +.Ar cbq +achieves both partitioning and sharing of link bandwidth +by hierarchically structured classes. +Each class has its own +.Ar queue +and is assigned its share of +.Ar bandwidth . +A child class can borrow bandwidth from its parent class +as long as excess bandwidth is available +(see the option +.Ar borrow , +below). +.It Ar priq +Priority Queueing. +.Ar Queues +are flat attached to the interface, thus, +.Ar queues +cannot have further child +.Ar queues . +Each +.Ar queue +has a unique +.Ar priority +assigned, ranging from 0 to 15. +Packets in the +.Ar queue +with the highest +.Ar priority +are processed first. +.It Ar hfsc +Hierarchical Fair Service Curve. +.Ar Queues +attached to an interface build a tree, thus each +.Ar queue +can have further child +.Ar queues . +Each queue can have a +.Ar priority +and a +.Ar bandwidth +assigned. +.Ar Priority +mainly controls the time packets take to get sent out, while +.Ar bandwidth +primarily affects throughput. +.Ar hfsc +supports both link-sharing and guaranteed real-time services. +It employs a service curve based QoS model, +and its unique feature is an ability to decouple +.Ar delay +and +.Ar bandwidth +allocation. +.El +.Pp +The interfaces on which queueing should be activated are declared using +the +.Ar altq on +declaration. +.Ar altq on +has the following keywords: +.Bl -tag -width xxxx +.It Aq Ar interface +Queueing is enabled on the named interface. +.It Aq Ar scheduler +Specifies which queueing scheduler to use. +Currently supported values +are +.Ar cbq +for Class Based Queueing, +.Ar priq +for Priority Queueing and +.Ar hfsc +for the Hierarchical Fair Service Curve scheduler. +.It Ar bandwidth Aq Ar bw +The maximum bitrate for all queues on an +interface may be specified using the +.Ar bandwidth +keyword. +The value can be specified as an absolute value or as a +percentage of the interface bandwidth. +When using an absolute value, the suffixes +.Ar b , +.Ar Kb , +.Ar Mb , +and +.Ar Gb +are used to represent bits, kilobits, megabits, and +gigabits per second, respectively. +The value must not exceed the interface bandwidth. +If +.Ar bandwidth +is not specified, the interface bandwidth is used +(but take note that some interfaces do not know their bandwidth, +or can adapt their bandwidth rates). +.It Ar qlimit Aq Ar limit +The maximum number of packets held in the queue. +The default is 50. +.It Ar tbrsize Aq Ar size +Adjusts the size, in bytes, of the token bucket regulator. +If not specified, heuristics based on the +interface bandwidth are used to determine the size. +.It Ar queue Aq Ar list +Defines a list of subqueues to create on an interface. +.El +.Pp +In the following example, the interface dc0 +should queue up to 5Mbps in four second-level queues using +Class Based Queueing. +Those four queues will be shown in a later example. +.Bd -literal -offset indent +altq on dc0 cbq bandwidth 5Mb queue { std, http, mail, ssh } +.Ed +.Pp +Once interfaces are activated for queueing using the +.Ar altq +directive, a sequence of +.Ar queue +directives may be defined. +The name associated with a +.Ar queue +must match a queue defined in the +.Ar altq +directive (e.g. mail), or, except for the +.Ar priq +.Ar scheduler , +in a parent +.Ar queue +declaration. +The following keywords can be used: +.Bl -tag -width xxxx +.It Ar on Aq Ar interface +Specifies the interface the queue operates on. +If not given, it operates on all matching interfaces. +.It Ar bandwidth Aq Ar bw +Specifies the maximum bitrate to be processed by the queue. +This value must not exceed the value of the parent +.Ar queue +and can be specified as an absolute value or a percentage of the parent +queue's bandwidth. +If not specified, defaults to 100% of the parent queue's bandwidth. +The +.Ar priq +scheduler does not support bandwidth specification. +.It Ar priority Aq Ar level +Between queues a priority level can be set. +For +.Ar cbq +and +.Ar hfsc , +the range is 0 to 7 and for +.Ar priq , +the range is 0 to 15. +The default for all is 1. +.Ar Priq +queues with a higher priority are always served first. +.Ar Cbq +and +.Ar Hfsc +queues with a higher priority are preferred in the case of overload. +.It Ar qlimit Aq Ar limit +The maximum number of packets held in the queue. +The default is 50. +.El +.Pp +The +.Ar scheduler +can get additional parameters with +.Xo Aq Ar scheduler +.Pf ( Aq Ar parameters ) . +.Xc +Parameters are as follows: +.Bl -tag -width Fl +.It Ar default +Packets not matched by another queue are assigned to this one. +Exactly one default queue is required. +.It Ar red +Enable RED (Random Early Detection) on this queue. +RED drops packets with a probability proportional to the average +queue length. +.It Ar rio +Enables RIO on this queue. +RIO is RED with IN/OUT, thus running +RED two times more than RIO would achieve the same effect. +RIO is currently not supported in the GENERIC kernel. +.It Ar ecn +Enables ECN (Explicit Congestion Notification) on this queue. +ECN implies RED. +.El +.Pp +The +.Ar cbq +.Ar scheduler +supports an additional option: +.Bl -tag -width Fl +.It Ar borrow +The queue can borrow bandwidth from the parent. +.El +.Pp +The +.Ar hfsc +.Ar scheduler +supports some additional options: +.Bl -tag -width Fl +.It Ar realtime Aq Ar sc +The minimum required bandwidth for the queue. +.It Ar upperlimit Aq Ar sc +The maximum allowed bandwidth for the queue. +.It Ar linkshare Aq Ar sc +The bandwidth share of a backlogged queue. +.El +.Pp +.Aq Ar sc +is an acronym for +.Ar service curve . +.Pp +The format for service curve specifications is +.Ar ( m1 , d , m2 ) . +.Ar m2 +controls the bandwidth assigned to the queue. +.Ar m1 +and +.Ar d +are optional and can be used to control the initial bandwidth assignment. +For the first +.Ar d +milliseconds the queue gets the bandwidth given as +.Ar m1 , +afterwards the value given in +.Ar m2 . +.Pp +Furthermore, with +.Ar cbq +and +.Ar hfsc , +child queues can be specified as in an +.Ar altq +declaration, thus building a tree of queues using a part of +their parent's bandwidth. +.Pp +Packets can be assigned to queues based on filter rules by using the +.Ar queue +keyword. +Normally only one +.Ar queue +is specified; when a second one is specified it will instead be used for +packets which have a +.Em TOS +of +.Em lowdelay +and for TCP ACKs with no data payload. +.Pp +To continue the previous example, the examples below would specify the +four referenced +queues, plus a few child queues. +Interactive +.Xr ssh 1 +sessions get priority over bulk transfers like +.Xr scp 1 +and +.Xr sftp 1 . +The queues may then be referenced by filtering rules (see +.Sx PACKET FILTERING +below). +.Bd -literal +queue std bandwidth 10% cbq(default) +queue http bandwidth 60% priority 2 cbq(borrow red) \e + { employees, developers } +queue developers bandwidth 75% cbq(borrow) +queue employees bandwidth 15% +queue mail bandwidth 10% priority 0 cbq(borrow ecn) +queue ssh bandwidth 20% cbq(borrow) { ssh_interactive, ssh_bulk } +queue ssh_interactive bandwidth 50% priority 7 cbq(borrow) +queue ssh_bulk bandwidth 50% priority 0 cbq(borrow) + +block return out on dc0 inet all queue std +pass out on dc0 inet proto tcp from $developerhosts to any port 80 \e + queue developers +pass out on dc0 inet proto tcp from $employeehosts to any port 80 \e + queue employees +pass out on dc0 inet proto tcp from any to any port 22 \e + queue(ssh_bulk, ssh_interactive) +pass out on dc0 inet proto tcp from any to any port 25 \e + queue mail +.Ed +.Sh TRANSLATION +Translation rules modify either the source or destination address of the +packets associated with a stateful connection. +A stateful connection is automatically created to track packets matching +such a rule as long as they are not blocked by the filtering section of +.Nm pf.conf . +The translation engine modifies the specified address and/or port in the +packet, recalculates IP, TCP and UDP checksums as necessary, and passes it to +the packet filter for evaluation. +.Pp +Since translation occurs before filtering the filter +engine will see packets as they look after any +addresses and ports have been translated. +Filter rules will therefore have to filter based on the translated +address and port number. +Packets that match a translation rule are only automatically passed if +the +.Ar pass +modifier is given, otherwise they are +still subject to +.Ar block +and +.Ar pass +rules. +.Pp +The state entry created permits +.Xr pf 4 +to keep track of the original address for traffic associated with that state +and correctly direct return traffic for that connection. +.Pp +Various types of translation are possible with pf: +.Bl -tag -width xxxx +.It Ar binat +A +.Ar binat +rule specifies a bidirectional mapping between an external IP netblock +and an internal IP netblock. +.It Ar nat +A +.Ar nat +rule specifies that IP addresses are to be changed as the packet +traverses the given interface. +This technique allows one or more IP addresses +on the translating host to support network traffic for a larger range of +machines on an "inside" network. +Although in theory any IP address can be used on the inside, it is strongly +recommended that one of the address ranges defined by RFC 1918 be used. +These netblocks are: +.Bd -literal +10.0.0.0 - 10.255.255.255 (all of net 10, i.e., 10/8) +172.16.0.0 - 172.31.255.255 (i.e., 172.16/12) +192.168.0.0 - 192.168.255.255 (i.e., 192.168/16) +.Ed +.It Pa rdr +The packet is redirected to another destination and possibly a +different port. +.Ar rdr +rules can optionally specify port ranges instead of single ports. +rdr ... port 2000:2999 -\*(Gt ... port 4000 +redirects ports 2000 to 2999 (inclusive) to port 4000. +rdr ... port 2000:2999 -\*(Gt ... port 4000:* +redirects port 2000 to 4000, 2001 to 4001, ..., 2999 to 4999. +.El +.Pp +In addition to modifying the address, some translation rules may modify +source or destination ports for +.Xr tcp 4 +or +.Xr udp 4 +connections; implicitly in the case of +.Ar nat +rules and explicitly in the case of +.Ar rdr +rules. +Port numbers are never translated with a +.Ar binat +rule. +.Pp +Evaluation order of the translation rules is dependent on the type +of the translation rules and of the direction of a packet. +.Ar binat +rules are always evaluated first. +Then either the +.Ar rdr +rules are evaluated on an inbound packet or the +.Ar nat +rules on an outbound packet. +Rules of the same type are evaluated in the same order in which they +appear in the ruleset. +The first matching rule decides what action is taken. +.Pp +The +.Ar no +option prefixed to a translation rule causes packets to remain untranslated, +much in the same way as +.Ar drop quick +works in the packet filter (see below). +If no rule matches the packet it is passed to the filter engine unmodified. +.Pp +Translation rules apply only to packets that pass through +the specified interface, and if no interface is specified, +translation is applied to packets on all interfaces. +For instance, redirecting port 80 on an external interface to an internal +web server will only work for connections originating from the outside. +Connections to the address of the external interface from local hosts will +not be redirected, since such packets do not actually pass through the +external interface. +Redirections cannot reflect packets back through the interface they arrive +on, they can only be redirected to hosts connected to different interfaces +or to the firewall itself. +.Pp +Note that redirecting external incoming connections to the loopback +address, as in +.Bd -literal -offset indent +rdr on ne3 inet proto tcp to port smtp -\*(Gt 127.0.0.1 port spamd +.Ed +.Pp +will effectively allow an external host to connect to daemons +bound solely to the loopback address, circumventing the traditional +blocking of such connections on a real interface. +Unless this effect is desired, any of the local non-loopback addresses +should be used as redirection target instead, which allows external +connections only to daemons bound to this address or not bound to +any address. +.Pp +See +.Sx TRANSLATION EXAMPLES +below. +.Sh PACKET FILTERING +.Xr pf 4 +has the ability to +.Ar block +and +.Ar pass +packets based on attributes of their layer 3 (see +.Xr ip 4 +and +.Xr ip6 4 ) +and layer 4 (see +.Xr icmp 4 , +.Xr icmp6 4 , +.Xr tcp 4 , +.Xr udp 4 ) +headers. +In addition, packets may also be +assigned to queues for the purpose of bandwidth control. +.Pp +For each packet processed by the packet filter, the filter rules are +evaluated in sequential order, from first to last. +The last matching rule decides what action is taken. +If no rule matches the packet, the default action is to pass +the packet. +.Pp +The following actions can be used in the filter: +.Bl -tag -width xxxx +.It Ar block +The packet is blocked. +There are a number of ways in which a +.Ar block +rule can behave when blocking a packet. +The default behaviour is to +.Ar drop +packets silently, however this can be overridden or made +explicit either globally, by setting the +.Ar block-policy +option, or on a per-rule basis with one of the following options: +.Pp +.Bl -tag -width xxxx -compact +.It Ar drop +The packet is silently dropped. +.It Ar return-rst +This applies only to +.Xr tcp 4 +packets, and issues a TCP RST which closes the +connection. +.It Ar return-icmp +.It Ar return-icmp6 +This causes ICMP messages to be returned for packets which match the rule. +By default this is an ICMP UNREACHABLE message, however this +can be overridden by specifying a message as a code or number. +.It Ar return +This causes a TCP RST to be returned for +.Xr tcp 4 +packets and an ICMP UNREACHABLE for UDP and other packets. +.El +.Pp +Options returning ICMP packets currently have no effect if +.Xr pf 4 +operates on a +.Xr if_bridge 4 , +as the code to support this feature has not yet been implemented. +.Pp +The simplest mechanism to block everything by default and only pass +packets that match explicit rules is specify a first filter rule of: +.Bd -literal -offset indent +block all +.Ed +.It Ar pass +The packet is passed; +state is created unless the +.Ar no state +option is specified. +.El +.Pp +By default +.Xr pf 4 +filters packets statefully; the first time a packet matches a +.Ar pass +rule, a state entry is created; for subsequent packets the filter checks +whether the packet matches any state. +If it does, the packet is passed without evaluation of any rules. +After the connection is closed or times out, the state entry is automatically +removed. +.Pp +This has several advantages. +For TCP connections, comparing a packet to a state involves checking +its sequence numbers, as well as TCP timestamps if a +.Ar scrub reassemble tcp +rule applies to the connection. +If these values are outside the narrow windows of expected +values, the packet is dropped. +This prevents spoofing attacks, such as when an attacker sends packets with +a fake source address/port but does not know the connection's sequence +numbers. +Similarly, +.Xr pf 4 +knows how to match ICMP replies to states. +For example, +.Bd -literal -offset indent +pass out inet proto icmp all icmp-type echoreq +.Ed +.Pp +allows echo requests (such as those created by +.Xr ping 8 ) +out statefully, and matches incoming echo replies correctly to states. +.Pp +Also, looking up states is usually faster than evaluating rules. +If there are 50 rules, all of them are evaluated sequentially in O(n). +Even with 50000 states, only 16 comparisons are needed to match a +state, since states are stored in a binary search tree that allows +searches in O(log2 n). +.Pp +Furthermore, correct handling of ICMP error messages is critical to +many protocols, particularly TCP. +.Xr pf 4 +matches ICMP error messages to the correct connection, checks them against +connection parameters, and passes them if appropriate. +For example if an ICMP source quench message referring to a stateful TCP +connection arrives, it will be matched to the state and get passed. +.Pp +Finally, state tracking is required for +.Ar nat , binat No and Ar rdr +rules, in order to track address and port translations and reverse the +translation on returning packets. +.Pp +.Xr pf 4 +will also create state for other protocols which are effectively stateless by +nature. +UDP packets are matched to states using only host addresses and ports, +and other protocols are matched to states using only the host addresses. +.Pp +If stateless filtering of individual packets is desired, +the +.Ar no state +keyword can be used to specify that state will not be created +if this is the last matching rule. +A number of parameters can also be set to affect how +.Xr pf 4 +handles state tracking. +See +.Sx STATEFUL TRACKING OPTIONS +below for further details. +.Sh PARAMETERS +The rule parameters specify the packets to which a rule applies. +A packet always comes in on, or goes out through, one interface. +Most parameters are optional. +If a parameter is specified, the rule only applies to packets with +matching attributes. +Certain parameters can be expressed as lists, in which case +.Xr pfctl 8 +generates all needed rule combinations. +.Bl -tag -width xxxx +.It Ar in No or Ar out +This rule applies to incoming or outgoing packets. +If neither +.Ar in +nor +.Ar out +are specified, the rule will match packets in both directions. +.It Ar log +In addition to the action specified, a log message is generated. +Only the packet that establishes the state is logged, +unless the +.Ar no state +option is specified. +The logged packets are sent to a +.Xr pflog 4 +interface, by default +.Ar pflog0 . +This interface is monitored by the +.Xr pflogd 8 +logging daemon, which dumps the logged packets to the file +.Pa /var/log/pflog +in +.Xr pcap 3 +binary format. +.It Ar log (all) +Used to force logging of all packets for a connection. +This is not necessary when +.Ar no state +is explicitly specified. +As with +.Ar log , +packets are logged to +.Xr pflog 4 . +.It Ar log (user) +Logs the +.Ux +user ID of the user that owns the socket and the PID of the process that +has the socket open where the packet is sourced from or destined to +(depending on which socket is local). +This is in addition to the normal information logged. +.Pp +Only the first packet +logged via +.Ar log (all, user) +will have the user credentials logged when using stateful matching. +.It Ar log (to Aq Ar interface ) +Send logs to the specified +.Xr pflog 4 +interface instead of +.Ar pflog0 . +.It Ar quick +If a packet matches a rule which has the +.Ar quick +option set, this rule +is considered the last matching rule, and evaluation of subsequent rules +is skipped. +.It Ar on Aq Ar interface +This rule applies only to packets coming in on, or going out through, this +particular interface or interface group. +For more information on interface groups, +see the +.Ic group +keyword in +.Xr ifconfig 8 . +.It Aq Ar af +This rule applies only to packets of this address family. +Supported values are +.Ar inet +and +.Ar inet6 . +.It Ar proto Aq Ar protocol +This rule applies only to packets of this protocol. +Common protocols are +.Xr icmp 4 , +.Xr icmp6 4 , +.Xr tcp 4 , +and +.Xr udp 4 . +For a list of all the protocol name to number mappings used by +.Xr pfctl 8 , +see the file +.Em /etc/protocols . +.It Xo +.Ar from Aq Ar source +.Ar port Aq Ar source +.Ar os Aq Ar source +.Ar to Aq Ar dest +.Ar port Aq Ar dest +.Xc +This rule applies only to packets with the specified source and destination +addresses and ports. +.Pp +Addresses can be specified in CIDR notation (matching netblocks), as +symbolic host names, interface names or interface group names, or as any +of the following keywords: +.Pp +.Bl -tag -width xxxxxxxxxxxxxx -compact +.It Ar any +Any address. +.It Ar no-route +Any address which is not currently routable. +.It Ar urpf-failed +Any source address that fails a unicast reverse path forwarding (URPF) +check, i.e. packets coming in on an interface other than that which holds +the route back to the packet's source address. +.It Aq Ar table +Any address that matches the given table. +.El +.Pp +Ranges of addresses are specified by using the +.Sq - +operator. +For instance: +.Dq 10.1.1.10 - 10.1.1.12 +means all addresses from 10.1.1.10 to 10.1.1.12, +hence addresses 10.1.1.10, 10.1.1.11, and 10.1.1.12. +.Pp +Interface names and interface group names can have modifiers appended: +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Ar :network +Translates to the network(s) attached to the interface. +.It Ar :broadcast +Translates to the interface's broadcast address(es). +.It Ar :peer +Translates to the point-to-point interface's peer address(es). +.It Ar :0 +Do not include interface aliases. +.El +.Pp +Host names may also have the +.Ar :0 +option appended to restrict the name resolution to the first of each +v4 and v6 address found. +.Pp +Host name resolution and interface to address translation are done at +ruleset load-time. +When the address of an interface (or host name) changes (under DHCP or PPP, +for instance), the ruleset must be reloaded for the change to be reflected +in the kernel. +Surrounding the interface name (and optional modifiers) in parentheses +changes this behaviour. +When the interface name is surrounded by parentheses, the rule is +automatically updated whenever the interface changes its address. +The ruleset does not need to be reloaded. +This is especially useful with +.Ar nat . +.Pp +Ports can be specified either by number or by name. +For example, port 80 can be specified as +.Em www . +For a list of all port name to number mappings used by +.Xr pfctl 8 , +see the file +.Pa /etc/services . +.Pp +Ports and ranges of ports are specified by using these operators: +.Bd -literal -offset indent += (equal) +!= (unequal) +\*(Lt (less than) +\*(Le (less than or equal) +\*(Gt (greater than) +\*(Ge (greater than or equal) +: (range including boundaries) +\*(Gt\*(Lt (range excluding boundaries) +\*(Lt\*(Gt (except range) +.Ed +.Pp +.Sq \*(Gt\*(Lt , +.Sq \*(Lt\*(Gt +and +.Sq \&: +are binary operators (they take two arguments). +For instance: +.Bl -tag -width Fl +.It Ar port 2000:2004 +means +.Sq all ports \*(Ge 2000 and \*(Le 2004 , +hence ports 2000, 2001, 2002, 2003 and 2004. +.It Ar port 2000 \*(Gt\*(Lt 2004 +means +.Sq all ports \*(Gt 2000 and \*(Lt 2004 , +hence ports 2001, 2002 and 2003. +.It Ar port 2000 \*(Lt\*(Gt 2004 +means +.Sq all ports \*(Lt 2000 or \*(Gt 2004 , +hence ports 1-1999 and 2005-65535. +.El +.Pp +The operating system of the source host can be specified in the case of TCP +rules with the +.Ar OS +modifier. +See the +.Sx OPERATING SYSTEM FINGERPRINTING +section for more information. +.Pp +The host, port and OS specifications are optional, as in the following examples: +.Bd -literal -offset indent +pass in all +pass in from any to any +pass in proto tcp from any port \*(Le 1024 to any +pass in proto tcp from any to any port 25 +pass in proto tcp from 10.0.0.0/8 port \*(Gt 1024 \e + to ! 10.1.2.3 port != ssh +pass in proto tcp from any os "OpenBSD" +.Ed +.It Ar all +This is equivalent to "from any to any". +.It Ar group Aq Ar group +Similar to +.Ar user , +this rule only applies to packets of sockets owned by the specified group. +.It Ar user Aq Ar user +This rule only applies to packets of sockets owned by the specified user. +For outgoing connections initiated from the firewall, this is the user +that opened the connection. +For incoming connections to the firewall itself, this is the user that +listens on the destination port. +For forwarded connections, where the firewall is not a connection endpoint, +the user and group are +.Em unknown . +.Pp +All packets, both outgoing and incoming, of one connection are associated +with the same user and group. +Only TCP and UDP packets can be associated with users; for other protocols +these parameters are ignored. +.Pp +User and group refer to the effective (as opposed to the real) IDs, in +case the socket is created by a setuid/setgid process. +User and group IDs are stored when a socket is created; +when a process creates a listening socket as root (for instance, by +binding to a privileged port) and subsequently changes to another +user ID (to drop privileges), the credentials will remain root. +.Pp +User and group IDs can be specified as either numbers or names. +The syntax is similar to the one for ports. +The value +.Em unknown +matches packets of forwarded connections. +.Em unknown +can only be used with the operators +.Cm = +and +.Cm != . +Other constructs like +.Cm user \*(Ge unknown +are invalid. +Forwarded packets with unknown user and group ID match only rules +that explicitly compare against +.Em unknown +with the operators +.Cm = +or +.Cm != . +For instance +.Cm user \*(Ge 0 +does not match forwarded packets. +The following example allows only selected users to open outgoing +connections: +.Bd -literal -offset indent +block out proto { tcp, udp } all +pass out proto { tcp, udp } all user { \*(Lt 1000, dhartmei } +.Ed +.It Xo Ar flags Aq Ar a +.Pf / Ns Aq Ar b +.No \*(Ba / Ns Aq Ar b +.No \*(Ba any +.Xc +This rule only applies to TCP packets that have the flags +.Aq Ar a +set out of set +.Aq Ar b . +Flags not specified in +.Aq Ar b +are ignored. +For stateful connections, the default is +.Ar flags S/SA . +To indicate that flags should not be checked at all, specify +.Ar flags any . +The flags are: (F)IN, (S)YN, (R)ST, (P)USH, (A)CK, (U)RG, (E)CE, and C(W)R. +.Bl -tag -width Fl +.It Ar flags S/S +Flag SYN is set. +The other flags are ignored. +.It Ar flags S/SA +This is the default setting for stateful connections. +Out of SYN and ACK, exactly SYN may be set. +SYN, SYN+PSH and SYN+RST match, but SYN+ACK, ACK and ACK+RST do not. +This is more restrictive than the previous example. +.It Ar flags /SFRA +If the first set is not specified, it defaults to none. +All of SYN, FIN, RST and ACK must be unset. +.El +.Pp +Because +.Ar flags S/SA +is applied by default (unless +.Ar no state +is specified), only the initial SYN packet of a TCP handshake will create +a state for a TCP connection. +It is possible to be less restrictive, and allow state creation from +intermediate +.Pq non-SYN +packets, by specifying +.Ar flags any . +This will cause +.Xr pf 4 +to synchronize to existing connections, for instance +if one flushes the state table. +However, states created from such intermediate packets may be missing +connection details such as the TCP window scaling factor. +States which modify the packet flow, such as those affected by +.Ar nat , binat No or Ar rdr +rules, +.Ar modulate No or Ar synproxy state +options, or scrubbed with +.Ar reassemble tcp +will also not be recoverable from intermediate packets. +Such connections will stall and time out. +.It Xo Ar icmp-type Aq Ar type +.Ar code Aq Ar code +.Xc +.It Xo Ar icmp6-type Aq Ar type +.Ar code Aq Ar code +.Xc +This rule only applies to ICMP or ICMPv6 packets with the specified type +and code. +Text names for ICMP types and codes are listed in +.Xr icmp 4 +and +.Xr icmp6 4 . +This parameter is only valid for rules that cover protocols ICMP or +ICMP6. +The protocol and the ICMP type indicator +.Po +.Ar icmp-type +or +.Ar icmp6-type +.Pc +must match. +.It Xo Ar tos Aq Ar string +.No \*(Ba Aq Ar number +.Xc +This rule applies to packets with the specified +.Em TOS +bits set. +.Em TOS +may be +given as one of +.Ar lowdelay , +.Ar throughput , +.Ar reliability , +or as either hex or decimal. +.Pp +For example, the following rules are identical: +.Bd -literal -offset indent +pass all tos lowdelay +pass all tos 0x10 +pass all tos 16 +.Ed +.It Ar allow-opts +By default, IPv4 packets with IP options or IPv6 packets with routing +extension headers are blocked. +When +.Ar allow-opts +is specified for a +.Ar pass +rule, packets that pass the filter based on that rule (last matching) +do so even if they contain IP options or routing extension headers. +For packets that match state, the rule that initially created the +state is used. +The implicit +.Ar pass +rule that is used when a packet does not match any rules does not +allow IP options. +.It Ar label Aq Ar string +Adds a label (name) to the rule, which can be used to identify the rule. +For instance, +pfctl -s labels +shows per-rule statistics for rules that have labels. +.Pp +The following macros can be used in labels: +.Pp +.Bl -tag -width $srcaddr -compact -offset indent +.It Ar $if +The interface. +.It Ar $srcaddr +The source IP address. +.It Ar $dstaddr +The destination IP address. +.It Ar $srcport +The source port specification. +.It Ar $dstport +The destination port specification. +.It Ar $proto +The protocol name. +.It Ar $nr +The rule number. +.El +.Pp +For example: +.Bd -literal -offset indent +ips = \&"{ 1.2.3.4, 1.2.3.5 }\&" +pass in proto tcp from any to $ips \e + port \*(Gt 1023 label \&"$dstaddr:$dstport\&" +.Ed +.Pp +expands to +.Bd -literal -offset indent +pass in inet proto tcp from any to 1.2.3.4 \e + port \*(Gt 1023 label \&"1.2.3.4:\*(Gt1023\&" +pass in inet proto tcp from any to 1.2.3.5 \e + port \*(Gt 1023 label \&"1.2.3.5:\*(Gt1023\&" +.Ed +.Pp +The macro expansion for the +.Ar label +directive occurs only at configuration file parse time, not during runtime. +.It Xo Ar queue Aq Ar queue +.No \*(Ba ( Aq Ar queue , +.Aq Ar queue ) +.Xc +Packets matching this rule will be assigned to the specified queue. +If two queues are given, packets which have a +.Em TOS +of +.Em lowdelay +and TCP ACKs with no data payload will be assigned to the second one. +See +.Sx QUEUEING +for setup details. +.Pp +For example: +.Bd -literal -offset indent +pass in proto tcp to port 25 queue mail +pass in proto tcp to port 22 queue(ssh_bulk, ssh_prio) +.Ed +.It Ar tag Aq Ar string +Packets matching this rule will be tagged with the +specified string. +The tag acts as an internal marker that can be used to +identify these packets later on. +This can be used, for example, to provide trust between +interfaces and to determine if packets have been +processed by translation rules. +Tags are +.Qq sticky , +meaning that the packet will be tagged even if the rule +is not the last matching rule. +Further matching rules can replace the tag with a +new one but will not remove a previously applied tag. +A packet is only ever assigned one tag at a time. +Packet tagging can be done during +.Ar nat , +.Ar rdr , +or +.Ar binat +rules in addition to filter rules. +Tags take the same macros as labels (see above). +.It Ar tagged Aq Ar string +Used with filter, translation or scrub rules +to specify that packets must already +be tagged with the given tag in order to match the rule. +Inverse tag matching can also be done +by specifying the +.Cm !\& +operator before the +.Ar tagged +keyword. +.It Ar rtable Aq Ar number +Used to select an alternate routing table for the routing lookup. +Only effective before the route lookup happened, i.e. when filtering inbound. +.It Xo Ar divert-to Aq Ar host +.Ar port Aq Ar port +.Xc +Used to redirect packets to a local socket bound to +.Ar host +and +.Ar port . +The packets will not be modified, so +.Xr getsockname 2 +on the socket will return the original destination address of the packet. +.It Ar divert-reply +Used to receive replies for sockets that are bound to addresses +which are not local to the machine. +See +.Xr setsockopt 2 +for information on how to bind these sockets. +.It Ar probability Aq Ar number +A probability attribute can be attached to a rule, with a value set between +0 and 1, bounds not included. +In that case, the rule will be honoured using the given probability value +only. +For example, the following rule will drop 20% of incoming ICMP packets: +.Bd -literal -offset indent +block in proto icmp probability 20% +.Ed +.El +.Sh ROUTING +If a packet matches a rule with a route option set, the packet filter will +route the packet according to the type of route option. +When such a rule creates state, the route option is also applied to all +packets matching the same connection. +.Bl -tag -width xxxx +.It Ar fastroute +The +.Ar fastroute +option does a normal route lookup to find the next hop for the packet. +.It Ar route-to +The +.Ar route-to +option routes the packet to the specified interface with an optional address +for the next hop. +When a +.Ar route-to +rule creates state, only packets that pass in the same direction as the +filter rule specifies will be routed in this way. +Packets passing in the opposite direction (replies) are not affected +and are routed normally. +.It Ar reply-to +The +.Ar reply-to +option is similar to +.Ar route-to , +but routes packets that pass in the opposite direction (replies) to the +specified interface. +Opposite direction is only defined in the context of a state entry, and +.Ar reply-to +is useful only in rules that create state. +It can be used on systems with multiple external connections to +route all outgoing packets of a connection through the interface +the incoming connection arrived through (symmetric routing enforcement). +.It Ar dup-to +The +.Ar dup-to +option creates a duplicate of the packet and routes it like +.Ar route-to . +The original packet gets routed as it normally would. +.El +.Sh POOL OPTIONS +For +.Ar nat +and +.Ar rdr +rules, (as well as for the +.Ar route-to , +.Ar reply-to +and +.Ar dup-to +rule options) for which there is a single redirection address which has a +subnet mask smaller than 32 for IPv4 or 128 for IPv6 (more than one IP +address), a variety of different methods for assigning this address can be +used: +.Bl -tag -width xxxx +.It Ar bitmask +The +.Ar bitmask +option applies the network portion of the redirection address to the address +to be modified (source with +.Ar nat , +destination with +.Ar rdr ) . +.It Ar random +The +.Ar random +option selects an address at random within the defined block of addresses. +.It Ar source-hash +The +.Ar source-hash +option uses a hash of the source address to determine the redirection address, +ensuring that the redirection address is always the same for a given source. +An optional key can be specified after this keyword either in hex or as a +string; by default +.Xr pfctl 8 +randomly generates a key for source-hash every time the +ruleset is reloaded. +.It Ar round-robin +The +.Ar round-robin +option loops through the redirection address(es). +.Pp +When more than one redirection address is specified, +.Ar round-robin +is the only permitted pool type. +.It Ar static-port +With +.Ar nat +rules, the +.Ar static-port +option prevents +.Xr pf 4 +from modifying the source port on TCP and UDP packets. +.El +.Pp +Additionally, the +.Ar sticky-address +option can be specified to help ensure that multiple connections from the +same source are mapped to the same redirection address. +This option can be used with the +.Ar random +and +.Ar round-robin +pool options. +Note that by default these associations are destroyed as soon as there are +no longer states which refer to them; in order to make the mappings last +beyond the lifetime of the states, increase the global options with +.Ar set timeout src.track . +See +.Sx STATEFUL TRACKING OPTIONS +for more ways to control the source tracking. +.Sh STATE MODULATION +Much of the security derived from TCP is attributable to how well the +initial sequence numbers (ISNs) are chosen. +Some popular stack implementations choose +.Em very +poor ISNs and thus are normally susceptible to ISN prediction exploits. +By applying a +.Ar modulate state +rule to a TCP connection, +.Xr pf 4 +will create a high quality random sequence number for each connection +endpoint. +.Pp +The +.Ar modulate state +directive implicitly keeps state on the rule and is +only applicable to TCP connections. +.Pp +For instance: +.Bd -literal -offset indent +block all +pass out proto tcp from any to any modulate state +pass in proto tcp from any to any port 25 flags S/SFRA modulate state +.Ed +.Pp +Note that modulated connections will not recover when the state table +is lost (firewall reboot, flushing the state table, etc...). +.Xr pf 4 +will not be able to infer a connection again after the state table flushes +the connection's modulator. +When the state is lost, the connection may be left dangling until the +respective endpoints time out the connection. +It is possible on a fast local network for the endpoints to start an ACK +storm while trying to resynchronize after the loss of the modulator. +The default +.Ar flags +settings (or a more strict equivalent) should be used on +.Ar modulate state +rules to prevent ACK storms. +.Pp +Note that alternative methods are available +to prevent loss of the state table +and allow for firewall failover. +See +.Xr carp 4 +and +.Xr pfsync 4 +for further information. +.Sh SYN PROXY +By default, +.Xr pf 4 +passes packets that are part of a +.Xr tcp 4 +handshake between the endpoints. +The +.Ar synproxy state +option can be used to cause +.Xr pf 4 +itself to complete the handshake with the active endpoint, perform a handshake +with the passive endpoint, and then forward packets between the endpoints. +.Pp +No packets are sent to the passive endpoint before the active endpoint has +completed the handshake, hence so-called SYN floods with spoofed source +addresses will not reach the passive endpoint, as the sender can't complete the +handshake. +.Pp +The proxy is transparent to both endpoints, they each see a single +connection from/to the other endpoint. +.Xr pf 4 +chooses random initial sequence numbers for both handshakes. +Once the handshakes are completed, the sequence number modulators +(see previous section) are used to translate further packets of the +connection. +.Ar synproxy state +includes +.Ar modulate state . +.Pp +Rules with +.Ar synproxy +will not work if +.Xr pf 4 +operates on a +.Xr bridge 4 . +.Pp +Example: +.Bd -literal -offset indent +pass in proto tcp from any to any port www synproxy state +.Ed +.Sh STATEFUL TRACKING OPTIONS +A number of options related to stateful tracking can be applied on a +per-rule basis. +.Ar keep state , +.Ar modulate state +and +.Ar synproxy state +support these options, and +.Ar keep state +must be specified explicitly to apply options to a rule. +.Pp +.Bl -tag -width xxxx -compact +.It Ar max Aq Ar number +Limits the number of concurrent states the rule may create. +When this limit is reached, further packets that would create +state will not match this rule until existing states time out. +.It Ar no-sync +Prevent state changes for states created by this rule from appearing on the +.Xr pfsync 4 +interface. +.It Xo Aq Ar timeout +.Aq Ar seconds +.Xc +Changes the timeout values used for states created by this rule. +For a list of all valid timeout names, see +.Sx OPTIONS +above. +.It Ar sloppy +Uses a sloppy TCP connection tracker that does not check sequence +numbers at all, which makes insertion and ICMP teardown attacks way +easier. +This is intended to be used in situations where one does not see all +packets of a connection, e.g. in asymmetric routing situations. +Cannot be used with modulate or synproxy state. +.It Ar pflow +States created by this rule are exported on the +.Xr pflow 4 +interface. +.El +.Pp +Multiple options can be specified, separated by commas: +.Bd -literal -offset indent +pass in proto tcp from any to any \e + port www keep state \e + (max 100, source-track rule, max-src-nodes 75, \e + max-src-states 3, tcp.established 60, tcp.closing 5) +.Ed +.Pp +When the +.Ar source-track +keyword is specified, the number of states per source IP is tracked. +.Pp +.Bl -tag -width xxxx -compact +.It Ar source-track rule +The maximum number of states created by this rule is limited by the rule's +.Ar max-src-nodes +and +.Ar max-src-states +options. +Only state entries created by this particular rule count toward the rule's +limits. +.It Ar source-track global +The number of states created by all rules that use this option is limited. +Each rule can specify different +.Ar max-src-nodes +and +.Ar max-src-states +options, however state entries created by any participating rule count towards +each individual rule's limits. +.El +.Pp +The following limits can be set: +.Pp +.Bl -tag -width xxxx -compact +.It Ar max-src-nodes Aq Ar number +Limits the maximum number of source addresses which can simultaneously +have state table entries. +.It Ar max-src-states Aq Ar number +Limits the maximum number of simultaneous state entries that a single +source address can create with this rule. +.El +.Pp +For stateful TCP connections, limits on established connections (connections +which have completed the TCP 3-way handshake) can also be enforced +per source IP. +.Pp +.Bl -tag -width xxxx -compact +.It Ar max-src-conn Aq Ar number +Limits the maximum number of simultaneous TCP connections which have +completed the 3-way handshake that a single host can make. +.It Xo Ar max-src-conn-rate Aq Ar number +.No / Aq Ar seconds +.Xc +Limit the rate of new connections over a time interval. +The connection rate is an approximation calculated as a moving average. +.El +.Pp +Because the 3-way handshake ensures that the source address is not being +spoofed, more aggressive action can be taken based on these limits. +With the +.Ar overload Aq Ar table +state option, source IP addresses which hit either of the limits on +established connections will be added to the named table. +This table can be used in the ruleset to block further activity from +the offending host, redirect it to a tarpit process, or restrict its +bandwidth. +.Pp +The optional +.Ar flush +keyword kills all states created by the matching rule which originate +from the host which exceeds these limits. +The +.Ar global +modifier to the flush command kills all states originating from the +offending host, regardless of which rule created the state. +.Pp +For example, the following rules will protect the webserver against +hosts making more than 100 connections in 10 seconds. +Any host which connects faster than this rate will have its address added +to the +.Aq bad_hosts +table and have all states originating from it flushed. +Any new packets arriving from this host will be dropped unconditionally +by the block rule. +.Bd -literal -offset indent +block quick from \*(Ltbad_hosts\*(Gt +pass in on $ext_if proto tcp to $webserver port www keep state \e + (max-src-conn-rate 100/10, overload \*(Ltbad_hosts\*(Gt flush global) +.Ed +.Sh OPERATING SYSTEM FINGERPRINTING +Passive OS Fingerprinting is a mechanism to inspect nuances of a TCP +connection's initial SYN packet and guess at the host's operating system. +Unfortunately these nuances are easily spoofed by an attacker so the +fingerprint is not useful in making security decisions. +But the fingerprint is typically accurate enough to make policy decisions +upon. +.Pp +The fingerprints may be specified by operating system class, by +version, or by subtype/patchlevel. +The class of an operating system is typically the vendor or genre +and would be +.Ox +for the +.Xr pf 4 +firewall itself. +The version of the oldest available +.Ox +release on the main FTP site +would be 2.6 and the fingerprint would be written +.Pp +.Dl \&"OpenBSD 2.6\&" +.Pp +The subtype of an operating system is typically used to describe the +patchlevel if that patch led to changes in the TCP stack behavior. +In the case of +.Ox , +the only subtype is for a fingerprint that was +normalized by the +.Ar no-df +scrub option and would be specified as +.Pp +.Dl \&"OpenBSD 3.3 no-df\&" +.Pp +Fingerprints for most popular operating systems are provided by +.Xr pf.os 5 . +Once +.Xr pf 4 +is running, a complete list of known operating system fingerprints may +be listed by running: +.Pp +.Dl # pfctl -so +.Pp +Filter rules can enforce policy at any level of operating system specification +assuming a fingerprint is present. +Policy could limit traffic to approved operating systems or even ban traffic +from hosts that aren't at the latest service pack. +.Pp +The +.Ar unknown +class can also be used as the fingerprint which will match packets for +which no operating system fingerprint is known. +.Pp +Examples: +.Bd -literal -offset indent +pass out proto tcp from any os OpenBSD +block out proto tcp from any os Doors +block out proto tcp from any os "Doors PT" +block out proto tcp from any os "Doors PT SP3" +block out from any os "unknown" +pass on lo0 proto tcp from any os "OpenBSD 3.3 lo0" +.Ed +.Pp +Operating system fingerprinting is limited only to the TCP SYN packet. +This means that it will not work on other protocols and will not match +a currently established connection. +.Pp +Caveat: operating system fingerprints are occasionally wrong. +There are three problems: an attacker can trivially craft his packets to +appear as any operating system he chooses; +an operating system patch could change the stack behavior and no fingerprints +will match it until the database is updated; +and multiple operating systems may have the same fingerprint. +.Sh BLOCKING SPOOFED TRAFFIC +"Spoofing" is the faking of IP addresses, typically for malicious +purposes. +The +.Ar antispoof +directive expands to a set of filter rules which will block all +traffic with a source IP from the network(s) directly connected +to the specified interface(s) from entering the system through +any other interface. +.Pp +For example, the line +.Bd -literal -offset indent +antispoof for lo0 +.Ed +.Pp +expands to +.Bd -literal -offset indent +block drop in on ! lo0 inet from 127.0.0.1/8 to any +block drop in on ! lo0 inet6 from ::1 to any +.Ed +.Pp +For non-loopback interfaces, there are additional rules to block incoming +packets with a source IP address identical to the interface's IP(s). +For example, assuming the interface wi0 had an IP address of 10.0.0.1 and a +netmask of 255.255.255.0, +the line +.Bd -literal -offset indent +antispoof for wi0 inet +.Ed +.Pp +expands to +.Bd -literal -offset indent +block drop in on ! wi0 inet from 10.0.0.0/24 to any +block drop in inet from 10.0.0.1 to any +.Ed +.Pp +Caveat: Rules created by the +.Ar antispoof +directive interfere with packets sent over loopback interfaces +to local addresses. +One should pass these explicitly. +.Sh FRAGMENT HANDLING +The size of IP datagrams (packets) can be significantly larger than the +maximum transmission unit (MTU) of the network. +In cases when it is necessary or more efficient to send such large packets, +the large packet will be fragmented into many smaller packets that will each +fit onto the wire. +Unfortunately for a firewalling device, only the first logical fragment will +contain the necessary header information for the subprotocol that allows +.Xr pf 4 +to filter on things such as TCP ports or to perform NAT. +.Pp +Besides the use of +.Ar scrub +rules as described in +.Sx TRAFFIC NORMALIZATION +above, there are three options for handling fragments in the packet filter. +.Pp +One alternative is to filter individual fragments with filter rules. +If no +.Ar scrub +rule applies to a fragment, it is passed to the filter. +Filter rules with matching IP header parameters decide whether the +fragment is passed or blocked, in the same way as complete packets +are filtered. +Without reassembly, fragments can only be filtered based on IP header +fields (source/destination address, protocol), since subprotocol header +fields are not available (TCP/UDP port numbers, ICMP code/type). +The +.Ar fragment +option can be used to restrict filter rules to apply only to +fragments, but not complete packets. +Filter rules without the +.Ar fragment +option still apply to fragments, if they only specify IP header fields. +For instance, the rule +.Bd -literal -offset indent +pass in proto tcp from any to any port 80 +.Ed +.Pp +never applies to a fragment, even if the fragment is part of a TCP +packet with destination port 80, because without reassembly this information +is not available for each fragment. +This also means that fragments cannot create new or match existing +state table entries, which makes stateful filtering and address +translation (NAT, redirection) for fragments impossible. +.Pp +It's also possible to reassemble only certain fragments by specifying +source or destination addresses or protocols as parameters in +.Ar scrub +rules. +.Pp +In most cases, the benefits of reassembly outweigh the additional +memory cost, and it's recommended to use +.Ar scrub +rules to reassemble +all fragments via the +.Ar fragment reassemble +modifier. +.Pp +The memory allocated for fragment caching can be limited using +.Xr pfctl 8 . +Once this limit is reached, fragments that would have to be cached +are dropped until other entries time out. +The timeout value can also be adjusted. +.Pp +Currently, only IPv4 fragments are supported and IPv6 fragments +are blocked unconditionally. +.Sh ANCHORS +Besides the main ruleset, +.Xr pfctl 8 +can load rulesets into +.Ar anchor +attachment points. +An +.Ar anchor +is a container that can hold rules, address tables, and other anchors. +.Pp +An +.Ar anchor +has a name which specifies the path where +.Xr pfctl 8 +can be used to access the anchor to perform operations on it, such as +attaching child anchors to it or loading rules into it. +Anchors may be nested, with components separated by +.Sq / +characters, similar to how file system hierarchies are laid out. +The main ruleset is actually the default anchor, so filter and +translation rules, for example, may also be contained in any anchor. +.Pp +An anchor can reference another +.Ar anchor +attachment point +using the following kinds +of rules: +.Bl -tag -width xxxx +.It Ar nat-anchor Aq Ar name +Evaluates the +.Ar nat +rules in the specified +.Ar anchor . +.It Ar rdr-anchor Aq Ar name +Evaluates the +.Ar rdr +rules in the specified +.Ar anchor . +.It Ar binat-anchor Aq Ar name +Evaluates the +.Ar binat +rules in the specified +.Ar anchor . +.It Ar anchor Aq Ar name +Evaluates the filter rules in the specified +.Ar anchor . +.It Xo Ar load anchor +.Aq Ar name +.Ar from Aq Ar file +.Xc +Loads the rules from the specified file into the +anchor +.Ar name . +.El +.Pp +When evaluation of the main ruleset reaches an +.Ar anchor +rule, +.Xr pf 4 +will proceed to evaluate all rules specified in that anchor. +.Pp +Matching filter and translation rules marked with the +.Ar quick +option are final and abort the evaluation of the rules in other +anchors and the main ruleset. +If the +.Ar anchor +itself is marked with the +.Ar quick +option, +ruleset evaluation will terminate when the anchor is exited if the packet is +matched by any rule within the anchor. +.Pp +.Ar anchor +rules are evaluated relative to the anchor in which they are contained. +For example, all +.Ar anchor +rules specified in the main ruleset will reference anchor +attachment points underneath the main ruleset, and +.Ar anchor +rules specified in a file loaded from a +.Ar load anchor +rule will be attached under that anchor point. +.Pp +Rules may be contained in +.Ar anchor +attachment points which do not contain any rules when the main ruleset +is loaded, and later such anchors can be manipulated through +.Xr pfctl 8 +without reloading the main ruleset or other anchors. +For example, +.Bd -literal -offset indent +ext_if = \&"kue0\&" +block on $ext_if all +anchor spam +pass out on $ext_if all +pass in on $ext_if proto tcp from any \e + to $ext_if port smtp +.Ed +.Pp +blocks all packets on the external interface by default, then evaluates +all rules in the +.Ar anchor +named "spam", and finally passes all outgoing connections and +incoming connections to port 25. +.Bd -literal -offset indent +# echo \&"block in quick from 1.2.3.4 to any\&" \&| \e + pfctl -a spam -f - +.Ed +.Pp +This loads a single rule into the +.Ar anchor , +which blocks all packets from a specific address. +.Pp +The anchor can also be populated by adding a +.Ar load anchor +rule after the +.Ar anchor +rule: +.Bd -literal -offset indent +anchor spam +load anchor spam from "/etc/pf-spam.conf" +.Ed +.Pp +When +.Xr pfctl 8 +loads +.Nm pf.conf , +it will also load all the rules from the file +.Pa /etc/pf-spam.conf +into the anchor. +.Pp +Optionally, +.Ar anchor +rules can specify packet filtering parameters using the same syntax as +filter rules. +When parameters are used, the +.Ar anchor +rule is only evaluated for matching packets. +This allows conditional evaluation of anchors, like: +.Bd -literal -offset indent +block on $ext_if all +anchor spam proto tcp from any to any port smtp +pass out on $ext_if all +pass in on $ext_if proto tcp from any to $ext_if port smtp +.Ed +.Pp +The rules inside +.Ar anchor +spam are only evaluated for +.Ar tcp +packets with destination port 25. +Hence, +.Bd -literal -offset indent +# echo \&"block in quick from 1.2.3.4 to any" \&| \e + pfctl -a spam -f - +.Ed +.Pp +will only block connections from 1.2.3.4 to port 25. +.Pp +Anchors may end with the asterisk +.Pq Sq * +character, which signifies that all anchors attached at that point +should be evaluated in the alphabetical ordering of their anchor name. +For example, +.Bd -literal -offset indent +anchor "spam/*" +.Ed +.Pp +will evaluate each rule in each anchor attached to the +.Li spam +anchor. +Note that it will only evaluate anchors that are directly attached to the +.Li spam +anchor, and will not descend to evaluate anchors recursively. +.Pp +Since anchors are evaluated relative to the anchor in which they are +contained, there is a mechanism for accessing the parent and ancestor +anchors of a given anchor. +Similar to file system path name resolution, if the sequence +.Dq .. +appears as an anchor path component, the parent anchor of the current +anchor in the path evaluation at that point will become the new current +anchor. +As an example, consider the following: +.Bd -literal -offset indent +# echo ' anchor "spam/allowed" ' | pfctl -f - +# echo -e ' anchor "../banned" \en pass' | \e + pfctl -a spam/allowed -f - +.Ed +.Pp +Evaluation of the main ruleset will lead into the +.Li spam/allowed +anchor, which will evaluate the rules in the +.Li spam/banned +anchor, if any, before finally evaluating the +.Ar pass +rule. +.Pp +Filter rule +.Ar anchors +can also be loaded inline in the ruleset within a brace ('{' '}') delimited +block. +Brace delimited blocks may contain rules or other brace-delimited blocks. +When anchors are loaded this way the anchor name becomes optional. +.Bd -literal -offset indent +anchor "external" on egress { + block + anchor out { + pass proto tcp from any to port { 25, 80, 443 } + } + pass in proto tcp to any port 22 +} +.Ed +.Pp +Since the parser specification for anchor names is a string, any +reference to an anchor name containing +.Sq / +characters will require double quote +.Pq Sq \&" +characters around the anchor name. +.Sh TRANSLATION EXAMPLES +This example maps incoming requests on port 80 to port 8080, on +which a daemon is running (because, for example, it is not run as root, +and therefore lacks permission to bind to port 80). +.Bd -literal +# use a macro for the interface name, so it can be changed easily +ext_if = \&"ne3\&" + +# map daemon on 8080 to appear to be on 80 +rdr on $ext_if proto tcp from any to any port 80 -\*(Gt 127.0.0.1 port 8080 +.Ed +.Pp +If the +.Ar pass +modifier is given, packets matching the translation rule are passed without +inspecting the filter rules: +.Bd -literal +rdr pass on $ext_if proto tcp from any to any port 80 -\*(Gt 127.0.0.1 \e + port 8080 +.Ed +.Pp +In the example below, vlan12 is configured as 192.168.168.1; +the machine translates all packets coming from 192.168.168.0/24 to 204.92.77.111 +when they are going out any interface except vlan12. +This has the net effect of making traffic from the 192.168.168.0/24 +network appear as though it is the Internet routable address +204.92.77.111 to nodes behind any interface on the router except +for the nodes on vlan12. +(Thus, 192.168.168.1 can talk to the 192.168.168.0/24 nodes.) +.Bd -literal +nat on ! vlan12 from 192.168.168.0/24 to any -\*(Gt 204.92.77.111 +.Ed +.Pp +In the example below, the machine sits between a fake internal 144.19.74.* +network, and a routable external IP of 204.92.77.100. +The +.Ar no nat +rule excludes protocol AH from being translated. +.Bd -literal +# NO NAT +no nat on $ext_if proto ah from 144.19.74.0/24 to any +nat on $ext_if from 144.19.74.0/24 to any -\*(Gt 204.92.77.100 +.Ed +.Pp +In the example below, packets bound for one specific server, as well as those +generated by the sysadmins are not proxied; all other connections are. +.Bd -literal +# NO RDR +no rdr on $int_if proto { tcp, udp } from any to $server port 80 +no rdr on $int_if proto { tcp, udp } from $sysadmins to any port 80 +rdr on $int_if proto { tcp, udp } from any to any port 80 -\*(Gt 127.0.0.1 \e + port 80 +.Ed +.Pp +This longer example uses both a NAT and a redirection. +The external interface has the address 157.161.48.183. +On localhost, we are running +.Xr ftp-proxy 8 , +waiting for FTP sessions to be redirected to it. +The three mandatory anchors for +.Xr ftp-proxy 8 +are omitted from this example; see the +.Xr ftp-proxy 8 +manpage. +.Bd -literal +# NAT +# Translate outgoing packets' source addresses (any protocol). +# In this case, any address but the gateway's external address is mapped. +nat on $ext_if inet from ! ($ext_if) to any -\*(Gt ($ext_if) + +# NAT PROXYING +# Map outgoing packets' source port to an assigned proxy port instead of +# an arbitrary port. +# In this case, proxy outgoing isakmp with port 500 on the gateway. +nat on $ext_if inet proto udp from any port = isakmp to any -\*(Gt ($ext_if) \e + port 500 + +# BINAT +# Translate outgoing packets' source address (any protocol). +# Translate incoming packets' destination address to an internal machine +# (bidirectional). +binat on $ext_if from 10.1.2.150 to any -\*(Gt $ext_if + +# RDR +# Translate incoming packets' destination addresses. +# As an example, redirect a TCP and UDP port to an internal machine. +rdr on $ext_if inet proto tcp from any to ($ext_if) port 8080 \e + -\*(Gt 10.1.2.151 port 22 +rdr on $ext_if inet proto udp from any to ($ext_if) port 8080 \e + -\*(Gt 10.1.2.151 port 53 + +# RDR +# Translate outgoing ftp control connections to send them to localhost +# for proxying with ftp-proxy(8) running on port 8021. +rdr on $int_if proto tcp from any to any port 21 -\*(Gt 127.0.0.1 port 8021 +.Ed +.Pp +In this example, a NAT gateway is set up to translate internal addresses +using a pool of public addresses (192.0.2.16/28) and to redirect +incoming web server connections to a group of web servers on the internal +network. +.Bd -literal +# NAT LOAD BALANCE +# Translate outgoing packets' source addresses using an address pool. +# A given source address is always translated to the same pool address by +# using the source-hash keyword. +nat on $ext_if inet from any to any -\*(Gt 192.0.2.16/28 source-hash + +# RDR ROUND ROBIN +# Translate incoming web server connections to a group of web servers on +# the internal network. +rdr on $ext_if proto tcp from any to any port 80 \e + -\*(Gt { 10.1.2.155, 10.1.2.160, 10.1.2.161 } round-robin +.Ed +.Sh FILTER EXAMPLES +.Bd -literal +# The external interface is kue0 +# (157.161.48.183, the only routable address) +# and the private network is 10.0.0.0/8, for which we are doing NAT. + +# use a macro for the interface name, so it can be changed easily +ext_if = \&"kue0\&" + +# normalize all incoming traffic +scrub in on $ext_if all fragment reassemble + +# block and log everything by default +block return log on $ext_if all + +# block anything coming from source we have no back routes for +block in from no-route to any + +# block packets whose ingress interface does not match the one in +# the route back to their source address +block in from urpf-failed to any + +# block and log outgoing packets that do not have our address as source, +# they are either spoofed or something is misconfigured (NAT disabled, +# for instance), we want to be nice and do not send out garbage. +block out log quick on $ext_if from ! 157.161.48.183 to any + +# silently drop broadcasts (cable modem noise) +block in quick on $ext_if from any to 255.255.255.255 + +# block and log incoming packets from reserved address space and invalid +# addresses, they are either spoofed or misconfigured, we cannot reply to +# them anyway (hence, no return-rst). +block in log quick on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, \e + 192.168.0.0/16, 255.255.255.255/32 } to any + +# ICMP + +# pass out/in certain ICMP queries and keep state (ping) +# state matching is done on host addresses and ICMP id (not type/code), +# so replies (like 0/0 for 8/0) will match queries +# ICMP error messages (which always refer to a TCP/UDP packet) are +# handled by the TCP/UDP states +pass on $ext_if inet proto icmp all icmp-type 8 code 0 + +# UDP + +# pass out all UDP connections and keep state +pass out on $ext_if proto udp all + +# pass in certain UDP connections and keep state (DNS) +pass in on $ext_if proto udp from any to any port domain + +# TCP + +# pass out all TCP connections and modulate state +pass out on $ext_if proto tcp all modulate state + +# pass in certain TCP connections and keep state (SSH, SMTP, DNS, IDENT) +pass in on $ext_if proto tcp from any to any port { ssh, smtp, domain, \e + auth } + +# Do not allow Windows 9x SMTP connections since they are typically +# a viral worm. Alternately we could limit these OSes to 1 connection each. +block in on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} \e + to any port smtp + +# IPv6 +# pass in/out all IPv6 traffic: note that we have to enable this in two +# different ways, on both our physical interface and our tunnel +pass quick on gif0 inet6 +pass quick on $ext_if proto ipv6 + +# Packet Tagging + +# three interfaces: $int_if, $ext_if, and $wifi_if (wireless). NAT is +# being done on $ext_if for all outgoing packets. tag packets in on +# $int_if and pass those tagged packets out on $ext_if. all other +# outgoing packets (i.e., packets from the wireless network) are only +# permitted to access port 80. + +pass in on $int_if from any to any tag INTNET +pass in on $wifi_if from any to any + +block out on $ext_if from any to any +pass out quick on $ext_if tagged INTNET +pass out on $ext_if proto tcp from any to any port 80 + +# tag incoming packets as they are redirected to spamd(8). use the tag +# to pass those packets through the packet filter. + +rdr on $ext_if inet proto tcp from \*(Ltspammers\*(Gt to port smtp \e + tag SPAMD -\*(Gt 127.0.0.1 port spamd + +block in on $ext_if +pass in on $ext_if inet proto tcp tagged SPAMD +.Ed +.Sh GRAMMAR +Syntax for +.Nm +in BNF: +.Bd -literal +line = ( option | pf-rule | nat-rule | binat-rule | rdr-rule | + antispoof-rule | altq-rule | queue-rule | trans-anchors | + anchor-rule | anchor-close | load-anchor | table-rule | + include ) + +option = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] | + [ "ruleset-optimization" [ "none" | "basic" | "profile" ]] | + [ "optimization" [ "default" | "normal" | + "high-latency" | "satellite" | + "aggressive" | "conservative" ] ] + [ "limit" ( limit-item | "{" limit-list "}" ) ] | + [ "loginterface" ( interface-name | "none" ) ] | + [ "block-policy" ( "drop" | "return" ) ] | + [ "state-policy" ( "if-bound" | "floating" ) ] + [ "state-defaults" state-opts ] + [ "require-order" ( "yes" | "no" ) ] + [ "fingerprints" filename ] | + [ "skip on" ifspec ] | + [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ] ) + +pf-rule = action [ ( "in" | "out" ) ] + [ "log" [ "(" logopts ")"] ] [ "quick" ] + [ "on" ifspec ] [ "fastroute" | route ] [ af ] [ protospec ] + hosts [ filteropt-list ] + +logopts = logopt [ "," logopts ] +logopt = "all" | "user" | "to" interface-name + +filteropt-list = filteropt-list filteropt | filteropt +filteropt = user | group | flags | icmp-type | icmp6-type | "tos" tos | + ( "no" | "keep" | "modulate" | "synproxy" ) "state" + [ "(" state-opts ")" ] | + "fragment" | "no-df" | "min-ttl" number | "set-tos" tos | + "max-mss" number | "random-id" | "reassemble tcp" | + fragmentation | "allow-opts" | + "label" string | "tag" string | [ ! ] "tagged" string | + "queue" ( string | "(" string [ [ "," ] string ] ")" ) | + "rtable" number | "probability" number"%" + +nat-rule = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] + [ "on" ifspec ] [ af ] + [ protospec ] hosts [ "tag" string ] [ "tagged" string ] + [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) + [ portspec ] [ pooltype ] [ "static-port" ] ] + +binat-rule = [ "no" ] "binat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] + [ "on" interface-name ] [ af ] + [ "proto" ( proto-name | proto-number ) ] + "from" address [ "/" mask-bits ] "to" ipspec + [ "tag" string ] [ "tagged" string ] + [ "-\*(Gt" address [ "/" mask-bits ] ] + +rdr-rule = [ "no" ] "rdr" [ "pass" [ "log" [ "(" logopts ")" ] ] ] + [ "on" ifspec ] [ af ] + [ protospec ] hosts [ "tag" string ] [ "tagged" string ] + [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) + [ portspec ] [ pooltype ] ] + +antispoof-rule = "antispoof" [ "log" ] [ "quick" ] + "for" ifspec [ af ] [ "label" string ] + +table-rule = "table" "\*(Lt" string "\*(Gt" [ tableopts-list ] +tableopts-list = tableopts-list tableopts | tableopts +tableopts = "persist" | "const" | "counters" | "file" string | + "{" [ tableaddr-list ] "}" +tableaddr-list = tableaddr-list [ "," ] tableaddr-spec | tableaddr-spec +tableaddr-spec = [ "!" ] tableaddr [ "/" mask-bits ] +tableaddr = hostname | ifspec | "self" | + ipv4-dotted-quad | ipv6-coloned-hex + +altq-rule = "altq on" interface-name queueopts-list + "queue" subqueue +queue-rule = "queue" string [ "on" interface-name ] queueopts-list + subqueue + +anchor-rule = "anchor" [ string ] [ ( "in" | "out" ) ] [ "on" ifspec ] + [ af ] [ protospec ] [ hosts ] [ filteropt-list ] [ "{" ] + +anchor-close = "}" + +trans-anchors = ( "nat-anchor" | "rdr-anchor" | "binat-anchor" ) string + [ "on" ifspec ] [ af ] [ "proto" ] [ protospec ] [ hosts ] + +load-anchor = "load anchor" string "from" filename + +queueopts-list = queueopts-list queueopts | queueopts +queueopts = [ "bandwidth" bandwidth-spec ] | + [ "qlimit" number ] | [ "tbrsize" number ] | + [ "priority" number ] | [ schedulers ] +schedulers = ( cbq-def | priq-def | hfsc-def ) +bandwidth-spec = "number" ( "b" | "Kb" | "Mb" | "Gb" | "%" ) + +action = "pass" | "block" [ return ] | [ "no" ] "scrub" +return = "drop" | "return" | "return-rst" [ "( ttl" number ")" ] | + "return-icmp" [ "(" icmpcode [ [ "," ] icmp6code ] ")" ] | + "return-icmp6" [ "(" icmp6code ")" ] +icmpcode = ( icmp-code-name | icmp-code-number ) +icmp6code = ( icmp6-code-name | icmp6-code-number ) + +ifspec = ( [ "!" ] ( interface-name | interface-group ) ) | + "{" interface-list "}" +interface-list = [ "!" ] ( interface-name | interface-group ) + [ [ "," ] interface-list ] +route = ( "route-to" | "reply-to" | "dup-to" ) + ( routehost | "{" routehost-list "}" ) + [ pooltype ] +af = "inet" | "inet6" + +protospec = "proto" ( proto-name | proto-number | + "{" proto-list "}" ) +proto-list = ( proto-name | proto-number ) [ [ "," ] proto-list ] + +hosts = "all" | + "from" ( "any" | "no-route" | "urpf-failed" | "self" | host | + "{" host-list "}" ) [ port ] [ os ] + "to" ( "any" | "no-route" | "self" | host | + "{" host-list "}" ) [ port ] + +ipspec = "any" | host | "{" host-list "}" +host = [ "!" ] ( address [ "/" mask-bits ] | "\*(Lt" string "\*(Gt" ) +redirhost = address [ "/" mask-bits ] +routehost = "(" interface-name [ address [ "/" mask-bits ] ] ")" +address = ( interface-name | interface-group | + "(" ( interface-name | interface-group ) ")" | + hostname | ipv4-dotted-quad | ipv6-coloned-hex ) +host-list = host [ [ "," ] host-list ] +redirhost-list = redirhost [ [ "," ] redirhost-list ] +routehost-list = routehost [ [ "," ] routehost-list ] + +port = "port" ( unary-op | binary-op | "{" op-list "}" ) +portspec = "port" ( number | name ) [ ":" ( "*" | number | name ) ] +os = "os" ( os-name | "{" os-list "}" ) +user = "user" ( unary-op | binary-op | "{" op-list "}" ) +group = "group" ( unary-op | binary-op | "{" op-list "}" ) + +unary-op = [ "=" | "!=" | "\*(Lt" | "\*(Le" | "\*(Gt" | "\*(Ge" ] + ( name | number ) +binary-op = number ( "\*(Lt\*(Gt" | "\*(Gt\*(Lt" | ":" ) number +op-list = ( unary-op | binary-op ) [ [ "," ] op-list ] + +os-name = operating-system-name +os-list = os-name [ [ "," ] os-list ] + +flags = "flags" ( [ flag-set ] "/" flag-set | "any" ) +flag-set = [ "F" ] [ "S" ] [ "R" ] [ "P" ] [ "A" ] [ "U" ] [ "E" ] + [ "W" ] + +icmp-type = "icmp-type" ( icmp-type-code | "{" icmp-list "}" ) +icmp6-type = "icmp6-type" ( icmp-type-code | "{" icmp-list "}" ) +icmp-type-code = ( icmp-type-name | icmp-type-number ) + [ "code" ( icmp-code-name | icmp-code-number ) ] +icmp-list = icmp-type-code [ [ "," ] icmp-list ] + +tos = ( "lowdelay" | "throughput" | "reliability" | + [ "0x" ] number ) + +state-opts = state-opt [ [ "," ] state-opts ] +state-opt = ( "max" number | "no-sync" | timeout | "sloppy" | "pflow" | + "source-track" [ ( "rule" | "global" ) ] | + "max-src-nodes" number | "max-src-states" number | + "max-src-conn" number | + "max-src-conn-rate" number "/" number | + "overload" "\*(Lt" string "\*(Gt" [ "flush" ] | + "if-bound" | "floating" ) + +fragmentation = [ "fragment reassemble" | "fragment crop" | + "fragment drop-ovl" ] + +timeout-list = timeout [ [ "," ] timeout-list ] +timeout = ( "tcp.first" | "tcp.opening" | "tcp.established" | + "tcp.closing" | "tcp.finwait" | "tcp.closed" | + "udp.first" | "udp.single" | "udp.multiple" | + "icmp.first" | "icmp.error" | + "other.first" | "other.single" | "other.multiple" | + "frag" | "interval" | "src.track" | + "adaptive.start" | "adaptive.end" ) number + +limit-list = limit-item [ [ "," ] limit-list ] +limit-item = ( "states" | "frags" | "src-nodes" ) number + +pooltype = ( "bitmask" | "random" | + "source-hash" [ ( hex-key | string-key ) ] | + "round-robin" ) [ sticky-address ] + +subqueue = string | "{" queue-list "}" +queue-list = string [ [ "," ] string ] +cbq-def = "cbq" [ "(" cbq-opt [ [ "," ] cbq-opt ] ")" ] +priq-def = "priq" [ "(" priq-opt [ [ "," ] priq-opt ] ")" ] +hfsc-def = "hfsc" [ "(" hfsc-opt [ [ "," ] hfsc-opt ] ")" ] +cbq-opt = ( "default" | "borrow" | "red" | "ecn" | "rio" ) +priq-opt = ( "default" | "red" | "ecn" | "rio" ) +hfsc-opt = ( "default" | "red" | "ecn" | "rio" | + linkshare-sc | realtime-sc | upperlimit-sc ) +linkshare-sc = "linkshare" sc-spec +realtime-sc = "realtime" sc-spec +upperlimit-sc = "upperlimit" sc-spec +sc-spec = ( bandwidth-spec | + "(" bandwidth-spec number bandwidth-spec ")" ) +include = "include" filename +.Ed +.Sh FILES +.Bl -tag -width "/etc/protocols" -compact +.It Pa /etc/hosts +Host name database. +.It Pa /etc/pf.conf +Default location of the ruleset file. +.It Pa /etc/pf.os +Default location of OS fingerprints. +.It Pa /etc/protocols +Protocol name database. +.It Pa /etc/services +Service name database. +.El +.Sh SEE ALSO +.Xr altq 4 , +.Xr carp 4 , +.Xr icmp 4 , +.Xr icmp6 4 , +.Xr ip 4 , +.Xr ip6 4 , +.Xr pf 4 , +.Xr pflow 4 , +.Xr pfsync 4 , +.Xr tcp 4 , +.Xr udp 4 , +.Xr hosts 5 , +.Xr pf.os 5 , +.Xr protocols 5 , +.Xr services 5 , +.Xr ftp-proxy 8 , +.Xr pfctl 8 , +.Xr pflogd 8 , +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 3.0 . diff --git a/share/man/man5/pf.os.5 b/share/man/man5/pf.os.5 new file mode 100644 index 0000000000000..593052572951b --- /dev/null +++ b/share/man/man5/pf.os.5 @@ -0,0 +1,223 @@ +.\" $OpenBSD: pf.os.5,v 1.8 2007/05/31 19:19:58 jmc Exp $ +.\" +.\" Copyright (c) 2003 Mike Frantzen +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd May 31 2007 +.Dt PF.OS 5 +.Os +.Sh NAME +.Nm pf.os +.Nd format of the operating system fingerprints file +.Sh DESCRIPTION +The +.Xr pf 4 +firewall and the +.Xr tcpdump 1 +program can both fingerprint the operating system of hosts that +originate an IPv4 TCP connection. +The file consists of newline-separated records, one per fingerprint, +containing nine colon +.Pq Ql \&: +separated fields. +These fields are as follows: +.Pp +.Bl -tag -width Description -offset indent -compact +.It window +The TCP window size. +.It TTL +The IP time to live. +.It df +The presence of the IPv4 don't fragment bit. +.It packet size +The size of the initial TCP packet. +.It TCP options +An ordered list of the TCP options. +.It class +The class of operating system. +.It version +The version of the operating system. +.It subtype +The subtype of patchlevel of the operating system. +.It description +The overall textual description of the operating system, version and subtype. +.El +.Pp +The +.Ar window +field corresponds to the th->th_win field in the TCP header and is the +source host's advertised TCP window size. +It may be between zero and 65,535 inclusive. +The window size may be given as a multiple of a constant by prepending +the size with a percent sign +.Sq % +and the value will be used as a modulus. +Three special values may be used for the window size: +.Pp +.Bl -tag -width xxx -offset indent -compact +.It * +An asterisk will wildcard the value so any window size will match. +.It S +Allow any window size which is a multiple of the maximum segment size (MSS). +.It T +Allow any window size which is a multiple of the maximum transmission unit +(MTU). +.El +.Pp +The +.Ar ttl +value is the initial time to live in the IP header. +The fingerprint code will account for the volatility of the packet's TTL +as it traverses a network. +.Pp +The +.Ar df +bit corresponds to the Don't Fragment bit in an IPv4 header. +It tells intermediate routers not to fragment the packet and is used for +path MTU discovery. +It may be either a zero or a one. +.Pp +The +.Ar packet size +is the literal size of the full IP packet and is a function of all of +the IP and TCP options. +.Pp +The +.Ar TCP options +field is an ordered list of the individual TCP options that appear in the +SYN packet. +Each option is described by a single character separated by a comma and +certain ones may include a value. +The options are: +.Pp +.Bl -tag -width Description -offset indent -compact +.It Mnnn +maximum segment size (MSS) option. +The value is the maximum packet size of the network link which may +include the +.Sq % +modulus or match all MSSes with the +.Sq * +value. +.It N +the NOP option (NO Operation). +.It T[0] +the timestamp option. +Certain operating systems always start with a zero timestamp in which +case a zero value is added to the option; otherwise no value is appended. +.It S +the Selective ACKnowledgement OK (SACKOK) option. +.It Wnnn +window scaling option. +The value is the size of the window scaling which may include the +.Sq % +modulus or match all window scalings with the +.Sq * +value. +.El +.Pp +No TCP options in the fingerprint may be given with a single dot +.Sq \&. . +.Pp +An example of OpenBSD's TCP options are: +.Pp +.Dl M*,N,N,S,N,W0,N,N,T +.Pp +The first option +.Ar M* +is the MSS option and will match all values. +The second and third options +.Ar N +will match two NOPs. +The fourth option +.Ar S +will match the SACKOK option. +The fifth +.Ar N +will match another NOP. +The sixth +.Ar W0 +will match a window scaling option with a zero scaling size. +The seventh and eighth +.Ar N +options will match two NOPs. +And the ninth and final option +.Ar T +will match the timestamp option with any time value. +.Pp +The TCP options in a fingerprint will only match packets with the +exact same TCP options in the same order. +.Pp +The +.Ar class +field is the class, genre or vendor of the operating system. +.Pp +The +.Ar version +is the version of the operating system. +It is used to distinguish between different fingerprints of operating +systems of the same class but different versions. +.Pp +The +.Ar subtype +is the subtype or patch level of the operating system version. +It is used to distinguish between different fingerprints of operating +systems of the same class and same version but slightly different +patches or tweaking. +.Pp +The +.Ar description +is a general description of the operating system, its version, +patchlevel and any further useful details. +.Sh EXAMPLES +The fingerprint of a plain +.Ox 3.3 +host is: +.Bd -literal + 16384:64:1:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3::OpenBSD 3.3 +.Ed +.Pp +The fingerprint of an +.Ox 3.3 +host behind a PF scrubbing firewall with a no-df rule would be: +.Bd -literal + 16384:64:0:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3:!df:OpenBSD 3.3 scrub no-df +.Ed +.Pp +An absolutely braindead embedded operating system fingerprint could be: +.Bd -literal + 65535:255:0:40:.:DUMMY:1.1:p3:Dummy embedded OS v1.1p3 +.Ed +.Pp +The +.Xr tcpdump 1 +output of +.Bd -literal + # tcpdump -s128 -c1 -nv 'tcp[13] == 2' + 03:13:48.118526 10.0.0.1.3377 > 10.0.0.2.80: S [tcp sum ok] \e + 534596083:534596083(0) win 57344 (DF) [tos 0x10] \e + (ttl 64, id 11315, len 44) +.Ed +.Pp +almost translates into the following fingerprint +.Bd -literal + 57344:64:1:44:M1460: exampleOS:1.0::exampleOS 1.0 +.Ed +.Sh SEE ALSO +.Xr pf 4 , +.Xr pf.conf 5 , +.Xr pfctl 8 , +.Xr tcpdump 1 diff --git a/sys/conf/files b/sys/conf/files index 569849be41089..e9aeef3c6e717 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -270,19 +270,19 @@ cddl/contrib/opensolaris/uts/common/zmod/zmod.c optional zfs compile-with "${ cddl/contrib/opensolaris/uts/common/zmod/zmod_subr.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/uts/common/zmod/zutil.c optional zfs compile-with "${ZFS_C}" contrib/altq/altq/altq_cbq.c optional altq \ - compile-with "${NORMAL_C} -I$S/contrib/pf" + compile-with "${NORMAL_C}" contrib/altq/altq/altq_cdnr.c optional altq contrib/altq/altq/altq_hfsc.c optional altq \ - compile-with "${NORMAL_C} -I$S/contrib/pf" + compile-with "${NORMAL_C}" contrib/altq/altq/altq_priq.c optional altq \ - compile-with "${NORMAL_C} -I$S/contrib/pf" + compile-with "${NORMAL_C}" contrib/altq/altq/altq_red.c optional altq \ - compile-with "${NORMAL_C} -I$S/contrib/pf" + compile-with "${NORMAL_C}" contrib/altq/altq/altq_rio.c optional altq \ - compile-with "${NORMAL_C} -I$S/contrib/pf" + compile-with "${NORMAL_C}" contrib/altq/altq/altq_rmclass.c optional altq contrib/altq/altq/altq_subr.c optional altq \ - compile-with "${NORMAL_C} -I$S/contrib/pf" + compile-with "${NORMAL_C}" contrib/dev/acpica/components/debugger/dbcmds.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbdisply.c optional acpi acpi_debug contrib/dev/acpica/components/debugger/dbexec.c optional acpi acpi_debug @@ -513,27 +513,6 @@ contrib/ngatm/netnatm/sig/sig_unimsgcpy.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_verify.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" -contrib/pf/net/if_pflog.c optional pflog pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/if_pfsync.c optional pfsync pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_if.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_ioctl.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_lb.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_norm.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_osfp.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_ruleset.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/net/pf_table.c optional pf inet \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -contrib/pf/netinet/in4_cksum.c optional pf inet crypto/blowfish/bf_ecb.c optional ipsec crypto/blowfish/bf_skey.c optional crypto | ipsec crypto/camellia/camellia.c optional crypto | ipsec @@ -2881,8 +2860,7 @@ net/if_edsc.c optional edsc net/if_ef.c optional ef net/if_enc.c optional enc ipsec inet | enc ipsec inet6 net/if_epair.c optional epair -net/if_ethersubr.c optional ether \ - compile-with "${NORMAL_C} -I$S/contrib/pf" +net/if_ethersubr.c optional ether net/if_faith.c optional faith net/if_fddisubr.c optional fddi net/if_fwsubr.c optional fwip @@ -3061,30 +3039,12 @@ netinet/ip_id.c optional inet netinet/in_mcast.c optional inet netinet/in_pcb.c optional inet | inet6 netinet/in_pcbgroup.c optional inet pcbgroup | inet6 pcbgroup -netinet/in_proto.c optional inet | inet6 \ - compile-with "${NORMAL_C} -I$S/contrib/pf" +netinet/in_proto.c optional inet | inet6 netinet/in_rmx.c optional inet netinet/ip_divert.c optional inet ipdivert ipfirewall -netinet/ipfw/dn_heap.c optional inet dummynet -netinet/ipfw/dn_sched_fifo.c optional inet dummynet -netinet/ipfw/dn_sched_prio.c optional inet dummynet -netinet/ipfw/dn_sched_qfq.c optional inet dummynet -netinet/ipfw/dn_sched_rr.c optional inet dummynet -netinet/ipfw/dn_sched_wf2q.c optional inet dummynet -netinet/ipfw/ip_dummynet.c optional inet dummynet -netinet/ipfw/ip_dn_io.c optional inet dummynet -netinet/ipfw/ip_dn_glue.c optional inet dummynet netinet/ip_ecn.c optional inet | inet6 netinet/ip_encap.c optional inet | inet6 netinet/ip_fastfwd.c optional inet -netinet/ipfw/ip_fw2.c optional inet ipfirewall \ - compile-with "${NORMAL_C} -I$S/contrib/pf" -netinet/ipfw/ip_fw_dynamic.c optional inet ipfirewall -netinet/ipfw/ip_fw_log.c optional inet ipfirewall -netinet/ipfw/ip_fw_pfil.c optional inet ipfirewall -netinet/ipfw/ip_fw_sockopt.c optional inet ipfirewall -netinet/ipfw/ip_fw_table.c optional inet ipfirewall -netinet/ipfw/ip_fw_nat.c optional inet ipfirewall_nat netinet/ip_icmp.c optional inet | inet6 netinet/ip_input.c optional inet netinet/ip_ipsec.c optional inet ipsec @@ -3192,6 +3152,33 @@ netncp/ncp_nls.c optional ncp netncp/ncp_rq.c optional ncp netncp/ncp_sock.c optional ncp netncp/ncp_subr.c optional ncp +netpfil/ipfw/dn_heap.c optional inet dummynet +netpfil/ipfw/dn_sched_fifo.c optional inet dummynet +netpfil/ipfw/dn_sched_prio.c optional inet dummynet +netpfil/ipfw/dn_sched_qfq.c optional inet dummynet +netpfil/ipfw/dn_sched_rr.c optional inet dummynet +netpfil/ipfw/dn_sched_wf2q.c optional inet dummynet +netpfil/ipfw/ip_dummynet.c optional inet dummynet +netpfil/ipfw/ip_dn_io.c optional inet dummynet +netpfil/ipfw/ip_dn_glue.c optional inet dummynet +netpfil/ipfw/ip_fw2.c optional inet ipfirewall +netpfil/ipfw/ip_fw_dynamic.c optional inet ipfirewall +netpfil/ipfw/ip_fw_log.c optional inet ipfirewall +netpfil/ipfw/ip_fw_pfil.c optional inet ipfirewall +netpfil/ipfw/ip_fw_sockopt.c optional inet ipfirewall +netpfil/ipfw/ip_fw_table.c optional inet ipfirewall +netpfil/ipfw/ip_fw_nat.c optional inet ipfirewall_nat +netpfil/pf/if_pflog.c optional pflog pf inet +netpfil/pf/if_pfsync.c optional pfsync pf inet +netpfil/pf/pf.c optional pf inet +netpfil/pf/pf_if.c optional pf inet +netpfil/pf/pf_ioctl.c optional pf inet +netpfil/pf/pf_lb.c optional pf inet +netpfil/pf/pf_norm.c optional pf inet +netpfil/pf/pf_osfp.c optional pf inet +netpfil/pf/pf_ruleset.c optional pf inet +netpfil/pf/pf_table.c optional pf inet +netpfil/pf/in4_cksum.c optional pf inet netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk index fbf686af95e5b..64980fe66e47a 100644 --- a/sys/conf/kern.pre.mk +++ b/sys/conf/kern.pre.mk @@ -64,9 +64,6 @@ INCLUDES+= -I$S/contrib/altq # ... and the same for ipfilter INCLUDES+= -I$S/contrib/ipfilter -# ... and the same for pf -INCLUDES+= -I$S/contrib/pf - # ... and the same for ath INCLUDES+= -I$S/dev/ath -I$S/dev/ath/ath_hal diff --git a/sys/contrib/pf/net/if_pflog.c b/sys/contrib/pf/net/if_pflog.c deleted file mode 100644 index 20feea2330e14..0000000000000 --- a/sys/contrib/pf/net/if_pflog.c +++ /dev/null @@ -1,290 +0,0 @@ -/* $OpenBSD: if_pflog.c,v 1.26 2007/10/18 21:58:18 mpf Exp $ */ -/* - * The authors of this code are John Ioannidis (ji@tla.org), - * Angelos D. Keromytis (kermit@csd.uch.gr) and - * Niels Provos (provos@physnet.uni-hamburg.de). - * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. - * - * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, - * by Angelos D. Keromytis. - * - * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis - * and Niels Provos. - * - * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis - * and Niels Provos. - * Copyright (c) 2001, Angelos D. Keromytis, Niels Provos. - * - * Permission to use, copy, and modify this software with or without fee - * is hereby granted, provided that this entire notice is included in - * all copies of any software which is or includes a copy or - * modification of this software. - * You may use this code under the GNU public license if you so wish. Please - * contribute changes back to the authors under this freer than GPL license - * so that we may further the use of strong encryption without limitations to - * all. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE - * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR - * PURPOSE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_bpf.h" -#include "opt_pf.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if defined(INET) || defined(INET6) -#include -#endif -#ifdef INET -#include -#include -#endif - -#ifdef INET6 -#include -#include -#endif /* INET6 */ - -#ifdef INET -#include -#endif /* INET */ - -#define PFLOGMTU (32768 + MHLEN + MLEN) - -#ifdef PFLOGDEBUG -#define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0) -#else -#define DPRINTF(x) -#endif - -static int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *, - struct route *); -static void pflogattach(int); -static int pflogioctl(struct ifnet *, u_long, caddr_t); -static void pflogstart(struct ifnet *); -static int pflog_clone_create(struct if_clone *, int, caddr_t); -static void pflog_clone_destroy(struct ifnet *); - -IFC_SIMPLE_DECLARE(pflog, 1); - -struct ifnet *pflogifs[PFLOGIFS_MAX]; /* for fast access */ - -static void -pflogattach(int npflog) -{ - int i; - for (i = 0; i < PFLOGIFS_MAX; i++) - pflogifs[i] = NULL; - if_clone_attach(&pflog_cloner); -} - -static int -pflog_clone_create(struct if_clone *ifc, int unit, caddr_t param) -{ - struct ifnet *ifp; - - if (unit >= PFLOGIFS_MAX) - return (EINVAL); - - ifp = if_alloc(IFT_PFLOG); - if (ifp == NULL) { - return (ENOSPC); - } - if_initname(ifp, ifc->ifc_name, unit); - ifp->if_mtu = PFLOGMTU; - ifp->if_ioctl = pflogioctl; - ifp->if_output = pflogoutput; - ifp->if_start = pflogstart; - ifp->if_snd.ifq_maxlen = ifqmaxlen; - ifp->if_hdrlen = PFLOG_HDRLEN; - if_attach(ifp); - - bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN); - - pflogifs[unit] = ifp; - - return (0); -} - -static void -pflog_clone_destroy(struct ifnet *ifp) -{ - int i; - - for (i = 0; i < PFLOGIFS_MAX; i++) - if (pflogifs[i] == ifp) - pflogifs[i] = NULL; - - bpfdetach(ifp); - if_detach(ifp); - if_free(ifp); -} - -/* - * Start output on the pflog interface. - */ -static void -pflogstart(struct ifnet *ifp) -{ - struct mbuf *m; - - for (;;) { - IF_LOCK(&ifp->if_snd); - _IF_DROP(&ifp->if_snd); - _IF_DEQUEUE(&ifp->if_snd, m); - IF_UNLOCK(&ifp->if_snd); - - if (m == NULL) - return; - else - m_freem(m); - } -} - -static int -pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, - struct route *rt) -{ - m_freem(m); - return (0); -} - -/* ARGSUSED */ -static int -pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - switch (cmd) { - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) - ifp->if_drv_flags |= IFF_DRV_RUNNING; - else - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - break; - default: - return (ENOTTY); - } - - return (0); -} - -static int -pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir, - u_int8_t reason, struct pf_rule *rm, struct pf_rule *am, - struct pf_ruleset *ruleset, struct pf_pdesc *pd, int lookupsafe) -{ - struct ifnet *ifn; - struct pfloghdr hdr; - - if (kif == NULL || m == NULL || rm == NULL || pd == NULL) - return ( 1); - - if ((ifn = pflogifs[rm->logif]) == NULL || !ifn->if_bpf) - return (0); - - bzero(&hdr, sizeof(hdr)); - hdr.length = PFLOG_REAL_HDRLEN; - hdr.af = af; - hdr.action = rm->action; - hdr.reason = reason; - memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname)); - - if (am == NULL) { - hdr.rulenr = htonl(rm->nr); - hdr.subrulenr = 1; - } else { - hdr.rulenr = htonl(am->nr); - hdr.subrulenr = htonl(rm->nr); - if (ruleset != NULL && ruleset->anchor != NULL) - strlcpy(hdr.ruleset, ruleset->anchor->name, - sizeof(hdr.ruleset)); - } - /* - * XXXGL: we avoid pf_socket_lookup() when we are holding - * state lock, since this leads to unsafe LOR. - * These conditions are very very rare, however. - */ - if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done && lookupsafe) - pd->lookup.done = pf_socket_lookup(dir, pd, m); - if (pd->lookup.done > 0) - hdr.uid = pd->lookup.uid; - else - hdr.uid = UID_MAX; - hdr.pid = NO_PID; - hdr.rule_uid = rm->cuid; - hdr.rule_pid = rm->cpid; - hdr.dir = dir; - -#ifdef INET - if (af == AF_INET && dir == PF_OUT) { - struct ip *ip; - - ip = mtod(m, struct ip *); - ip->ip_sum = 0; - ip->ip_sum = in_cksum(m, ip->ip_hl << 2); - } -#endif /* INET */ - - ifn->if_opackets++; - ifn->if_obytes += m->m_pkthdr.len; - BPF_MTAP2(ifn, &hdr, PFLOG_HDRLEN, m); - - return (0); -} - -static int -pflog_modevent(module_t mod, int type, void *data) -{ - int error = 0; - - switch (type) { - case MOD_LOAD: - pflogattach(1); - PF_RULES_WLOCK(); - pflog_packet_ptr = pflog_packet; - PF_RULES_WUNLOCK(); - break; - case MOD_UNLOAD: - PF_RULES_WLOCK(); - pflog_packet_ptr = NULL; - PF_RULES_WUNLOCK(); - if_clone_detach(&pflog_cloner); - break; - default: - error = EINVAL; - break; - } - - return error; -} - -static moduledata_t pflog_mod = { "pflog", pflog_modevent, 0 }; - -#define PFLOG_MODVER 1 - -DECLARE_MODULE(pflog, pflog_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); -MODULE_VERSION(pflog, PFLOG_MODVER); -MODULE_DEPEND(pflog, pf, PF_MODVER, PF_MODVER, PF_MODVER); diff --git a/sys/contrib/pf/net/if_pflog.h b/sys/contrib/pf/net/if_pflog.h deleted file mode 100644 index 0faeb7d46322c..0000000000000 --- a/sys/contrib/pf/net/if_pflog.h +++ /dev/null @@ -1,66 +0,0 @@ -/* $OpenBSD: if_pflog.h,v 1.13 2006/10/23 12:46:09 henning Exp $ */ -/* - * Copyright 2001 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _NET_IF_PFLOG_H_ -#define _NET_IF_PFLOG_H_ - -#define PFLOGIFS_MAX 16 - -#define PFLOG_RULESET_NAME_SIZE 16 - -struct pfloghdr { - u_int8_t length; - sa_family_t af; - u_int8_t action; - u_int8_t reason; - char ifname[IFNAMSIZ]; - char ruleset[PFLOG_RULESET_NAME_SIZE]; - u_int32_t rulenr; - u_int32_t subrulenr; - uid_t uid; - pid_t pid; - uid_t rule_uid; - pid_t rule_pid; - u_int8_t dir; - u_int8_t pad[3]; -}; - -#define PFLOG_HDRLEN sizeof(struct pfloghdr) -/* minus pad, also used as a signature */ -#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad) - -#ifdef _KERNEL -struct pf_rule; -struct pf_ruleset; -struct pfi_kif; -struct pf_pdesc; - -#define PFLOG_PACKET(i,a,b,c,d,e,f,g,h,di) do { \ - if (pflog_packet_ptr != NULL) \ - pflog_packet_ptr(i,a,b,c,d,e,f,g,h,di); \ -} while (0) -#endif /* _KERNEL */ -#endif /* _NET_IF_PFLOG_H_ */ diff --git a/sys/contrib/pf/net/if_pfsync.c b/sys/contrib/pf/net/if_pfsync.c deleted file mode 100644 index 28af641ad6fab..0000000000000 --- a/sys/contrib/pf/net/if_pfsync.c +++ /dev/null @@ -1,2397 +0,0 @@ -/* $OpenBSD: if_pfsync.c,v 1.110 2009/02/24 05:39:19 dlg Exp $ */ - -/* - * Copyright (c) 2002 Michael Shalayeff - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES 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 MIND, 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) 2009 David Gwynne - * - * 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. - */ - -/* - * Revisions picked from OpenBSD after revision 1.110 import: - * 1.118, 1.124, 1.148, 1.149, 1.151, 1.171 - fixes to bulk updates - * 1.120, 1.175 - use monotonic time_uptime - * 1.122 - reduce number of updates for non-TCP sessions - * 1.128 - cleanups - * 1.146 - bzero() mbuf before sparsely filling it with data - * 1.170 - SIOCSIFMTU checks - * 1.126, 1.142 - deferred packets processing - * 1.173 - correct expire time processing - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_pf.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PFSYNC_MINPKT ( \ - sizeof(struct ip) + \ - sizeof(struct pfsync_header) + \ - sizeof(struct pfsync_subheader) + \ - sizeof(struct pfsync_eof)) - -struct pfsync_pkt { - struct ip *ip; - struct in_addr src; - u_int8_t flags; -}; - -static int pfsync_upd_tcp(struct pf_state *, struct pfsync_state_peer *, - struct pfsync_state_peer *); -static int pfsync_in_clr(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_ins(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_iack(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_upd(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_upd_c(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_ureq(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_del(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_del_c(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_bus(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_tdb(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_eof(struct pfsync_pkt *, struct mbuf *, int, int); -static int pfsync_in_error(struct pfsync_pkt *, struct mbuf *, int, int); - -static int (*pfsync_acts[])(struct pfsync_pkt *, struct mbuf *, int, int) = { - pfsync_in_clr, /* PFSYNC_ACT_CLR */ - pfsync_in_ins, /* PFSYNC_ACT_INS */ - pfsync_in_iack, /* PFSYNC_ACT_INS_ACK */ - pfsync_in_upd, /* PFSYNC_ACT_UPD */ - pfsync_in_upd_c, /* PFSYNC_ACT_UPD_C */ - pfsync_in_ureq, /* PFSYNC_ACT_UPD_REQ */ - pfsync_in_del, /* PFSYNC_ACT_DEL */ - pfsync_in_del_c, /* PFSYNC_ACT_DEL_C */ - pfsync_in_error, /* PFSYNC_ACT_INS_F */ - pfsync_in_error, /* PFSYNC_ACT_DEL_F */ - pfsync_in_bus, /* PFSYNC_ACT_BUS */ - pfsync_in_tdb, /* PFSYNC_ACT_TDB */ - pfsync_in_eof /* PFSYNC_ACT_EOF */ -}; - -struct pfsync_q { - int (*write)(struct pf_state *, struct mbuf *, int); - size_t len; - u_int8_t action; -}; - -/* we have one of these for every PFSYNC_S_ */ -static int pfsync_out_state(struct pf_state *, struct mbuf *, int); -static int pfsync_out_iack(struct pf_state *, struct mbuf *, int); -static int pfsync_out_upd_c(struct pf_state *, struct mbuf *, int); -static int pfsync_out_del(struct pf_state *, struct mbuf *, int); - -static struct pfsync_q pfsync_qs[] = { - { pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_INS }, - { pfsync_out_iack, sizeof(struct pfsync_ins_ack), PFSYNC_ACT_INS_ACK }, - { pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_UPD }, - { pfsync_out_upd_c, sizeof(struct pfsync_upd_c), PFSYNC_ACT_UPD_C }, - { pfsync_out_del, sizeof(struct pfsync_del_c), PFSYNC_ACT_DEL_C } -}; - -static void pfsync_q_ins(struct pf_state *, int); -static void pfsync_q_del(struct pf_state *); - -static void pfsync_update_state(struct pf_state *); - -struct pfsync_upd_req_item { - TAILQ_ENTRY(pfsync_upd_req_item) ur_entry; - struct pfsync_upd_req ur_msg; -}; - -struct pfsync_deferral { - struct pfsync_softc *pd_sc; - TAILQ_ENTRY(pfsync_deferral) pd_entry; - u_int pd_refs; - struct callout pd_tmo; - - struct pf_state *pd_st; - struct mbuf *pd_m; -}; - -struct pfsync_softc { - /* Configuration */ - struct ifnet *sc_ifp; - struct ifnet *sc_sync_if; - struct ip_moptions sc_imo; - struct in_addr sc_sync_peer; - uint32_t sc_flags; -#define PFSYNCF_OK 0x00000001 -#define PFSYNCF_DEFER 0x00000002 -#define PFSYNCF_PUSH 0x00000004 - uint8_t sc_maxupdates; - struct ip sc_template; - struct callout sc_tmo; - struct mtx sc_mtx; - - /* Queued data */ - size_t sc_len; - TAILQ_HEAD(, pf_state) sc_qs[PFSYNC_S_COUNT]; - TAILQ_HEAD(, pfsync_upd_req_item) sc_upd_req_list; - TAILQ_HEAD(, pfsync_deferral) sc_deferrals; - u_int sc_deferred; - void *sc_plus; - size_t sc_pluslen; - - /* Bulk update info */ - struct mtx sc_bulk_mtx; - uint32_t sc_ureq_sent; - int sc_bulk_tries; - uint32_t sc_ureq_received; - int sc_bulk_hashid; - uint64_t sc_bulk_stateid; - uint32_t sc_bulk_creatorid; - struct callout sc_bulk_tmo; - struct callout sc_bulkfail_tmo; -}; - -#define PFSYNC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) -#define PFSYNC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) -#define PFSYNC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) - -#define PFSYNC_BLOCK(sc) mtx_lock(&(sc)->sc_bulk_mtx) -#define PFSYNC_BUNLOCK(sc) mtx_unlock(&(sc)->sc_bulk_mtx) -#define PFSYNC_BLOCK_ASSERT(sc) mtx_assert(&(sc)->sc_bulk_mtx, MA_OWNED) - -static MALLOC_DEFINE(M_PFSYNC, "pfsync", "pfsync(4) data"); -static VNET_DEFINE(struct pfsync_softc *, pfsyncif) = NULL; -#define V_pfsyncif VNET(pfsyncif) -static VNET_DEFINE(void *, pfsync_swi_cookie) = NULL; -#define V_pfsync_swi_cookie VNET(pfsync_swi_cookie) -static VNET_DEFINE(struct pfsyncstats, pfsyncstats); -#define V_pfsyncstats VNET(pfsyncstats) -static VNET_DEFINE(int, pfsync_carp_adj) = CARP_MAXSKEW; -#define V_pfsync_carp_adj VNET(pfsync_carp_adj) - -static void pfsync_timeout(void *); -static void pfsync_push(struct pfsync_softc *); -static void pfsyncintr(void *); -static int pfsync_multicast_setup(struct pfsync_softc *, struct ifnet *, - void *); -static void pfsync_multicast_cleanup(struct pfsync_softc *); -static int pfsync_init(void); -static void pfsync_uninit(void); - -SYSCTL_NODE(_net, OID_AUTO, pfsync, CTLFLAG_RW, 0, "PFSYNC"); -SYSCTL_VNET_STRUCT(_net_pfsync, OID_AUTO, stats, CTLFLAG_RW, - &VNET_NAME(pfsyncstats), pfsyncstats, - "PFSYNC statistics (struct pfsyncstats, net/if_pfsync.h)"); -SYSCTL_INT(_net_pfsync, OID_AUTO, carp_demotion_factor, CTLFLAG_RW, - &VNET_NAME(pfsync_carp_adj), 0, "pfsync's CARP demotion factor adjustment"); - -static int pfsync_clone_create(struct if_clone *, int, caddr_t); -static void pfsync_clone_destroy(struct ifnet *); -static int pfsync_alloc_scrub_memory(struct pfsync_state_peer *, - struct pf_state_peer *); -static int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, - struct route *); -static int pfsyncioctl(struct ifnet *, u_long, caddr_t); - -static int pfsync_defer(struct pf_state *, struct mbuf *); -static void pfsync_undefer(struct pfsync_deferral *, int); -static void pfsync_undefer_state(struct pf_state *, int); -static void pfsync_defer_tmo(void *); - -static void pfsync_request_update(u_int32_t, u_int64_t); -static void pfsync_update_state_req(struct pf_state *); - -static void pfsync_drop(struct pfsync_softc *); -static void pfsync_sendout(int); -static void pfsync_send_plus(void *, size_t); - -static void pfsync_bulk_start(void); -static void pfsync_bulk_status(u_int8_t); -static void pfsync_bulk_update(void *); -static void pfsync_bulk_fail(void *); - -#ifdef IPSEC -static void pfsync_update_net_tdb(struct pfsync_tdb *); -#endif - -#define PFSYNC_MAX_BULKTRIES 12 - -VNET_DEFINE(struct ifc_simple_data, pfsync_cloner_data); -VNET_DEFINE(struct if_clone, pfsync_cloner); -#define V_pfsync_cloner_data VNET(pfsync_cloner_data) -#define V_pfsync_cloner VNET(pfsync_cloner) -IFC_SIMPLE_DECLARE(pfsync, 1); - -static int -pfsync_clone_create(struct if_clone *ifc, int unit, caddr_t param) -{ - struct pfsync_softc *sc; - struct ifnet *ifp; - int q; - - if (unit != 0) - return (EINVAL); - - sc = malloc(sizeof(struct pfsync_softc), M_PFSYNC, M_WAITOK | M_ZERO); - sc->sc_flags |= PFSYNCF_OK; - - for (q = 0; q < PFSYNC_S_COUNT; q++) - TAILQ_INIT(&sc->sc_qs[q]); - - TAILQ_INIT(&sc->sc_upd_req_list); - TAILQ_INIT(&sc->sc_deferrals); - - sc->sc_len = PFSYNC_MINPKT; - sc->sc_maxupdates = 128; - - ifp = sc->sc_ifp = if_alloc(IFT_PFSYNC); - if (ifp == NULL) { - free(sc, M_PFSYNC); - return (ENOSPC); - } - if_initname(ifp, ifc->ifc_name, unit); - ifp->if_softc = sc; - ifp->if_ioctl = pfsyncioctl; - ifp->if_output = pfsyncoutput; - ifp->if_type = IFT_PFSYNC; - ifp->if_snd.ifq_maxlen = ifqmaxlen; - ifp->if_hdrlen = sizeof(struct pfsync_header); - ifp->if_mtu = ETHERMTU; - mtx_init(&sc->sc_mtx, "pfsync", NULL, MTX_DEF); - mtx_init(&sc->sc_bulk_mtx, "pfsync bulk", NULL, MTX_DEF); - callout_init(&sc->sc_tmo, CALLOUT_MPSAFE); - callout_init_mtx(&sc->sc_bulk_tmo, &sc->sc_bulk_mtx, 0); - callout_init_mtx(&sc->sc_bulkfail_tmo, &sc->sc_bulk_mtx, 0); - - if_attach(ifp); - - bpfattach(ifp, DLT_PFSYNC, PFSYNC_HDRLEN); - - V_pfsyncif = sc; - - return (0); -} - -static void -pfsync_clone_destroy(struct ifnet *ifp) -{ - struct pfsync_softc *sc = ifp->if_softc; - - /* - * At this stage, everything should have already been - * cleared by pfsync_uninit(), and we have only to - * drain callouts. - */ - while (sc->sc_deferred > 0) { - struct pfsync_deferral *pd = TAILQ_FIRST(&sc->sc_deferrals); - - TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); - sc->sc_deferred--; - if (callout_stop(&pd->pd_tmo)) { - pf_release_state(pd->pd_st); - m_freem(pd->pd_m); - free(pd, M_PFSYNC); - } else { - pd->pd_refs++; - callout_drain(&pd->pd_tmo); - free(pd, M_PFSYNC); - } - } - - callout_drain(&sc->sc_tmo); - callout_drain(&sc->sc_bulkfail_tmo); - callout_drain(&sc->sc_bulk_tmo); - - if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) - (*carp_demote_adj_p)(-V_pfsync_carp_adj, "pfsync destroy"); - bpfdetach(ifp); - if_detach(ifp); - - pfsync_drop(sc); - - if_free(ifp); - if (sc->sc_imo.imo_membership) - pfsync_multicast_cleanup(sc); - mtx_destroy(&sc->sc_mtx); - mtx_destroy(&sc->sc_bulk_mtx); - free(sc, M_PFSYNC); - - V_pfsyncif = NULL; -} - -static int -pfsync_alloc_scrub_memory(struct pfsync_state_peer *s, - struct pf_state_peer *d) -{ - if (s->scrub.scrub_flag && d->scrub == NULL) { - d->scrub = uma_zalloc(V_pf_state_scrub_z, M_NOWAIT | M_ZERO); - if (d->scrub == NULL) - return (ENOMEM); - } - - return (0); -} - - -static int -pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pf_state *st = NULL; - struct pf_state_key *skw = NULL, *sks = NULL; - struct pf_rule *r = NULL; - struct pfi_kif *kif; - int error; - - PF_RULES_RASSERT(); - - if (sp->creatorid == 0 && V_pf_status.debug >= PF_DEBUG_MISC) { - printf("%s: invalid creator id: %08x\n", __func__, - ntohl(sp->creatorid)); - return (EINVAL); - } - - if ((kif = pfi_kif_find(sp->ifname)) == NULL) { - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("%s: unknown interface: %s\n", __func__, - sp->ifname); - if (flags & PFSYNC_SI_IOCTL) - return (EINVAL); - return (0); /* skip this state */ - } - - /* - * If the ruleset checksums match or the state is coming from the ioctl, - * it's safe to associate the state with the rule of that number. - */ - if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) && - (flags & (PFSYNC_SI_IOCTL | PFSYNC_SI_CKSUM)) && ntohl(sp->rule) < - pf_main_ruleset.rules[PF_RULESET_FILTER].active.rcount) - r = pf_main_ruleset.rules[ - PF_RULESET_FILTER].active.ptr_array[ntohl(sp->rule)]; - else - r = &V_pf_default_rule; - - if ((r->max_states && r->states_cur >= r->max_states)) - goto cleanup; - - /* - * XXXGL: consider M_WAITOK in ioctl path after. - */ - if ((st = uma_zalloc(V_pf_state_z, M_NOWAIT | M_ZERO)) == NULL) - goto cleanup; - - if ((skw = uma_zalloc(V_pf_state_key_z, M_NOWAIT)) == NULL) - goto cleanup; - - if (PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0], - &sp->key[PF_SK_STACK].addr[0], sp->af) || - PF_ANEQ(&sp->key[PF_SK_WIRE].addr[1], - &sp->key[PF_SK_STACK].addr[1], sp->af) || - sp->key[PF_SK_WIRE].port[0] != sp->key[PF_SK_STACK].port[0] || - sp->key[PF_SK_WIRE].port[1] != sp->key[PF_SK_STACK].port[1]) { - sks = uma_zalloc(V_pf_state_key_z, M_NOWAIT); - if (sks == NULL) - goto cleanup; - } else - sks = skw; - - /* allocate memory for scrub info */ - if (pfsync_alloc_scrub_memory(&sp->src, &st->src) || - pfsync_alloc_scrub_memory(&sp->dst, &st->dst)) - goto cleanup; - - /* copy to state key(s) */ - skw->addr[0] = sp->key[PF_SK_WIRE].addr[0]; - skw->addr[1] = sp->key[PF_SK_WIRE].addr[1]; - skw->port[0] = sp->key[PF_SK_WIRE].port[0]; - skw->port[1] = sp->key[PF_SK_WIRE].port[1]; - skw->proto = sp->proto; - skw->af = sp->af; - if (sks != skw) { - sks->addr[0] = sp->key[PF_SK_STACK].addr[0]; - sks->addr[1] = sp->key[PF_SK_STACK].addr[1]; - sks->port[0] = sp->key[PF_SK_STACK].port[0]; - sks->port[1] = sp->key[PF_SK_STACK].port[1]; - sks->proto = sp->proto; - sks->af = sp->af; - } - - /* copy to state */ - bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr)); - st->creation = time_uptime - ntohl(sp->creation); - st->expire = time_uptime; - if (sp->expire) { - uint32_t timeout; - - timeout = r->timeout[sp->timeout]; - if (!timeout) - timeout = V_pf_default_rule.timeout[sp->timeout]; - - /* sp->expire may have been adaptively scaled by export. */ - st->expire -= timeout - ntohl(sp->expire); - } - - st->direction = sp->direction; - st->log = sp->log; - st->timeout = sp->timeout; - st->state_flags = sp->state_flags; - - st->id = sp->id; - st->creatorid = sp->creatorid; - pf_state_peer_ntoh(&sp->src, &st->src); - pf_state_peer_ntoh(&sp->dst, &st->dst); - - st->rule.ptr = r; - st->nat_rule.ptr = NULL; - st->anchor.ptr = NULL; - st->rt_kif = NULL; - - st->pfsync_time = time_uptime; - st->sync_state = PFSYNC_S_NONE; - - /* XXX when we have nat_rule/anchors, use STATE_INC_COUNTERS */ - r->states_cur++; - r->states_tot++; - - if (!(flags & PFSYNC_SI_IOCTL)) - st->state_flags |= PFSTATE_NOSYNC; - - if ((error = pf_state_insert(kif, skw, sks, st)) != 0) { - /* XXX when we have nat_rule/anchors, use STATE_DEC_COUNTERS */ - r->states_cur--; - goto cleanup_state; - } - - if (!(flags & PFSYNC_SI_IOCTL)) { - st->state_flags &= ~PFSTATE_NOSYNC; - if (st->state_flags & PFSTATE_ACK) { - pfsync_q_ins(st, PFSYNC_S_IACK); - pfsync_push(sc); - } - } - st->state_flags &= ~PFSTATE_ACK; - PF_STATE_UNLOCK(st); - - return (0); - -cleanup: - error = ENOMEM; - if (skw == sks) - sks = NULL; - if (skw != NULL) - uma_zfree(V_pf_state_key_z, skw); - if (sks != NULL) - uma_zfree(V_pf_state_key_z, sks); - -cleanup_state: /* pf_state_insert() frees the state keys. */ - if (st) { - if (st->dst.scrub) - uma_zfree(V_pf_state_scrub_z, st->dst.scrub); - if (st->src.scrub) - uma_zfree(V_pf_state_scrub_z, st->src.scrub); - uma_zfree(V_pf_state_z, st); - } - return (error); -} - -static void -pfsync_input(struct mbuf *m, __unused int off) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_pkt pkt; - struct ip *ip = mtod(m, struct ip *); - struct pfsync_header *ph; - struct pfsync_subheader subh; - - int offset; - int rv; - uint16_t count; - - V_pfsyncstats.pfsyncs_ipackets++; - - /* Verify that we have a sync interface configured. */ - if (!sc || !sc->sc_sync_if || !V_pf_status.running || - (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - goto done; - - /* verify that the packet came in on the right interface */ - if (sc->sc_sync_if != m->m_pkthdr.rcvif) { - V_pfsyncstats.pfsyncs_badif++; - goto done; - } - - sc->sc_ifp->if_ipackets++; - sc->sc_ifp->if_ibytes += m->m_pkthdr.len; - /* verify that the IP TTL is 255. */ - if (ip->ip_ttl != PFSYNC_DFLTTL) { - V_pfsyncstats.pfsyncs_badttl++; - goto done; - } - - offset = ip->ip_hl << 2; - if (m->m_pkthdr.len < offset + sizeof(*ph)) { - V_pfsyncstats.pfsyncs_hdrops++; - goto done; - } - - if (offset + sizeof(*ph) > m->m_len) { - if (m_pullup(m, offset + sizeof(*ph)) == NULL) { - V_pfsyncstats.pfsyncs_hdrops++; - return; - } - ip = mtod(m, struct ip *); - } - ph = (struct pfsync_header *)((char *)ip + offset); - - /* verify the version */ - if (ph->version != PFSYNC_VERSION) { - V_pfsyncstats.pfsyncs_badver++; - goto done; - } - - /* Cheaper to grab this now than having to mess with mbufs later */ - pkt.ip = ip; - pkt.src = ip->ip_src; - pkt.flags = 0; - - /* - * Trusting pf_chksum during packet processing, as well as seeking - * in interface name tree, require holding PF_RULES_RLOCK(). - */ - PF_RULES_RLOCK(); - if (!bcmp(&ph->pfcksum, &V_pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH)) - pkt.flags |= PFSYNC_SI_CKSUM; - - offset += sizeof(*ph); - for (;;) { - m_copydata(m, offset, sizeof(subh), (caddr_t)&subh); - offset += sizeof(subh); - - if (subh.action >= PFSYNC_ACT_MAX) { - V_pfsyncstats.pfsyncs_badact++; - PF_RULES_RUNLOCK(); - goto done; - } - - count = ntohs(subh.count); - V_pfsyncstats.pfsyncs_iacts[subh.action] += count; - rv = (*pfsync_acts[subh.action])(&pkt, m, offset, count); - if (rv == -1) { - PF_RULES_RUNLOCK(); - return; - } - - offset += rv; - } - PF_RULES_RUNLOCK(); - -done: - m_freem(m); -} - -static int -pfsync_in_clr(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct pfsync_clr *clr; - struct mbuf *mp; - int len = sizeof(*clr) * count; - int i, offp; - u_int32_t creatorid; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - clr = (struct pfsync_clr *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - creatorid = clr[i].creatorid; - - if (clr[i].ifname[0] != '\0' && - pfi_kif_find(clr[i].ifname) == NULL) - continue; - - for (int i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - struct pf_state *s; -relock: - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - if (s->creatorid == creatorid) { - s->state_flags |= PFSTATE_NOSYNC; - pf_unlink_state(s, PF_ENTER_LOCKED); - goto relock; - } - } - PF_HASHROW_UNLOCK(ih); - } - } - - return (len); -} - -static int -pfsync_in_ins(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct mbuf *mp; - struct pfsync_state *sa, *sp; - int len = sizeof(*sp) * count; - int i, offp; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - sa = (struct pfsync_state *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - sp = &sa[i]; - - /* Check for invalid values. */ - if (sp->timeout >= PFTM_MAX || - sp->src.state > PF_TCPS_PROXY_DST || - sp->dst.state > PF_TCPS_PROXY_DST || - sp->direction > PF_OUT || - (sp->af != AF_INET && sp->af != AF_INET6)) { - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("%s: invalid value\n", __func__); - V_pfsyncstats.pfsyncs_badval++; - continue; - } - - if (pfsync_state_import(sp, pkt->flags) == ENOMEM) - /* Drop out, but process the rest of the actions. */ - break; - } - - return (len); -} - -static int -pfsync_in_iack(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct pfsync_ins_ack *ia, *iaa; - struct pf_state *st; - - struct mbuf *mp; - int len = count * sizeof(*ia); - int offp, i; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - iaa = (struct pfsync_ins_ack *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - ia = &iaa[i]; - - st = pf_find_state_byid(ia->id, ia->creatorid); - if (st == NULL) - continue; - - if (st->state_flags & PFSTATE_ACK) { - PFSYNC_LOCK(V_pfsyncif); - pfsync_undefer_state(st, 0); - PFSYNC_UNLOCK(V_pfsyncif); - } - PF_STATE_UNLOCK(st); - } - /* - * XXX this is not yet implemented, but we know the size of the - * message so we can skip it. - */ - - return (count * sizeof(struct pfsync_ins_ack)); -} - -static int -pfsync_upd_tcp(struct pf_state *st, struct pfsync_state_peer *src, - struct pfsync_state_peer *dst) -{ - int sfail = 0; - - PF_STATE_LOCK_ASSERT(st); - - /* - * The state should never go backwards except - * for syn-proxy states. Neither should the - * sequence window slide backwards. - */ - if (st->src.state > src->state && - (st->src.state < PF_TCPS_PROXY_SRC || - src->state >= PF_TCPS_PROXY_SRC)) - sfail = 1; - else if (SEQ_GT(st->src.seqlo, ntohl(src->seqlo))) - sfail = 3; - else if (st->dst.state > dst->state) { - /* There might still be useful - * information about the src state here, - * so import that part of the update, - * then "fail" so we send the updated - * state back to the peer who is missing - * our what we know. */ - pf_state_peer_ntoh(src, &st->src); - /* XXX do anything with timeouts? */ - sfail = 7; - } else if (st->dst.state >= TCPS_SYN_SENT && - SEQ_GT(st->dst.seqlo, ntohl(dst->seqlo))) - sfail = 4; - - return (sfail); -} - -static int -pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_state *sa, *sp; - struct pf_state_key *sk; - struct pf_state *st; - int sfail; - - struct mbuf *mp; - int len = count * sizeof(*sp); - int offp, i; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - sa = (struct pfsync_state *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - sp = &sa[i]; - - /* check for invalid values */ - if (sp->timeout >= PFTM_MAX || - sp->src.state > PF_TCPS_PROXY_DST || - sp->dst.state > PF_TCPS_PROXY_DST) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pfsync_input: PFSYNC_ACT_UPD: " - "invalid value\n"); - } - V_pfsyncstats.pfsyncs_badval++; - continue; - } - - st = pf_find_state_byid(sp->id, sp->creatorid); - if (st == NULL) { - /* insert the update */ - if (pfsync_state_import(sp, 0)) - V_pfsyncstats.pfsyncs_badstate++; - continue; - } - - if (st->state_flags & PFSTATE_ACK) { - PFSYNC_LOCK(sc); - pfsync_undefer_state(st, 1); - PFSYNC_UNLOCK(sc); - } - - sk = st->key[PF_SK_WIRE]; /* XXX right one? */ - sfail = 0; - if (sk->proto == IPPROTO_TCP) - sfail = pfsync_upd_tcp(st, &sp->src, &sp->dst); - else { - /* - * Non-TCP protocol state machine always go - * forwards - */ - if (st->src.state > sp->src.state) - sfail = 5; - else if (st->dst.state > sp->dst.state) - sfail = 6; - } - - if (sfail) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pfsync: %s stale update (%d)" - " id: %016llx creatorid: %08x\n", - (sfail < 7 ? "ignoring" : "partial"), - sfail, (unsigned long long)be64toh(st->id), - ntohl(st->creatorid)); - } - V_pfsyncstats.pfsyncs_stale++; - - pfsync_update_state(st); - PF_STATE_UNLOCK(st); - PFSYNC_LOCK(sc); - pfsync_push(sc); - PFSYNC_UNLOCK(sc); - continue; - } - pfsync_alloc_scrub_memory(&sp->dst, &st->dst); - pf_state_peer_ntoh(&sp->src, &st->src); - pf_state_peer_ntoh(&sp->dst, &st->dst); - st->expire = time_uptime; - st->timeout = sp->timeout; - st->pfsync_time = time_uptime; - PF_STATE_UNLOCK(st); - } - - return (len); -} - -static int -pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_upd_c *ua, *up; - struct pf_state_key *sk; - struct pf_state *st; - - int len = count * sizeof(*up); - int sfail; - - struct mbuf *mp; - int offp, i; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - ua = (struct pfsync_upd_c *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - up = &ua[i]; - - /* check for invalid values */ - if (up->timeout >= PFTM_MAX || - up->src.state > PF_TCPS_PROXY_DST || - up->dst.state > PF_TCPS_PROXY_DST) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pfsync_input: " - "PFSYNC_ACT_UPD_C: " - "invalid value\n"); - } - V_pfsyncstats.pfsyncs_badval++; - continue; - } - - st = pf_find_state_byid(up->id, up->creatorid); - if (st == NULL) { - /* We don't have this state. Ask for it. */ - PFSYNC_LOCK(sc); - pfsync_request_update(up->creatorid, up->id); - PFSYNC_UNLOCK(sc); - continue; - } - - if (st->state_flags & PFSTATE_ACK) { - PFSYNC_LOCK(sc); - pfsync_undefer_state(st, 1); - PFSYNC_UNLOCK(sc); - } - - sk = st->key[PF_SK_WIRE]; /* XXX right one? */ - sfail = 0; - if (sk->proto == IPPROTO_TCP) - sfail = pfsync_upd_tcp(st, &up->src, &up->dst); - else { - /* - * Non-TCP protocol state machine always go forwards - */ - if (st->src.state > up->src.state) - sfail = 5; - else if (st->dst.state > up->dst.state) - sfail = 6; - } - - if (sfail) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pfsync: ignoring stale update " - "(%d) id: %016llx " - "creatorid: %08x\n", sfail, - (unsigned long long)be64toh(st->id), - ntohl(st->creatorid)); - } - V_pfsyncstats.pfsyncs_stale++; - - pfsync_update_state(st); - PF_STATE_UNLOCK(st); - PFSYNC_LOCK(sc); - pfsync_push(sc); - PFSYNC_UNLOCK(sc); - continue; - } - pfsync_alloc_scrub_memory(&up->dst, &st->dst); - pf_state_peer_ntoh(&up->src, &st->src); - pf_state_peer_ntoh(&up->dst, &st->dst); - st->expire = time_uptime; - st->timeout = up->timeout; - st->pfsync_time = time_uptime; - PF_STATE_UNLOCK(st); - } - - return (len); -} - -static int -pfsync_in_ureq(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct pfsync_upd_req *ur, *ura; - struct mbuf *mp; - int len = count * sizeof(*ur); - int i, offp; - - struct pf_state *st; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - ura = (struct pfsync_upd_req *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - ur = &ura[i]; - - if (ur->id == 0 && ur->creatorid == 0) - pfsync_bulk_start(); - else { - st = pf_find_state_byid(ur->id, ur->creatorid); - if (st == NULL) { - V_pfsyncstats.pfsyncs_badstate++; - continue; - } - if (st->state_flags & PFSTATE_NOSYNC) { - PF_STATE_UNLOCK(st); - continue; - } - - pfsync_update_state_req(st); - PF_STATE_UNLOCK(st); - } - } - - return (len); -} - -static int -pfsync_in_del(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct mbuf *mp; - struct pfsync_state *sa, *sp; - struct pf_state *st; - int len = count * sizeof(*sp); - int offp, i; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - sa = (struct pfsync_state *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - sp = &sa[i]; - - st = pf_find_state_byid(sp->id, sp->creatorid); - if (st == NULL) { - V_pfsyncstats.pfsyncs_badstate++; - continue; - } - st->state_flags |= PFSTATE_NOSYNC; - pf_unlink_state(st, PF_ENTER_LOCKED); - } - - return (len); -} - -static int -pfsync_in_del_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct mbuf *mp; - struct pfsync_del_c *sa, *sp; - struct pf_state *st; - int len = count * sizeof(*sp); - int offp, i; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - sa = (struct pfsync_del_c *)(mp->m_data + offp); - - for (i = 0; i < count; i++) { - sp = &sa[i]; - - st = pf_find_state_byid(sp->id, sp->creatorid); - if (st == NULL) { - V_pfsyncstats.pfsyncs_badstate++; - continue; - } - - st->state_flags |= PFSTATE_NOSYNC; - pf_unlink_state(st, PF_ENTER_LOCKED); - } - - return (len); -} - -static int -pfsync_in_bus(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_bus *bus; - struct mbuf *mp; - int len = count * sizeof(*bus); - int offp; - - PFSYNC_BLOCK(sc); - - /* If we're not waiting for a bulk update, who cares. */ - if (sc->sc_ureq_sent == 0) { - PFSYNC_BUNLOCK(sc); - return (len); - } - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - PFSYNC_BUNLOCK(sc); - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - bus = (struct pfsync_bus *)(mp->m_data + offp); - - switch (bus->status) { - case PFSYNC_BUS_START: - callout_reset(&sc->sc_bulkfail_tmo, 4 * hz + - V_pf_limits[PF_LIMIT_STATES].limit / - ((sc->sc_ifp->if_mtu - PFSYNC_MINPKT) / - sizeof(struct pfsync_state)), - pfsync_bulk_fail, sc); - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: received bulk update start\n"); - break; - - case PFSYNC_BUS_END: - if (time_uptime - ntohl(bus->endtime) >= - sc->sc_ureq_sent) { - /* that's it, we're happy */ - sc->sc_ureq_sent = 0; - sc->sc_bulk_tries = 0; - callout_stop(&sc->sc_bulkfail_tmo); - if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) - (*carp_demote_adj_p)(-V_pfsync_carp_adj, - "pfsync bulk done"); - sc->sc_flags |= PFSYNCF_OK; - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: received valid " - "bulk update end\n"); - } else { - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: received invalid " - "bulk update end: bad timestamp\n"); - } - break; - } - PFSYNC_BUNLOCK(sc); - - return (len); -} - -static int -pfsync_in_tdb(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - int len = count * sizeof(struct pfsync_tdb); - -#if defined(IPSEC) - struct pfsync_tdb *tp; - struct mbuf *mp; - int offp; - int i; - int s; - - mp = m_pulldown(m, offset, len, &offp); - if (mp == NULL) { - V_pfsyncstats.pfsyncs_badlen++; - return (-1); - } - tp = (struct pfsync_tdb *)(mp->m_data + offp); - - for (i = 0; i < count; i++) - pfsync_update_net_tdb(&tp[i]); -#endif - - return (len); -} - -#if defined(IPSEC) -/* Update an in-kernel tdb. Silently fail if no tdb is found. */ -static void -pfsync_update_net_tdb(struct pfsync_tdb *pt) -{ - struct tdb *tdb; - int s; - - /* check for invalid values */ - if (ntohl(pt->spi) <= SPI_RESERVED_MAX || - (pt->dst.sa.sa_family != AF_INET && - pt->dst.sa.sa_family != AF_INET6)) - goto bad; - - tdb = gettdb(pt->spi, &pt->dst, pt->sproto); - if (tdb) { - pt->rpl = ntohl(pt->rpl); - pt->cur_bytes = (unsigned long long)be64toh(pt->cur_bytes); - - /* Neither replay nor byte counter should ever decrease. */ - if (pt->rpl < tdb->tdb_rpl || - pt->cur_bytes < tdb->tdb_cur_bytes) { - goto bad; - } - - tdb->tdb_rpl = pt->rpl; - tdb->tdb_cur_bytes = pt->cur_bytes; - } - return; - -bad: - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync_insert: PFSYNC_ACT_TDB_UPD: " - "invalid value\n"); - V_pfsyncstats.pfsyncs_badstate++; - return; -} -#endif - - -static int -pfsync_in_eof(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - /* check if we are at the right place in the packet */ - if (offset != m->m_pkthdr.len - sizeof(struct pfsync_eof)) - V_pfsyncstats.pfsyncs_badact++; - - /* we're done. free and let the caller return */ - m_freem(m); - return (-1); -} - -static int -pfsync_in_error(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) -{ - V_pfsyncstats.pfsyncs_badact++; - - m_freem(m); - return (-1); -} - -static int -pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, - struct route *rt) -{ - m_freem(m); - return (0); -} - -/* ARGSUSED */ -static int -pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct pfsync_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *)data; - struct pfsyncreq pfsyncr; - int error; - - switch (cmd) { - case SIOCSIFFLAGS: - PFSYNC_LOCK(sc); - if (ifp->if_flags & IFF_UP) - ifp->if_drv_flags |= IFF_DRV_RUNNING; - else - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - PFSYNC_UNLOCK(sc); - break; - case SIOCSIFMTU: - if (!sc->sc_sync_if || - ifr->ifr_mtu <= PFSYNC_MINPKT || - ifr->ifr_mtu > sc->sc_sync_if->if_mtu) - return (EINVAL); - if (ifr->ifr_mtu < ifp->if_mtu) { - PFSYNC_LOCK(sc); - if (sc->sc_len > PFSYNC_MINPKT) - pfsync_sendout(1); - PFSYNC_UNLOCK(sc); - } - ifp->if_mtu = ifr->ifr_mtu; - break; - case SIOCGETPFSYNC: - bzero(&pfsyncr, sizeof(pfsyncr)); - PFSYNC_LOCK(sc); - if (sc->sc_sync_if) { - strlcpy(pfsyncr.pfsyncr_syncdev, - sc->sc_sync_if->if_xname, IFNAMSIZ); - } - pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer; - pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates; - pfsyncr.pfsyncr_defer = (PFSYNCF_DEFER == - (sc->sc_flags & PFSYNCF_DEFER)); - PFSYNC_UNLOCK(sc); - return (copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr))); - - case SIOCSETPFSYNC: - { - struct ip_moptions *imo = &sc->sc_imo; - struct ifnet *sifp; - struct ip *ip; - void *mship = NULL; - - if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0) - return (error); - if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr)))) - return (error); - - if (pfsyncr.pfsyncr_maxupdates > 255) - return (EINVAL); - - if (pfsyncr.pfsyncr_syncdev[0] == 0) - sifp = NULL; - else if ((sifp = ifunit_ref(pfsyncr.pfsyncr_syncdev)) == NULL) - return (EINVAL); - - if (pfsyncr.pfsyncr_syncpeer.s_addr == 0 && sifp != NULL) - mship = malloc((sizeof(struct in_multi *) * - IP_MIN_MEMBERSHIPS), M_PFSYNC, M_WAITOK | M_ZERO); - - PFSYNC_LOCK(sc); - if (pfsyncr.pfsyncr_syncpeer.s_addr == 0) - sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP); - else - sc->sc_sync_peer.s_addr = - pfsyncr.pfsyncr_syncpeer.s_addr; - - sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates; - if (pfsyncr.pfsyncr_defer) { - sc->sc_flags |= PFSYNCF_DEFER; - pfsync_defer_ptr = pfsync_defer; - } else { - sc->sc_flags &= ~PFSYNCF_DEFER; - pfsync_defer_ptr = NULL; - } - - if (sifp == NULL) { - if (sc->sc_sync_if) - if_rele(sc->sc_sync_if); - sc->sc_sync_if = NULL; - if (imo->imo_membership) - pfsync_multicast_cleanup(sc); - PFSYNC_UNLOCK(sc); - break; - } - - if (sc->sc_len > PFSYNC_MINPKT && - (sifp->if_mtu < sc->sc_ifp->if_mtu || - (sc->sc_sync_if != NULL && - sifp->if_mtu < sc->sc_sync_if->if_mtu) || - sifp->if_mtu < MCLBYTES - sizeof(struct ip))) - pfsync_sendout(1); - - if (imo->imo_membership) - pfsync_multicast_cleanup(sc); - - if (sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) { - error = pfsync_multicast_setup(sc, sifp, mship); - if (error) { - if_rele(sifp); - free(mship, M_PFSYNC); - return (error); - } - } - if (sc->sc_sync_if) - if_rele(sc->sc_sync_if); - sc->sc_sync_if = sifp; - - ip = &sc->sc_template; - bzero(ip, sizeof(*ip)); - ip->ip_v = IPVERSION; - ip->ip_hl = sizeof(sc->sc_template) >> 2; - ip->ip_tos = IPTOS_LOWDELAY; - /* len and id are set later. */ - ip->ip_off = IP_DF; - ip->ip_ttl = PFSYNC_DFLTTL; - ip->ip_p = IPPROTO_PFSYNC; - ip->ip_src.s_addr = INADDR_ANY; - ip->ip_dst.s_addr = sc->sc_sync_peer.s_addr; - - /* Request a full state table update. */ - if ((sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) - (*carp_demote_adj_p)(V_pfsync_carp_adj, - "pfsync bulk start"); - sc->sc_flags &= ~PFSYNCF_OK; - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: requesting bulk update\n"); - pfsync_request_update(0, 0); - PFSYNC_UNLOCK(sc); - PFSYNC_BLOCK(sc); - sc->sc_ureq_sent = time_uptime; - callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulk_fail, - sc); - PFSYNC_BUNLOCK(sc); - - break; - } - default: - return (ENOTTY); - } - - return (0); -} - -static int -pfsync_out_state(struct pf_state *st, struct mbuf *m, int offset) -{ - struct pfsync_state *sp = (struct pfsync_state *)(m->m_data + offset); - - pfsync_state_export(sp, st); - - return (sizeof(*sp)); -} - -static int -pfsync_out_iack(struct pf_state *st, struct mbuf *m, int offset) -{ - struct pfsync_ins_ack *iack = - (struct pfsync_ins_ack *)(m->m_data + offset); - - iack->id = st->id; - iack->creatorid = st->creatorid; - - return (sizeof(*iack)); -} - -static int -pfsync_out_upd_c(struct pf_state *st, struct mbuf *m, int offset) -{ - struct pfsync_upd_c *up = (struct pfsync_upd_c *)(m->m_data + offset); - - bzero(up, sizeof(*up)); - up->id = st->id; - pf_state_peer_hton(&st->src, &up->src); - pf_state_peer_hton(&st->dst, &up->dst); - up->creatorid = st->creatorid; - up->timeout = st->timeout; - - return (sizeof(*up)); -} - -static int -pfsync_out_del(struct pf_state *st, struct mbuf *m, int offset) -{ - struct pfsync_del_c *dp = (struct pfsync_del_c *)(m->m_data + offset); - - dp->id = st->id; - dp->creatorid = st->creatorid; - - st->state_flags |= PFSTATE_NOSYNC; - - return (sizeof(*dp)); -} - -static void -pfsync_drop(struct pfsync_softc *sc) -{ - struct pf_state *st, *next; - struct pfsync_upd_req_item *ur; - int q; - - for (q = 0; q < PFSYNC_S_COUNT; q++) { - if (TAILQ_EMPTY(&sc->sc_qs[q])) - continue; - - TAILQ_FOREACH_SAFE(st, &sc->sc_qs[q], sync_list, next) { - KASSERT(st->sync_state == q, - ("%s: st->sync_state == q", - __func__)); - st->sync_state = PFSYNC_S_NONE; - pf_release_state(st); - } - TAILQ_INIT(&sc->sc_qs[q]); - } - - while ((ur = TAILQ_FIRST(&sc->sc_upd_req_list)) != NULL) { - TAILQ_REMOVE(&sc->sc_upd_req_list, ur, ur_entry); - free(ur, M_PFSYNC); - } - - sc->sc_plus = NULL; - sc->sc_len = PFSYNC_MINPKT; -} - -static void -pfsync_sendout(int schedswi) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct ifnet *ifp = sc->sc_ifp; - struct mbuf *m; - struct ip *ip; - struct pfsync_header *ph; - struct pfsync_subheader *subh; - struct pf_state *st, *next; - struct pfsync_upd_req_item *ur; - int offset; - int q, count = 0; - - KASSERT(sc != NULL, ("%s: null sc", __func__)); - KASSERT(sc->sc_len > PFSYNC_MINPKT, - ("%s: sc_len %zu", __func__, sc->sc_len)); - PFSYNC_LOCK_ASSERT(sc); - - if (ifp->if_bpf == NULL && sc->sc_sync_if == NULL) { - pfsync_drop(sc); - return; - } - - m = m_get2(M_NOWAIT, MT_DATA, M_PKTHDR, max_linkhdr + sc->sc_len); - if (m == NULL) { - sc->sc_ifp->if_oerrors++; - V_pfsyncstats.pfsyncs_onomem++; - return; - } - m->m_data += max_linkhdr; - m->m_len = m->m_pkthdr.len = sc->sc_len; - - /* build the ip header */ - ip = (struct ip *)m->m_data; - bcopy(&sc->sc_template, ip, sizeof(*ip)); - offset = sizeof(*ip); - - ip->ip_len = m->m_pkthdr.len; - ip->ip_id = htons(ip_randomid()); - - /* build the pfsync header */ - ph = (struct pfsync_header *)(m->m_data + offset); - bzero(ph, sizeof(*ph)); - offset += sizeof(*ph); - - ph->version = PFSYNC_VERSION; - ph->len = htons(sc->sc_len - sizeof(*ip)); - bcopy(V_pf_status.pf_chksum, ph->pfcksum, PF_MD5_DIGEST_LENGTH); - - /* walk the queues */ - for (q = 0; q < PFSYNC_S_COUNT; q++) { - if (TAILQ_EMPTY(&sc->sc_qs[q])) - continue; - - subh = (struct pfsync_subheader *)(m->m_data + offset); - offset += sizeof(*subh); - - count = 0; - TAILQ_FOREACH_SAFE(st, &sc->sc_qs[q], sync_list, next) { - KASSERT(st->sync_state == q, - ("%s: st->sync_state == q", - __func__)); - /* - * XXXGL: some of write methods do unlocked reads - * of state data :( - */ - offset += pfsync_qs[q].write(st, m, offset); - st->sync_state = PFSYNC_S_NONE; - pf_release_state(st); - count++; - } - TAILQ_INIT(&sc->sc_qs[q]); - - bzero(subh, sizeof(*subh)); - subh->action = pfsync_qs[q].action; - subh->count = htons(count); - V_pfsyncstats.pfsyncs_oacts[pfsync_qs[q].action] += count; - } - - if (!TAILQ_EMPTY(&sc->sc_upd_req_list)) { - subh = (struct pfsync_subheader *)(m->m_data + offset); - offset += sizeof(*subh); - - count = 0; - while ((ur = TAILQ_FIRST(&sc->sc_upd_req_list)) != NULL) { - TAILQ_REMOVE(&sc->sc_upd_req_list, ur, ur_entry); - - bcopy(&ur->ur_msg, m->m_data + offset, - sizeof(ur->ur_msg)); - offset += sizeof(ur->ur_msg); - free(ur, M_PFSYNC); - count++; - } - - bzero(subh, sizeof(*subh)); - subh->action = PFSYNC_ACT_UPD_REQ; - subh->count = htons(count); - V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_UPD_REQ] += count; - } - - /* has someone built a custom region for us to add? */ - if (sc->sc_plus != NULL) { - bcopy(sc->sc_plus, m->m_data + offset, sc->sc_pluslen); - offset += sc->sc_pluslen; - - sc->sc_plus = NULL; - } - - subh = (struct pfsync_subheader *)(m->m_data + offset); - offset += sizeof(*subh); - - bzero(subh, sizeof(*subh)); - subh->action = PFSYNC_ACT_EOF; - subh->count = htons(1); - V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_EOF]++; - - /* XXX write checksum in EOF here */ - - /* we're done, let's put it on the wire */ - if (ifp->if_bpf) { - m->m_data += sizeof(*ip); - m->m_len = m->m_pkthdr.len = sc->sc_len - sizeof(*ip); - BPF_MTAP(ifp, m); - m->m_data -= sizeof(*ip); - m->m_len = m->m_pkthdr.len = sc->sc_len; - } - - if (sc->sc_sync_if == NULL) { - sc->sc_len = PFSYNC_MINPKT; - m_freem(m); - return; - } - - sc->sc_ifp->if_opackets++; - sc->sc_ifp->if_obytes += m->m_pkthdr.len; - sc->sc_len = PFSYNC_MINPKT; - - if (!_IF_QFULL(&sc->sc_ifp->if_snd)) - _IF_ENQUEUE(&sc->sc_ifp->if_snd, m); - else { - m_freem(m); - sc->sc_ifp->if_snd.ifq_drops++; - } - if (schedswi) - swi_sched(V_pfsync_swi_cookie, 0); -} - -static void -pfsync_insert_state(struct pf_state *st) -{ - struct pfsync_softc *sc = V_pfsyncif; - - if (st->state_flags & PFSTATE_NOSYNC) - return; - - if ((st->rule.ptr->rule_flag & PFRULE_NOSYNC) || - st->key[PF_SK_WIRE]->proto == IPPROTO_PFSYNC) { - st->state_flags |= PFSTATE_NOSYNC; - return; - } - - KASSERT(st->sync_state == PFSYNC_S_NONE, - ("%s: st->sync_state == PFSYNC_S_NONE", __func__)); - - PFSYNC_LOCK(sc); - if (sc->sc_len == PFSYNC_MINPKT) - callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); - - pfsync_q_ins(st, PFSYNC_S_INS); - PFSYNC_UNLOCK(sc); - - st->sync_updates = 0; -} - -static int -pfsync_defer(struct pf_state *st, struct mbuf *m) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_deferral *pd; - - if (m->m_flags & (M_BCAST|M_MCAST)) - return (0); - - PFSYNC_LOCK(sc); - - if (sc == NULL || !(sc->sc_ifp->if_flags & IFF_DRV_RUNNING) || - !(sc->sc_flags & PFSYNCF_DEFER)) { - PFSYNC_UNLOCK(sc); - return (0); - } - - if (sc->sc_deferred >= 128) - pfsync_undefer(TAILQ_FIRST(&sc->sc_deferrals), 0); - - pd = malloc(sizeof(*pd), M_PFSYNC, M_NOWAIT); - if (pd == NULL) - return (0); - sc->sc_deferred++; - - m->m_flags |= M_SKIP_FIREWALL; - st->state_flags |= PFSTATE_ACK; - - pd->pd_sc = sc; - pd->pd_refs = 0; - pd->pd_st = st; - pf_ref_state(st); - pd->pd_m = m; - - TAILQ_INSERT_TAIL(&sc->sc_deferrals, pd, pd_entry); - callout_init_mtx(&pd->pd_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); - callout_reset(&pd->pd_tmo, 10, pfsync_defer_tmo, pd); - - pfsync_push(sc); - - return (1); -} - -static void -pfsync_undefer(struct pfsync_deferral *pd, int drop) -{ - struct pfsync_softc *sc = pd->pd_sc; - struct mbuf *m = pd->pd_m; - struct pf_state *st = pd->pd_st; - - PFSYNC_LOCK_ASSERT(sc); - - TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); - sc->sc_deferred--; - pd->pd_st->state_flags &= ~PFSTATE_ACK; /* XXX: locking! */ - free(pd, M_PFSYNC); - pf_release_state(st); - - if (drop) - m_freem(m); - else { - _IF_ENQUEUE(&sc->sc_ifp->if_snd, m); - pfsync_push(sc); - } -} - -static void -pfsync_defer_tmo(void *arg) -{ - struct pfsync_deferral *pd = arg; - struct pfsync_softc *sc = pd->pd_sc; - struct mbuf *m = pd->pd_m; - struct pf_state *st = pd->pd_st; - - PFSYNC_LOCK_ASSERT(sc); - - CURVNET_SET(m->m_pkthdr.rcvif->if_vnet); - - TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); - sc->sc_deferred--; - pd->pd_st->state_flags &= ~PFSTATE_ACK; /* XXX: locking! */ - if (pd->pd_refs == 0) - free(pd, M_PFSYNC); - PFSYNC_UNLOCK(sc); - - ip_output(m, NULL, NULL, 0, NULL, NULL); - - pf_release_state(st); - - CURVNET_RESTORE(); -} - -static void -pfsync_undefer_state(struct pf_state *st, int drop) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_deferral *pd; - - PFSYNC_LOCK_ASSERT(sc); - - TAILQ_FOREACH(pd, &sc->sc_deferrals, pd_entry) { - if (pd->pd_st == st) { - if (callout_stop(&pd->pd_tmo)) - pfsync_undefer(pd, drop); - return; - } - } - - panic("%s: unable to find deferred state", __func__); -} - -static void -pfsync_update_state(struct pf_state *st) -{ - struct pfsync_softc *sc = V_pfsyncif; - int sync = 0; - - PF_STATE_LOCK_ASSERT(st); - PFSYNC_LOCK(sc); - - if (st->state_flags & PFSTATE_ACK) - pfsync_undefer_state(st, 0); - if (st->state_flags & PFSTATE_NOSYNC) { - if (st->sync_state != PFSYNC_S_NONE) - pfsync_q_del(st); - PFSYNC_UNLOCK(sc); - return; - } - - if (sc->sc_len == PFSYNC_MINPKT) - callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); - - switch (st->sync_state) { - case PFSYNC_S_UPD_C: - case PFSYNC_S_UPD: - case PFSYNC_S_INS: - /* we're already handling it */ - - if (st->key[PF_SK_WIRE]->proto == IPPROTO_TCP) { - st->sync_updates++; - if (st->sync_updates >= sc->sc_maxupdates) - sync = 1; - } - break; - - case PFSYNC_S_IACK: - pfsync_q_del(st); - case PFSYNC_S_NONE: - pfsync_q_ins(st, PFSYNC_S_UPD_C); - st->sync_updates = 0; - break; - - default: - panic("%s: unexpected sync state %d", __func__, st->sync_state); - } - - if (sync || (time_uptime - st->pfsync_time) < 2) - pfsync_push(sc); - - PFSYNC_UNLOCK(sc); -} - -static void -pfsync_request_update(u_int32_t creatorid, u_int64_t id) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct pfsync_upd_req_item *item; - size_t nlen = sizeof(struct pfsync_upd_req); - - PFSYNC_LOCK_ASSERT(sc); - - /* - * This code does nothing to prevent multiple update requests for the - * same state being generated. - */ - item = malloc(sizeof(*item), M_PFSYNC, M_NOWAIT); - if (item == NULL) - return; /* XXX stats */ - - item->ur_msg.id = id; - item->ur_msg.creatorid = creatorid; - - if (TAILQ_EMPTY(&sc->sc_upd_req_list)) - nlen += sizeof(struct pfsync_subheader); - - if (sc->sc_len + nlen > sc->sc_ifp->if_mtu) { - pfsync_sendout(1); - - nlen = sizeof(struct pfsync_subheader) + - sizeof(struct pfsync_upd_req); - } - - TAILQ_INSERT_TAIL(&sc->sc_upd_req_list, item, ur_entry); - sc->sc_len += nlen; - - pfsync_push(sc); -} - -static void -pfsync_update_state_req(struct pf_state *st) -{ - struct pfsync_softc *sc = V_pfsyncif; - - PF_STATE_LOCK_ASSERT(st); - PFSYNC_LOCK(sc); - - if (st->state_flags & PFSTATE_NOSYNC) { - if (st->sync_state != PFSYNC_S_NONE) - pfsync_q_del(st); - PFSYNC_UNLOCK(sc); - return; - } - - switch (st->sync_state) { - case PFSYNC_S_UPD_C: - case PFSYNC_S_IACK: - pfsync_q_del(st); - case PFSYNC_S_NONE: - pfsync_q_ins(st, PFSYNC_S_UPD); - pfsync_push(sc); - break; - - case PFSYNC_S_INS: - case PFSYNC_S_UPD: - case PFSYNC_S_DEL: - /* we're already handling it */ - break; - - default: - panic("%s: unexpected sync state %d", __func__, st->sync_state); - } - - PFSYNC_UNLOCK(sc); -} - -static void -pfsync_delete_state(struct pf_state *st) -{ - struct pfsync_softc *sc = V_pfsyncif; - - PFSYNC_LOCK(sc); - if (st->state_flags & PFSTATE_ACK) - pfsync_undefer_state(st, 1); - if (st->state_flags & PFSTATE_NOSYNC) { - if (st->sync_state != PFSYNC_S_NONE) - pfsync_q_del(st); - PFSYNC_UNLOCK(sc); - return; - } - - if (sc->sc_len == PFSYNC_MINPKT) - callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); - - switch (st->sync_state) { - case PFSYNC_S_INS: - /* We never got to tell the world so just forget about it. */ - pfsync_q_del(st); - break; - - case PFSYNC_S_UPD_C: - case PFSYNC_S_UPD: - case PFSYNC_S_IACK: - pfsync_q_del(st); - /* FALLTHROUGH to putting it on the del list */ - - case PFSYNC_S_NONE: - pfsync_q_ins(st, PFSYNC_S_DEL); - break; - - default: - panic("%s: unexpected sync state %d", __func__, st->sync_state); - } - PFSYNC_UNLOCK(sc); -} - -static void -pfsync_clear_states(u_int32_t creatorid, const char *ifname) -{ - struct pfsync_softc *sc = V_pfsyncif; - struct { - struct pfsync_subheader subh; - struct pfsync_clr clr; - } __packed r; - - bzero(&r, sizeof(r)); - - r.subh.action = PFSYNC_ACT_CLR; - r.subh.count = htons(1); - V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_CLR]++; - - strlcpy(r.clr.ifname, ifname, sizeof(r.clr.ifname)); - r.clr.creatorid = creatorid; - - PFSYNC_LOCK(sc); - pfsync_send_plus(&r, sizeof(r)); - PFSYNC_UNLOCK(sc); -} - -static void -pfsync_q_ins(struct pf_state *st, int q) -{ - struct pfsync_softc *sc = V_pfsyncif; - size_t nlen = pfsync_qs[q].len; - - PFSYNC_LOCK_ASSERT(sc); - - KASSERT(st->sync_state == PFSYNC_S_NONE, - ("%s: st->sync_state == PFSYNC_S_NONE", __func__)); - KASSERT(sc->sc_len >= PFSYNC_MINPKT, ("pfsync pkt len is too low %zu", - sc->sc_len)); - - if (TAILQ_EMPTY(&sc->sc_qs[q])) - nlen += sizeof(struct pfsync_subheader); - - if (sc->sc_len + nlen > sc->sc_ifp->if_mtu) { - pfsync_sendout(1); - - nlen = sizeof(struct pfsync_subheader) + pfsync_qs[q].len; - } - - sc->sc_len += nlen; - TAILQ_INSERT_TAIL(&sc->sc_qs[q], st, sync_list); - st->sync_state = q; - pf_ref_state(st); -} - -static void -pfsync_q_del(struct pf_state *st) -{ - struct pfsync_softc *sc = V_pfsyncif; - int q = st->sync_state; - - PFSYNC_LOCK_ASSERT(sc); - KASSERT(st->sync_state != PFSYNC_S_NONE, - ("%s: st->sync_state != PFSYNC_S_NONE", __func__)); - - sc->sc_len -= pfsync_qs[q].len; - TAILQ_REMOVE(&sc->sc_qs[q], st, sync_list); - st->sync_state = PFSYNC_S_NONE; - pf_release_state(st); - - if (TAILQ_EMPTY(&sc->sc_qs[q])) - sc->sc_len -= sizeof(struct pfsync_subheader); -} - -static void -pfsync_bulk_start(void) -{ - struct pfsync_softc *sc = V_pfsyncif; - - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: received bulk update request\n"); - - PFSYNC_BLOCK(sc); - - sc->sc_ureq_received = time_uptime; - sc->sc_bulk_hashid = 0; - sc->sc_bulk_stateid = 0; - pfsync_bulk_status(PFSYNC_BUS_START); - callout_reset(&sc->sc_bulk_tmo, 1, pfsync_bulk_update, sc); - PFSYNC_BUNLOCK(sc); -} - -static void -pfsync_bulk_update(void *arg) -{ - struct pfsync_softc *sc = arg; - struct pf_state *s; - int i, sent = 0; - - PFSYNC_BLOCK_ASSERT(sc); - CURVNET_SET(sc->sc_ifp->if_vnet); - - /* - * Start with last state from previous invocation. - * It may had gone, in this case start from the - * hash slot. - */ - s = pf_find_state_byid(sc->sc_bulk_stateid, sc->sc_bulk_creatorid); - - if (s != NULL) - i = PF_IDHASH(s); - else - i = sc->sc_bulk_hashid; - - for (; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - - if (s != NULL) - PF_HASHROW_ASSERT(ih); - else { - PF_HASHROW_LOCK(ih); - s = LIST_FIRST(&ih->states); - } - - for (; s; s = LIST_NEXT(s, entry)) { - - if (sent > 1 && (sc->sc_ifp->if_mtu - sc->sc_len) < - sizeof(struct pfsync_state)) { - /* We've filled a packet. */ - sc->sc_bulk_hashid = i; - sc->sc_bulk_stateid = s->id; - sc->sc_bulk_creatorid = s->creatorid; - PF_HASHROW_UNLOCK(ih); - callout_reset(&sc->sc_bulk_tmo, 1, - pfsync_bulk_update, sc); - goto full; - } - - if (s->sync_state == PFSYNC_S_NONE && - s->timeout < PFTM_MAX && - s->pfsync_time <= sc->sc_ureq_received) { - PFSYNC_LOCK(sc); - pfsync_update_state_req(s); - PFSYNC_UNLOCK(sc); - sent++; - } - } - PF_HASHROW_UNLOCK(ih); - } - - /* We're done. */ - pfsync_bulk_status(PFSYNC_BUS_END); - -full: - CURVNET_RESTORE(); -} - -static void -pfsync_bulk_status(u_int8_t status) -{ - struct { - struct pfsync_subheader subh; - struct pfsync_bus bus; - } __packed r; - - struct pfsync_softc *sc = V_pfsyncif; - - bzero(&r, sizeof(r)); - - r.subh.action = PFSYNC_ACT_BUS; - r.subh.count = htons(1); - V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_BUS]++; - - r.bus.creatorid = V_pf_status.hostid; - r.bus.endtime = htonl(time_uptime - sc->sc_ureq_received); - r.bus.status = status; - - PFSYNC_LOCK(sc); - pfsync_send_plus(&r, sizeof(r)); - PFSYNC_UNLOCK(sc); -} - -static void -pfsync_bulk_fail(void *arg) -{ - struct pfsync_softc *sc = arg; - - CURVNET_SET(sc->sc_ifp->if_vnet); - - PFSYNC_BLOCK_ASSERT(sc); - - if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) { - /* Try again */ - callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, - pfsync_bulk_fail, V_pfsyncif); - PFSYNC_LOCK(sc); - pfsync_request_update(0, 0); - PFSYNC_UNLOCK(sc); - } else { - /* Pretend like the transfer was ok. */ - sc->sc_ureq_sent = 0; - sc->sc_bulk_tries = 0; - PFSYNC_LOCK(sc); - if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) - (*carp_demote_adj_p)(-V_pfsync_carp_adj, - "pfsync bulk fail"); - sc->sc_flags |= PFSYNCF_OK; - PFSYNC_UNLOCK(sc); - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: failed to receive bulk update\n"); - } - - CURVNET_RESTORE(); -} - -static void -pfsync_send_plus(void *plus, size_t pluslen) -{ - struct pfsync_softc *sc = V_pfsyncif; - - PFSYNC_LOCK_ASSERT(sc); - - if (sc->sc_len + pluslen > sc->sc_ifp->if_mtu) - pfsync_sendout(1); - - sc->sc_plus = plus; - sc->sc_len += (sc->sc_pluslen = pluslen); - - pfsync_sendout(1); -} - -static void -pfsync_timeout(void *arg) -{ - struct pfsync_softc *sc = arg; - - CURVNET_SET(sc->sc_ifp->if_vnet); - PFSYNC_LOCK(sc); - pfsync_push(sc); - PFSYNC_UNLOCK(sc); - CURVNET_RESTORE(); -} - -static void -pfsync_push(struct pfsync_softc *sc) -{ - - PFSYNC_LOCK_ASSERT(sc); - - sc->sc_flags |= PFSYNCF_PUSH; - swi_sched(V_pfsync_swi_cookie, 0); -} - -static void -pfsyncintr(void *arg) -{ - struct pfsync_softc *sc = arg; - struct mbuf *m, *n; - - CURVNET_SET(sc->sc_ifp->if_vnet); - - PFSYNC_LOCK(sc); - if ((sc->sc_flags & PFSYNCF_PUSH) && sc->sc_len > PFSYNC_MINPKT) { - pfsync_sendout(0); - sc->sc_flags &= ~PFSYNCF_PUSH; - } - _IF_DEQUEUE_ALL(&sc->sc_ifp->if_snd, m); - PFSYNC_UNLOCK(sc); - - for (; m != NULL; m = n) { - - n = m->m_nextpkt; - m->m_nextpkt = NULL; - - /* - * We distinguish between a deferral packet and our - * own pfsync packet based on M_SKIP_FIREWALL - * flag. This is XXX. - */ - if (m->m_flags & M_SKIP_FIREWALL) - ip_output(m, NULL, NULL, 0, NULL, NULL); - else if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, - NULL) == 0) - V_pfsyncstats.pfsyncs_opackets++; - else - V_pfsyncstats.pfsyncs_oerrors++; - } - CURVNET_RESTORE(); -} - -static int -pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp, void *mship) -{ - struct ip_moptions *imo = &sc->sc_imo; - int error; - - if (!(ifp->if_flags & IFF_MULTICAST)) - return (EADDRNOTAVAIL); - - imo->imo_membership = (struct in_multi **)mship; - imo->imo_max_memberships = IP_MIN_MEMBERSHIPS; - imo->imo_multicast_vif = -1; - - if ((error = in_joingroup(ifp, &sc->sc_sync_peer, NULL, - &imo->imo_membership[0])) != 0) { - imo->imo_membership = NULL; - return (error); - } - imo->imo_num_memberships++; - imo->imo_multicast_ifp = ifp; - imo->imo_multicast_ttl = PFSYNC_DFLTTL; - imo->imo_multicast_loop = 0; - - return (0); -} - -static void -pfsync_multicast_cleanup(struct pfsync_softc *sc) -{ - struct ip_moptions *imo = &sc->sc_imo; - - in_leavegroup(imo->imo_membership[0], NULL); - free(imo->imo_membership, M_PFSYNC); - imo->imo_membership = NULL; - imo->imo_multicast_ifp = NULL; -} - -#ifdef INET -extern struct domain inetdomain; -static struct protosw in_pfsync_protosw = { - .pr_type = SOCK_RAW, - .pr_domain = &inetdomain, - .pr_protocol = IPPROTO_PFSYNC, - .pr_flags = PR_ATOMIC|PR_ADDR, - .pr_input = pfsync_input, - .pr_output = (pr_output_t *)rip_output, - .pr_ctloutput = rip_ctloutput, - .pr_usrreqs = &rip_usrreqs -}; -#endif - -static int -pfsync_init() -{ - VNET_ITERATOR_DECL(vnet_iter); - int error = 0; - - VNET_LIST_RLOCK(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - V_pfsync_cloner = pfsync_cloner; - V_pfsync_cloner_data = pfsync_cloner_data; - V_pfsync_cloner.ifc_data = &V_pfsync_cloner_data; - if_clone_attach(&V_pfsync_cloner); - error = swi_add(NULL, "pfsync", pfsyncintr, V_pfsyncif, - SWI_NET, INTR_MPSAFE, &V_pfsync_swi_cookie); - CURVNET_RESTORE(); - if (error) - goto fail_locked; - } - VNET_LIST_RUNLOCK(); -#ifdef INET - error = pf_proto_register(PF_INET, &in_pfsync_protosw); - if (error) - goto fail; - error = ipproto_register(IPPROTO_PFSYNC); - if (error) { - pf_proto_unregister(PF_INET, IPPROTO_PFSYNC, SOCK_RAW); - goto fail; - } -#endif - PF_RULES_WLOCK(); - pfsync_state_import_ptr = pfsync_state_import; - pfsync_insert_state_ptr = pfsync_insert_state; - pfsync_update_state_ptr = pfsync_update_state; - pfsync_delete_state_ptr = pfsync_delete_state; - pfsync_clear_states_ptr = pfsync_clear_states; - pfsync_defer_ptr = pfsync_defer; - PF_RULES_WUNLOCK(); - - return (0); - -fail: - VNET_LIST_RLOCK(); -fail_locked: - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - if (V_pfsync_swi_cookie) { - swi_remove(V_pfsync_swi_cookie); - if_clone_detach(&V_pfsync_cloner); - } - CURVNET_RESTORE(); - } - VNET_LIST_RUNLOCK(); - - return (error); -} - -static void -pfsync_uninit() -{ - VNET_ITERATOR_DECL(vnet_iter); - - PF_RULES_WLOCK(); - pfsync_state_import_ptr = NULL; - pfsync_insert_state_ptr = NULL; - pfsync_update_state_ptr = NULL; - pfsync_delete_state_ptr = NULL; - pfsync_clear_states_ptr = NULL; - pfsync_defer_ptr = NULL; - PF_RULES_WUNLOCK(); - - ipproto_unregister(IPPROTO_PFSYNC); - pf_proto_unregister(PF_INET, IPPROTO_PFSYNC, SOCK_RAW); - VNET_LIST_RLOCK(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - if_clone_detach(&V_pfsync_cloner); - swi_remove(V_pfsync_swi_cookie); - CURVNET_RESTORE(); - } - VNET_LIST_RUNLOCK(); -} - -static int -pfsync_modevent(module_t mod, int type, void *data) -{ - int error = 0; - - switch (type) { - case MOD_LOAD: - error = pfsync_init(); - break; - case MOD_QUIESCE: - /* - * Module should not be unloaded due to race conditions. - */ - error = EPERM; - break; - case MOD_UNLOAD: - pfsync_uninit(); - break; - default: - error = EINVAL; - break; - } - - return (error); -} - -static moduledata_t pfsync_mod = { - "pfsync", - pfsync_modevent, - 0 -}; - -#define PFSYNC_MODVER 1 - -DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); -MODULE_VERSION(pfsync, PFSYNC_MODVER); -MODULE_DEPEND(pfsync, pf, PF_MODVER, PF_MODVER, PF_MODVER); diff --git a/sys/contrib/pf/net/if_pfsync.h b/sys/contrib/pf/net/if_pfsync.h deleted file mode 100644 index 5e71dd8e46f5c..0000000000000 --- a/sys/contrib/pf/net/if_pfsync.h +++ /dev/null @@ -1,298 +0,0 @@ -/* $OpenBSD: if_pfsync.h,v 1.35 2008/06/29 08:42:15 mcbride Exp $ */ - -/* - * Copyright (c) 2001 Michael Shalayeff - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES 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 MIND, 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) 2008 David Gwynne - * - * 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. - */ - -#ifndef _NET_IF_PFSYNC_H_ -#define _NET_IF_PFSYNC_H_ - -#define PFSYNC_VERSION 5 -#define PFSYNC_DFLTTL 255 - -#define PFSYNC_ACT_CLR 0 /* clear all states */ -#define PFSYNC_ACT_INS 1 /* insert state */ -#define PFSYNC_ACT_INS_ACK 2 /* ack of insterted state */ -#define PFSYNC_ACT_UPD 3 /* update state */ -#define PFSYNC_ACT_UPD_C 4 /* "compressed" update state */ -#define PFSYNC_ACT_UPD_REQ 5 /* request "uncompressed" state */ -#define PFSYNC_ACT_DEL 6 /* delete state */ -#define PFSYNC_ACT_DEL_C 7 /* "compressed" delete state */ -#define PFSYNC_ACT_INS_F 8 /* insert fragment */ -#define PFSYNC_ACT_DEL_F 9 /* delete fragments */ -#define PFSYNC_ACT_BUS 10 /* bulk update status */ -#define PFSYNC_ACT_TDB 11 /* TDB replay counter update */ -#define PFSYNC_ACT_EOF 12 /* end of frame */ -#define PFSYNC_ACT_MAX 13 - -#define PFSYNC_ACTIONS "CLR ST", \ - "INS ST", \ - "INS ST ACK", \ - "UPD ST", \ - "UPD ST COMP", \ - "UPD ST REQ", \ - "DEL ST", \ - "DEL ST COMP", \ - "INS FR", \ - "DEL FR", \ - "BULK UPD STAT", \ - "TDB UPD", \ - "EOF" - -#define PFSYNC_HMAC_LEN 20 - -/* - * A pfsync frame is built from a header followed by several sections which - * are all prefixed with their own subheaders. Frames must be terminated with - * an EOF subheader. - * - * | ... | - * | IP header | - * +============================+ - * | pfsync_header | - * +----------------------------+ - * | pfsync_subheader | - * +----------------------------+ - * | first action fields | - * | ... | - * +----------------------------+ - * | pfsync_subheader | - * +----------------------------+ - * | second action fields | - * | ... | - * +----------------------------+ - * | EOF pfsync_subheader | - * +----------------------------+ - * | HMAC | - * +============================+ - */ - -/* - * Frame header - */ - -struct pfsync_header { - u_int8_t version; - u_int8_t _pad; - u_int16_t len; - u_int8_t pfcksum[PF_MD5_DIGEST_LENGTH]; -} __packed; - -/* - * Frame region subheader - */ - -struct pfsync_subheader { - u_int8_t action; - u_int8_t _pad; - u_int16_t count; -} __packed; - -/* - * CLR - */ - -struct pfsync_clr { - char ifname[IFNAMSIZ]; - u_int32_t creatorid; -} __packed; - -/* - * INS, UPD, DEL - */ - -/* these use struct pfsync_state in pfvar.h */ - -/* - * INS_ACK - */ - -struct pfsync_ins_ack { - u_int64_t id; - u_int32_t creatorid; -} __packed; - -/* - * UPD_C - */ - -struct pfsync_upd_c { - u_int64_t id; - struct pfsync_state_peer src; - struct pfsync_state_peer dst; - u_int32_t creatorid; - u_int32_t expire; - u_int8_t timeout; - u_int8_t _pad[3]; -} __packed; - -/* - * UPD_REQ - */ - -struct pfsync_upd_req { - u_int64_t id; - u_int32_t creatorid; -} __packed; - -/* - * DEL_C - */ - -struct pfsync_del_c { - u_int64_t id; - u_int32_t creatorid; -} __packed; - -/* - * INS_F, DEL_F - */ - -/* not implemented (yet) */ - -/* - * BUS - */ - -struct pfsync_bus { - u_int32_t creatorid; - u_int32_t endtime; - u_int8_t status; -#define PFSYNC_BUS_START 1 -#define PFSYNC_BUS_END 2 - u_int8_t _pad[3]; -} __packed; - -/* - * TDB - */ - -struct pfsync_tdb { - u_int32_t spi; - union sockaddr_union dst; - u_int32_t rpl; - u_int64_t cur_bytes; - u_int8_t sproto; - u_int8_t updates; - u_int8_t _pad[2]; -} __packed; - -/* - * EOF - */ - -struct pfsync_eof { - u_int8_t hmac[PFSYNC_HMAC_LEN]; -} __packed; - -#define PFSYNC_HDRLEN sizeof(struct pfsync_header) - - - -/* - * Names for PFSYNC sysctl objects - */ -#define PFSYNCCTL_STATS 1 /* PFSYNC stats */ -#define PFSYNCCTL_MAXID 2 - -#define PFSYNCCTL_NAMES { \ - { 0, 0 }, \ - { "stats", CTLTYPE_STRUCT }, \ -} - -struct pfsyncstats { - u_int64_t pfsyncs_ipackets; /* total input packets, IPv4 */ - u_int64_t pfsyncs_ipackets6; /* total input packets, IPv6 */ - u_int64_t pfsyncs_badif; /* not the right interface */ - u_int64_t pfsyncs_badttl; /* TTL is not PFSYNC_DFLTTL */ - u_int64_t pfsyncs_hdrops; /* packets shorter than hdr */ - u_int64_t pfsyncs_badver; /* bad (incl unsupp) version */ - u_int64_t pfsyncs_badact; /* bad action */ - u_int64_t pfsyncs_badlen; /* data length does not match */ - u_int64_t pfsyncs_badauth; /* bad authentication */ - u_int64_t pfsyncs_stale; /* stale state */ - u_int64_t pfsyncs_badval; /* bad values */ - u_int64_t pfsyncs_badstate; /* insert/lookup failed */ - - u_int64_t pfsyncs_opackets; /* total output packets, IPv4 */ - u_int64_t pfsyncs_opackets6; /* total output packets, IPv6 */ - u_int64_t pfsyncs_onomem; /* no memory for an mbuf */ - u_int64_t pfsyncs_oerrors; /* ip output error */ - - u_int64_t pfsyncs_iacts[PFSYNC_ACT_MAX]; - u_int64_t pfsyncs_oacts[PFSYNC_ACT_MAX]; -}; - -/* - * Configuration structure for SIOCSETPFSYNC SIOCGETPFSYNC - */ -struct pfsyncreq { - char pfsyncr_syncdev[IFNAMSIZ]; - struct in_addr pfsyncr_syncpeer; - int pfsyncr_maxupdates; - int pfsyncr_defer; -}; - -#define SIOCSETPFSYNC _IOW('i', 247, struct ifreq) -#define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq) - -#ifdef _KERNEL - -/* - * this shows where a pf state is with respect to the syncing. - */ -#define PFSYNC_S_INS 0x00 -#define PFSYNC_S_IACK 0x01 -#define PFSYNC_S_UPD 0x02 -#define PFSYNC_S_UPD_C 0x03 -#define PFSYNC_S_DEL 0x04 -#define PFSYNC_S_COUNT 0x05 - -#define PFSYNC_S_DEFER 0xfe -#define PFSYNC_S_NONE 0xff - -#define PFSYNC_SI_IOCTL 0x01 -#define PFSYNC_SI_CKSUM 0x02 -#define PFSYNC_SI_ACK 0x04 - -#endif /* _KERNEL */ - -#endif /* _NET_IF_PFSYNC_H_ */ diff --git a/sys/contrib/pf/net/pf.c b/sys/contrib/pf/net/pf.c deleted file mode 100644 index b66d6dd538482..0000000000000 --- a/sys/contrib/pf/net/pf.c +++ /dev/null @@ -1,6270 +0,0 @@ -/* $OpenBSD: pf.c,v 1.634 2009/02/27 12:37:45 henning Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002 - 2008 Henning Brauer - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Effort sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F30602-01-2-0537. - * - */ - -#include - -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_bpf.h" -#include "opt_pf.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* XXX: only for DIR_IN/DIR_OUT */ -#include -#include -#include -#include -#include -#include -#include - -#ifdef INET6 -#include -#include -#include -#include -#include -#endif /* INET6 */ - -#include -#include - -#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x - -/* - * Global variables - */ - -/* state tables */ -VNET_DEFINE(struct pf_altqqueue, pf_altqs[2]); -VNET_DEFINE(struct pf_palist, pf_pabuf); -VNET_DEFINE(struct pf_altqqueue *, pf_altqs_active); -VNET_DEFINE(struct pf_altqqueue *, pf_altqs_inactive); -VNET_DEFINE(struct pf_status, pf_status); - -VNET_DEFINE(u_int32_t, ticket_altqs_active); -VNET_DEFINE(u_int32_t, ticket_altqs_inactive); -VNET_DEFINE(int, altqs_inactive_open); -VNET_DEFINE(u_int32_t, ticket_pabuf); - -VNET_DEFINE(MD5_CTX, pf_tcp_secret_ctx); -#define V_pf_tcp_secret_ctx VNET(pf_tcp_secret_ctx) -VNET_DEFINE(u_char, pf_tcp_secret[16]); -#define V_pf_tcp_secret VNET(pf_tcp_secret) -VNET_DEFINE(int, pf_tcp_secret_init); -#define V_pf_tcp_secret_init VNET(pf_tcp_secret_init) -VNET_DEFINE(int, pf_tcp_iss_off); -#define V_pf_tcp_iss_off VNET(pf_tcp_iss_off) - -struct pf_anchor_stackframe { - struct pf_ruleset *rs; - struct pf_rule *r; - struct pf_anchor_node *parent; - struct pf_anchor *child; -}; -VNET_DEFINE(struct pf_anchor_stackframe, pf_anchor_stack[64]); -#define V_pf_anchor_stack VNET(pf_anchor_stack) - -/* - * Queue for pf_intr() sends. - */ -static MALLOC_DEFINE(M_PFTEMP, "pf_temp", "pf(4) temporary allocations"); -struct pf_send_entry { - STAILQ_ENTRY(pf_send_entry) pfse_next; - struct mbuf *pfse_m; - enum { - PFSE_IP, - PFSE_IP6, - PFSE_ICMP, - PFSE_ICMP6, - } pfse_type; - union { - struct route ro; - struct { - int type; - int code; - int mtu; - } icmpopts; - } u; -#define pfse_ro u.ro -#define pfse_icmp_type u.icmpopts.type -#define pfse_icmp_code u.icmpopts.code -#define pfse_icmp_mtu u.icmpopts.mtu -}; - -STAILQ_HEAD(pf_send_head, pf_send_entry); -static VNET_DEFINE(struct pf_send_head, pf_sendqueue); -#define V_pf_sendqueue VNET(pf_sendqueue) - -static struct mtx pf_sendqueue_mtx; -#define PF_SENDQ_LOCK() mtx_lock(&pf_sendqueue_mtx) -#define PF_SENDQ_UNLOCK() mtx_unlock(&pf_sendqueue_mtx) - -/* - * Queue for pf_flush_task() tasks. - */ -struct pf_flush_entry { - SLIST_ENTRY(pf_flush_entry) next; - struct pf_addr addr; - sa_family_t af; - uint8_t dir; - struct pf_rule *rule; /* never dereferenced */ -}; - -SLIST_HEAD(pf_flush_head, pf_flush_entry); -static VNET_DEFINE(struct pf_flush_head, pf_flushqueue); -#define V_pf_flushqueue VNET(pf_flushqueue) -static VNET_DEFINE(struct task, pf_flushtask); -#define V_pf_flushtask VNET(pf_flushtask) - -static struct mtx pf_flushqueue_mtx; -#define PF_FLUSHQ_LOCK() mtx_lock(&pf_flushqueue_mtx) -#define PF_FLUSHQ_UNLOCK() mtx_unlock(&pf_flushqueue_mtx) - -VNET_DEFINE(struct pf_rulequeue, pf_unlinked_rules); -struct mtx pf_unlnkdrules_mtx; - -static VNET_DEFINE(uma_zone_t, pf_sources_z); -#define V_pf_sources_z VNET(pf_sources_z) -static VNET_DEFINE(uma_zone_t, pf_mtag_z); -#define V_pf_mtag_z VNET(pf_mtag_z) -VNET_DEFINE(uma_zone_t, pf_state_z); -VNET_DEFINE(uma_zone_t, pf_state_key_z); - -VNET_DEFINE(uint64_t, pf_stateid[MAXCPU]); -#define PFID_CPUBITS 8 -#define PFID_CPUSHIFT (sizeof(uint64_t) * NBBY - PFID_CPUBITS) -#define PFID_CPUMASK ((uint64_t)((1 << PFID_CPUBITS) - 1) << PFID_CPUSHIFT) -#define PFID_MAXID (~PFID_CPUMASK) -CTASSERT((1 << PFID_CPUBITS) > MAXCPU); - -static void pf_src_tree_remove_state(struct pf_state *); -static void pf_init_threshold(struct pf_threshold *, u_int32_t, - u_int32_t); -static void pf_add_threshold(struct pf_threshold *); -static int pf_check_threshold(struct pf_threshold *); - -static void pf_change_ap(struct pf_addr *, u_int16_t *, - u_int16_t *, u_int16_t *, struct pf_addr *, - u_int16_t, u_int8_t, sa_family_t); -static int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *, - struct tcphdr *, struct pf_state_peer *); -static void pf_change_icmp(struct pf_addr *, u_int16_t *, - struct pf_addr *, struct pf_addr *, u_int16_t, - u_int16_t *, u_int16_t *, u_int16_t *, - u_int16_t *, u_int8_t, sa_family_t); -static void pf_send_tcp(struct mbuf *, - const struct pf_rule *, sa_family_t, - const struct pf_addr *, const struct pf_addr *, - u_int16_t, u_int16_t, u_int32_t, u_int32_t, - u_int8_t, u_int16_t, u_int16_t, u_int8_t, int, - u_int16_t, struct ifnet *); -static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, - sa_family_t, struct pf_rule *); -static void pf_detach_state(struct pf_state *); -static int pf_state_key_attach(struct pf_state_key *, - struct pf_state_key *, struct pf_state *); -static void pf_state_key_detach(struct pf_state *, int); -static int pf_state_key_ctor(void *, int, void *, int); -static u_int32_t pf_tcp_iss(struct pf_pdesc *); -static int pf_test_rule(struct pf_rule **, struct pf_state **, - int, struct pfi_kif *, struct mbuf *, int, - struct pf_pdesc *, struct pf_rule **, - struct pf_ruleset **, struct inpcb *); -static int pf_create_state(struct pf_rule *, struct pf_rule *, - struct pf_rule *, struct pf_pdesc *, - struct pf_src_node *, struct pf_state_key *, - struct pf_state_key *, struct mbuf *, int, - u_int16_t, u_int16_t, int *, struct pfi_kif *, - struct pf_state **, int, u_int16_t, u_int16_t, - int); -static int pf_test_fragment(struct pf_rule **, int, - struct pfi_kif *, struct mbuf *, void *, - struct pf_pdesc *, struct pf_rule **, - struct pf_ruleset **); -static int pf_tcp_track_full(struct pf_state_peer *, - struct pf_state_peer *, struct pf_state **, - struct pfi_kif *, struct mbuf *, int, - struct pf_pdesc *, u_short *, int *); -static int pf_tcp_track_sloppy(struct pf_state_peer *, - struct pf_state_peer *, struct pf_state **, - struct pf_pdesc *, u_short *); -static int pf_test_state_tcp(struct pf_state **, int, - struct pfi_kif *, struct mbuf *, int, - void *, struct pf_pdesc *, u_short *); -static int pf_test_state_udp(struct pf_state **, int, - struct pfi_kif *, struct mbuf *, int, - void *, struct pf_pdesc *); -static int pf_test_state_icmp(struct pf_state **, int, - struct pfi_kif *, struct mbuf *, int, - void *, struct pf_pdesc *, u_short *); -static int pf_test_state_other(struct pf_state **, int, - struct pfi_kif *, struct mbuf *, struct pf_pdesc *); -static u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, - sa_family_t); -static u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, - sa_family_t); -static u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, - int, u_int16_t); -static void pf_set_rt_ifp(struct pf_state *, - struct pf_addr *); -static int pf_check_proto_cksum(struct mbuf *, int, int, - u_int8_t, sa_family_t); -static void pf_print_state_parts(struct pf_state *, - struct pf_state_key *, struct pf_state_key *); -static int pf_addr_wrap_neq(struct pf_addr_wrap *, - struct pf_addr_wrap *); -static struct pf_state *pf_find_state(struct pfi_kif *, - struct pf_state_key_cmp *, u_int); -static int pf_src_connlimit(struct pf_state **); -static void pf_flush_task(void *c, int pending); -static int pf_insert_src_node(struct pf_src_node **, - struct pf_rule *, struct pf_addr *, sa_family_t); -static int pf_purge_expired_states(int); -static void pf_purge_unlinked_rules(void); -static int pf_mtag_init(void *, int, int); -static void pf_mtag_free(struct m_tag *); -#ifdef INET -static void pf_route(struct mbuf **, struct pf_rule *, int, - struct ifnet *, struct pf_state *, - struct pf_pdesc *); -#endif /* INET */ -#ifdef INET6 -static void pf_change_a6(struct pf_addr *, u_int16_t *, - struct pf_addr *, u_int8_t); -static void pf_route6(struct mbuf **, struct pf_rule *, int, - struct ifnet *, struct pf_state *, - struct pf_pdesc *); -#endif /* INET6 */ - -int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); - -VNET_DECLARE(int, pf_end_threads); - -VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); - -#define PACKET_LOOPED(pd) ((pd)->pf_mtag && \ - (pd)->pf_mtag->flags & PF_PACKET_LOOPED) - -#define STATE_LOOKUP(i, k, d, s, pd) \ - do { \ - (s) = pf_find_state((i), (k), (d)); \ - if ((s) == NULL || (s)->timeout == PFTM_PURGE) \ - return (PF_DROP); \ - if (PACKET_LOOPED(pd)) \ - return (PF_PASS); \ - if ((d) == PF_OUT && \ - (((s)->rule.ptr->rt == PF_ROUTETO && \ - (s)->rule.ptr->direction == PF_OUT) || \ - ((s)->rule.ptr->rt == PF_REPLYTO && \ - (s)->rule.ptr->direction == PF_IN)) && \ - (s)->rt_kif != NULL && \ - (s)->rt_kif != (i)) \ - return (PF_PASS); \ - } while (0) - -#define BOUND_IFACE(r, k) \ - ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all - -#define STATE_INC_COUNTERS(s) \ - do { \ - s->rule.ptr->states_cur++; \ - s->rule.ptr->states_tot++; \ - if (s->anchor.ptr != NULL) { \ - s->anchor.ptr->states_cur++; \ - s->anchor.ptr->states_tot++; \ - } \ - if (s->nat_rule.ptr != NULL) { \ - s->nat_rule.ptr->states_cur++; \ - s->nat_rule.ptr->states_tot++; \ - } \ - } while (0) - -#define STATE_DEC_COUNTERS(s) \ - do { \ - if (s->nat_rule.ptr != NULL) \ - s->nat_rule.ptr->states_cur--; \ - if (s->anchor.ptr != NULL) \ - s->anchor.ptr->states_cur--; \ - s->rule.ptr->states_cur--; \ - } while (0) - -static MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures"); -VNET_DEFINE(struct pf_keyhash *, pf_keyhash); -VNET_DEFINE(struct pf_idhash *, pf_idhash); -VNET_DEFINE(u_long, pf_hashmask); -VNET_DEFINE(struct pf_srchash *, pf_srchash); -VNET_DEFINE(u_long, pf_srchashmask); - -SYSCTL_NODE(_net, OID_AUTO, pf, CTLFLAG_RW, 0, "pf(4)"); - -VNET_DEFINE(u_long, pf_hashsize); -#define V_pf_hashsize VNET(pf_hashsize) -SYSCTL_VNET_UINT(_net_pf, OID_AUTO, states_hashsize, CTLFLAG_RDTUN, - &VNET_NAME(pf_hashsize), 0, "Size of pf(4) states hashtable"); - -VNET_DEFINE(u_long, pf_srchashsize); -#define V_pf_srchashsize VNET(pf_srchashsize) -SYSCTL_VNET_UINT(_net_pf, OID_AUTO, source_nodes_hashsize, CTLFLAG_RDTUN, - &VNET_NAME(pf_srchashsize), 0, "Size of pf(4) source nodes hashtable"); - -VNET_DEFINE(void *, pf_swi_cookie); - -VNET_DEFINE(uint32_t, pf_hashseed); -#define V_pf_hashseed VNET(pf_hashseed) - -static __inline uint32_t -pf_hashkey(struct pf_state_key *sk) -{ - uint32_t h; - - h = jenkins_hash32((uint32_t *)sk, - sizeof(struct pf_state_key_cmp)/sizeof(uint32_t), - V_pf_hashseed); - - return (h & V_pf_hashmask); -} - -#ifdef INET6 -void -pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: - dst->addr32[0] = src->addr32[0]; - break; -#endif /* INET */ - case AF_INET6: - dst->addr32[0] = src->addr32[0]; - dst->addr32[1] = src->addr32[1]; - dst->addr32[2] = src->addr32[2]; - dst->addr32[3] = src->addr32[3]; - break; - } -} -#endif /* INET6 */ - -static void -pf_init_threshold(struct pf_threshold *threshold, - u_int32_t limit, u_int32_t seconds) -{ - threshold->limit = limit * PF_THRESHOLD_MULT; - threshold->seconds = seconds; - threshold->count = 0; - threshold->last = time_uptime; -} - -static void -pf_add_threshold(struct pf_threshold *threshold) -{ - u_int32_t t = time_uptime, diff = t - threshold->last; - - if (diff >= threshold->seconds) - threshold->count = 0; - else - threshold->count -= threshold->count * diff / - threshold->seconds; - threshold->count += PF_THRESHOLD_MULT; - threshold->last = t; -} - -static int -pf_check_threshold(struct pf_threshold *threshold) -{ - return (threshold->count > threshold->limit); -} - -static int -pf_src_connlimit(struct pf_state **state) -{ - struct pfr_addr p; - struct pf_flush_entry *pffe; - int bad = 0; - - PF_STATE_LOCK_ASSERT(*state); - - (*state)->src_node->conn++; - (*state)->src.tcp_est = 1; - pf_add_threshold(&(*state)->src_node->conn_rate); - - if ((*state)->rule.ptr->max_src_conn && - (*state)->rule.ptr->max_src_conn < - (*state)->src_node->conn) { - V_pf_status.lcounters[LCNT_SRCCONN]++; - bad++; - } - - if ((*state)->rule.ptr->max_src_conn_rate.limit && - pf_check_threshold(&(*state)->src_node->conn_rate)) { - V_pf_status.lcounters[LCNT_SRCCONNRATE]++; - bad++; - } - - if (!bad) - return (0); - - /* Kill this state. */ - (*state)->timeout = PFTM_PURGE; - (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; - - if ((*state)->rule.ptr->overload_tbl == NULL) - return (1); - - V_pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("%s: blocking address ", __func__); - pf_print_host(&(*state)->src_node->addr, 0, - (*state)->key[PF_SK_WIRE]->af); - printf("\n"); - } - - bzero(&p, sizeof(p)); - p.pfra_af = (*state)->key[PF_SK_WIRE]->af; - switch ((*state)->key[PF_SK_WIRE]->af) { -#ifdef INET - case AF_INET: - p.pfra_net = 32; - p.pfra_ip4addr = (*state)->src_node->addr.v4; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - p.pfra_net = 128; - p.pfra_ip6addr = (*state)->src_node->addr.v6; - break; -#endif /* INET6 */ - } - - pfr_insert_kentry((*state)->rule.ptr->overload_tbl, &p, time_second); - - if ((*state)->rule.ptr->flush == 0) - return (1); - - /* Schedule flushing task. */ - pffe = malloc(sizeof(*pffe), M_PFTEMP, M_NOWAIT); - if (pffe == NULL) - return (1); /* too bad :( */ - - bcopy(&(*state)->src_node->addr, &pffe->addr, sizeof(pffe->addr)); - pffe->af = (*state)->key[PF_SK_WIRE]->af; - pffe->dir = (*state)->direction; - if ((*state)->rule.ptr->flush & PF_FLUSH_GLOBAL) - pffe->rule = NULL; - else - pffe->rule = (*state)->rule.ptr; - PF_FLUSHQ_LOCK(); - SLIST_INSERT_HEAD(&V_pf_flushqueue, pffe, next); - PF_FLUSHQ_UNLOCK(); - taskqueue_enqueue(taskqueue_swi, &V_pf_flushtask); - - return (1); -} - -static void -pf_flush_task(void *c, int pending) -{ - struct pf_flush_head queue; - struct pf_flush_entry *pffe, *pffe1; - uint32_t killed = 0; - - PF_FLUSHQ_LOCK(); - queue = *(struct pf_flush_head *)c; - SLIST_INIT((struct pf_flush_head *)c); - PF_FLUSHQ_UNLOCK(); - - V_pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; - - for (int i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - struct pf_state_key *sk; - struct pf_state *s; - - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - sk = s->key[PF_SK_WIRE]; - SLIST_FOREACH(pffe, &queue, next) - if (sk->af == pffe->af && (pffe->rule == NULL || - pffe->rule == s->rule.ptr) && - ((pffe->dir == PF_OUT && - PF_AEQ(&pffe->addr, &sk->addr[1], sk->af)) || - (pffe->dir == PF_IN && - PF_AEQ(&pffe->addr, &sk->addr[0], sk->af)))) { - s->timeout = PFTM_PURGE; - s->src.state = s->dst.state = TCPS_CLOSED; - killed++; - } - } - PF_HASHROW_UNLOCK(ih); - } - SLIST_FOREACH_SAFE(pffe, &queue, next, pffe1) - free(pffe, M_PFTEMP); - if (V_pf_status.debug >= PF_DEBUG_MISC) - printf("%s: %u states killed", __func__, killed); -} - -/* - * Can return locked on failure, so that we can consistently - * allocate and insert a new one. - */ -struct pf_src_node * -pf_find_src_node(struct pf_addr *src, struct pf_rule *rule, sa_family_t af, - int returnlocked) -{ - struct pf_srchash *sh; - struct pf_src_node *n; - - V_pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; - - sh = &V_pf_srchash[pf_hashsrc(src, af)]; - PF_HASHROW_LOCK(sh); - LIST_FOREACH(n, &sh->nodes, entry) - if (n->rule.ptr == rule && n->af == af && - ((af == AF_INET && n->addr.v4.s_addr == src->v4.s_addr) || - (af == AF_INET6 && bcmp(&n->addr, src, sizeof(*src)) == 0))) - break; - if (n != NULL || returnlocked == 0) - PF_HASHROW_UNLOCK(sh); - - return (n); -} - -static int -pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, - struct pf_addr *src, sa_family_t af) -{ - - KASSERT((rule->rule_flag & PFRULE_RULESRCTRACK || - rule->rpool.opts & PF_POOL_STICKYADDR), - ("%s for non-tracking rule %p", __func__, rule)); - - if (*sn == NULL) - *sn = pf_find_src_node(src, rule, af, 1); - - if (*sn == NULL) { - struct pf_srchash *sh = &V_pf_srchash[pf_hashsrc(src, af)]; - - PF_HASHROW_ASSERT(sh); - - if (!rule->max_src_nodes || - rule->src_nodes < rule->max_src_nodes) - (*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO); - else - V_pf_status.lcounters[LCNT_SRCNODES]++; - if ((*sn) == NULL) { - PF_HASHROW_UNLOCK(sh); - return (-1); - } - - pf_init_threshold(&(*sn)->conn_rate, - rule->max_src_conn_rate.limit, - rule->max_src_conn_rate.seconds); - - (*sn)->af = af; - (*sn)->rule.ptr = rule; - PF_ACPY(&(*sn)->addr, src, af); - LIST_INSERT_HEAD(&sh->nodes, *sn, entry); - (*sn)->creation = time_uptime; - (*sn)->ruletype = rule->action; - if ((*sn)->rule.ptr != NULL) - (*sn)->rule.ptr->src_nodes++; - PF_HASHROW_UNLOCK(sh); - V_pf_status.scounters[SCNT_SRC_NODE_INSERT]++; - V_pf_status.src_nodes++; - } else { - if (rule->max_src_states && - (*sn)->states >= rule->max_src_states) { - V_pf_status.lcounters[LCNT_SRCSTATES]++; - return (-1); - } - } - return (0); -} - -static void -pf_remove_src_node(struct pf_src_node *src) -{ - struct pf_srchash *sh; - - sh = &V_pf_srchash[pf_hashsrc(&src->addr, src->af)]; - PF_HASHROW_LOCK(sh); - LIST_REMOVE(src, entry); - PF_HASHROW_UNLOCK(sh); -} - -/* Data storage structures initialization. */ -void -pf_initialize() -{ - struct pf_keyhash *kh; - struct pf_idhash *ih; - struct pf_srchash *sh; - u_int i; - - TUNABLE_ULONG_FETCH("net.pf.states_hashsize", &V_pf_hashsize); - if (V_pf_hashsize == 0 || !powerof2(V_pf_hashsize)) - V_pf_hashsize = PF_HASHSIZ; - TUNABLE_ULONG_FETCH("net.pf.source_nodes_hashsize", &V_pf_srchashsize); - if (V_pf_srchashsize == 0 || !powerof2(V_pf_srchashsize)) - V_pf_srchashsize = PF_HASHSIZ / 4; - - V_pf_hashseed = arc4random(); - - /* States and state keys storage. */ - V_pf_state_z = uma_zcreate("pf states", sizeof(struct pf_state), - NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); - V_pf_limits[PF_LIMIT_STATES].zone = V_pf_state_z; - uma_zone_set_max(V_pf_state_z, PFSTATE_HIWAT); - - V_pf_state_key_z = uma_zcreate("pf state keys", - sizeof(struct pf_state_key), pf_state_key_ctor, NULL, NULL, NULL, - UMA_ALIGN_PTR, 0); - V_pf_keyhash = malloc(V_pf_hashsize * sizeof(struct pf_keyhash), - M_PFHASH, M_WAITOK | M_ZERO); - V_pf_idhash = malloc(V_pf_hashsize * sizeof(struct pf_idhash), - M_PFHASH, M_WAITOK | M_ZERO); - V_pf_hashmask = V_pf_hashsize - 1; - for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask; - i++, kh++, ih++) { - mtx_init(&kh->lock, "pf_keyhash", NULL, MTX_DEF); - mtx_init(&ih->lock, "pf_idhash", NULL, MTX_DEF); - } - - /* Source nodes. */ - V_pf_sources_z = uma_zcreate("pf source nodes", - sizeof(struct pf_src_node), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, - 0); - V_pf_limits[PF_LIMIT_SRC_NODES].zone = V_pf_sources_z; - uma_zone_set_max(V_pf_sources_z, PFSNODE_HIWAT); - V_pf_srchash = malloc(V_pf_srchashsize * sizeof(struct pf_srchash), - M_PFHASH, M_WAITOK|M_ZERO); - V_pf_srchashmask = V_pf_srchashsize - 1; - for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) - mtx_init(&sh->lock, "pf_srchash", NULL, MTX_DEF); - - /* ALTQ */ - TAILQ_INIT(&V_pf_altqs[0]); - TAILQ_INIT(&V_pf_altqs[1]); - TAILQ_INIT(&V_pf_pabuf); - V_pf_altqs_active = &V_pf_altqs[0]; - V_pf_altqs_inactive = &V_pf_altqs[1]; - - /* Mbuf tags */ - V_pf_mtag_z = uma_zcreate("pf mtags", sizeof(struct m_tag) + - sizeof(struct pf_mtag), NULL, NULL, pf_mtag_init, NULL, - UMA_ALIGN_PTR, 0); - - /* Send & flush queues. */ - STAILQ_INIT(&V_pf_sendqueue); - SLIST_INIT(&V_pf_flushqueue); - TASK_INIT(&V_pf_flushtask, 0, pf_flush_task, &V_pf_flushqueue); - mtx_init(&pf_sendqueue_mtx, "pf send queue", NULL, MTX_DEF); - mtx_init(&pf_flushqueue_mtx, "pf flush queue", NULL, MTX_DEF); - - /* Unlinked, but may be referenced rules. */ - TAILQ_INIT(&V_pf_unlinked_rules); - mtx_init(&pf_unlnkdrules_mtx, "pf unlinked rules", NULL, MTX_DEF); -} - -void -pf_cleanup() -{ - struct pf_keyhash *kh; - struct pf_idhash *ih; - struct pf_srchash *sh; - struct pf_send_entry *pfse, *next; - u_int i; - - for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask; - i++, kh++, ih++) { - KASSERT(LIST_EMPTY(&kh->keys), ("%s: key hash not empty", - __func__)); - KASSERT(LIST_EMPTY(&ih->states), ("%s: id hash not empty", - __func__)); - mtx_destroy(&kh->lock); - mtx_destroy(&ih->lock); - } - free(V_pf_keyhash, M_PFHASH); - free(V_pf_idhash, M_PFHASH); - - for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) { - KASSERT(LIST_EMPTY(&sh->nodes), - ("%s: source node hash not empty", __func__)); - mtx_destroy(&sh->lock); - } - free(V_pf_srchash, M_PFHASH); - - STAILQ_FOREACH_SAFE(pfse, &V_pf_sendqueue, pfse_next, next) { - m_freem(pfse->pfse_m); - free(pfse, M_PFTEMP); - } - - mtx_destroy(&pf_sendqueue_mtx); - mtx_destroy(&pf_flushqueue_mtx); - mtx_destroy(&pf_unlnkdrules_mtx); - - uma_zdestroy(V_pf_mtag_z); - uma_zdestroy(V_pf_sources_z); - uma_zdestroy(V_pf_state_z); - uma_zdestroy(V_pf_state_key_z); -} - -static int -pf_mtag_init(void *mem, int size, int how) -{ - struct m_tag *t; - - t = (struct m_tag *)mem; - t->m_tag_cookie = MTAG_ABI_COMPAT; - t->m_tag_id = PACKET_TAG_PF; - t->m_tag_len = sizeof(struct pf_mtag); - t->m_tag_free = pf_mtag_free; - - return (0); -} - -static void -pf_mtag_free(struct m_tag *t) -{ - - uma_zfree(V_pf_mtag_z, t); -} - -struct pf_mtag * -pf_get_mtag(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) != NULL) - return ((struct pf_mtag *)(mtag + 1)); - - mtag = uma_zalloc(V_pf_mtag_z, M_NOWAIT); - if (mtag == NULL) - return (NULL); - bzero(mtag + 1, sizeof(struct pf_mtag)); - m_tag_prepend(m, mtag); - - return ((struct pf_mtag *)(mtag + 1)); -} - -static int -pf_state_key_attach(struct pf_state_key *skw, struct pf_state_key *sks, - struct pf_state *s) -{ - struct pf_keyhash *kh; - struct pf_state_key *sk, *cur; - struct pf_state *si, *olds = NULL; - int idx; - - KASSERT(s->refs == 0, ("%s: state not pristine", __func__)); - KASSERT(s->key[PF_SK_WIRE] == NULL, ("%s: state has key", __func__)); - KASSERT(s->key[PF_SK_STACK] == NULL, ("%s: state has key", __func__)); - - /* - * First run: start with wire key. - */ - sk = skw; - idx = PF_SK_WIRE; - -keyattach: - kh = &V_pf_keyhash[pf_hashkey(sk)]; - - PF_HASHROW_LOCK(kh); - LIST_FOREACH(cur, &kh->keys, entry) - if (bcmp(cur, sk, sizeof(struct pf_state_key_cmp)) == 0) - break; - - if (cur != NULL) { - /* Key exists. Check for same kif, if none, add to key. */ - TAILQ_FOREACH(si, &cur->states[idx], key_list[idx]) { - struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(si)]; - - PF_HASHROW_LOCK(ih); - if (si->kif == s->kif && - si->direction == s->direction) { - if (sk->proto == IPPROTO_TCP && - si->src.state >= TCPS_FIN_WAIT_2 && - si->dst.state >= TCPS_FIN_WAIT_2) { - si->src.state = si->dst.state = - TCPS_CLOSED; - /* Unlink later or cur can go away. */ - pf_ref_state(si); - olds = si; - } else { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: %s key attach " - "failed on %s: ", - (idx == PF_SK_WIRE) ? - "wire" : "stack", - s->kif->pfik_name); - pf_print_state_parts(s, - (idx == PF_SK_WIRE) ? - sk : NULL, - (idx == PF_SK_STACK) ? - sk : NULL); - printf(", existing: "); - pf_print_state_parts(si, - (idx == PF_SK_WIRE) ? - sk : NULL, - (idx == PF_SK_STACK) ? - sk : NULL); - printf("\n"); - } - PF_HASHROW_UNLOCK(ih); - PF_HASHROW_UNLOCK(kh); - uma_zfree(V_pf_state_key_z, sk); - if (idx == PF_SK_STACK) - pf_detach_state(s); - return (-1); /* collision! */ - } - } - PF_HASHROW_UNLOCK(ih); - } - uma_zfree(V_pf_state_key_z, sk); - s->key[idx] = cur; - } else { - LIST_INSERT_HEAD(&kh->keys, sk, entry); - s->key[idx] = sk; - } - -stateattach: - /* List is sorted, if-bound states before floating. */ - if (s->kif == V_pfi_all) - TAILQ_INSERT_TAIL(&s->key[idx]->states[idx], s, key_list[idx]); - else - TAILQ_INSERT_HEAD(&s->key[idx]->states[idx], s, key_list[idx]); - - /* - * Attach done. See how should we (or should not?) - * attach a second key. - */ - if (sks == skw) { - s->key[PF_SK_STACK] = s->key[PF_SK_WIRE]; - idx = PF_SK_STACK; - sks = NULL; - goto stateattach; - } else if (sks != NULL) { - PF_HASHROW_UNLOCK(kh); - if (olds) { - pf_unlink_state(olds, 0); - pf_release_state(olds); - olds = NULL; - } - /* - * Continue attaching with stack key. - */ - sk = sks; - idx = PF_SK_STACK; - sks = NULL; - goto keyattach; - } else - PF_HASHROW_UNLOCK(kh); - - if (olds) { - pf_unlink_state(olds, 0); - pf_release_state(olds); - } - - KASSERT(s->key[PF_SK_WIRE] != NULL && s->key[PF_SK_STACK] != NULL, - ("%s failure", __func__)); - - return (0); -} - -static void -pf_detach_state(struct pf_state *s) -{ - struct pf_state_key *sks = s->key[PF_SK_STACK]; - struct pf_keyhash *kh; - - if (sks != NULL) { - kh = &V_pf_keyhash[pf_hashkey(sks)]; - PF_HASHROW_LOCK(kh); - if (s->key[PF_SK_STACK] != NULL) - pf_state_key_detach(s, PF_SK_STACK); - /* - * If both point to same key, then we are done. - */ - if (sks == s->key[PF_SK_WIRE]) { - pf_state_key_detach(s, PF_SK_WIRE); - PF_HASHROW_UNLOCK(kh); - return; - } - PF_HASHROW_UNLOCK(kh); - } - - if (s->key[PF_SK_WIRE] != NULL) { - kh = &V_pf_keyhash[pf_hashkey(s->key[PF_SK_WIRE])]; - PF_HASHROW_LOCK(kh); - if (s->key[PF_SK_WIRE] != NULL) - pf_state_key_detach(s, PF_SK_WIRE); - PF_HASHROW_UNLOCK(kh); - } -} - -static void -pf_state_key_detach(struct pf_state *s, int idx) -{ - struct pf_state_key *sk = s->key[idx]; -#ifdef INVARIANTS - struct pf_keyhash *kh = &V_pf_keyhash[pf_hashkey(sk)]; - - PF_HASHROW_ASSERT(kh); -#endif - TAILQ_REMOVE(&sk->states[idx], s, key_list[idx]); - s->key[idx] = NULL; - - if (TAILQ_EMPTY(&sk->states[0]) && TAILQ_EMPTY(&sk->states[1])) { - LIST_REMOVE(sk, entry); - uma_zfree(V_pf_state_key_z, sk); - } -} - -static int -pf_state_key_ctor(void *mem, int size, void *arg, int flags) -{ - struct pf_state_key *sk = mem; - - bzero(sk, sizeof(struct pf_state_key_cmp)); - TAILQ_INIT(&sk->states[PF_SK_WIRE]); - TAILQ_INIT(&sk->states[PF_SK_STACK]); - - return (0); -} - -struct pf_state_key * -pf_state_key_setup(struct pf_pdesc *pd, struct pf_addr *saddr, - struct pf_addr *daddr, u_int16_t sport, u_int16_t dport) -{ - struct pf_state_key *sk; - - sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); - if (sk == NULL) - return (NULL); - - PF_ACPY(&sk->addr[pd->sidx], saddr, pd->af); - PF_ACPY(&sk->addr[pd->didx], daddr, pd->af); - sk->port[pd->sidx] = sport; - sk->port[pd->didx] = dport; - sk->proto = pd->proto; - sk->af = pd->af; - - return (sk); -} - -struct pf_state_key * -pf_state_key_clone(struct pf_state_key *orig) -{ - struct pf_state_key *sk; - - sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); - if (sk == NULL) - return (NULL); - - bcopy(orig, sk, sizeof(struct pf_state_key_cmp)); - - return (sk); -} - -int -pf_state_insert(struct pfi_kif *kif, struct pf_state_key *skw, - struct pf_state_key *sks, struct pf_state *s) -{ - struct pf_idhash *ih; - struct pf_state *cur; - - KASSERT(TAILQ_EMPTY(&sks->states[0]) && TAILQ_EMPTY(&sks->states[1]), - ("%s: sks not pristine", __func__)); - KASSERT(TAILQ_EMPTY(&skw->states[0]) && TAILQ_EMPTY(&skw->states[1]), - ("%s: skw not pristine", __func__)); - KASSERT(s->refs == 0, ("%s: state not pristine", __func__)); - - s->kif = kif; - - if (pf_state_key_attach(skw, sks, s)) - return (-1); - - if (s->id == 0 && s->creatorid == 0) { - /* XXX: should be atomic, but probability of collision low */ - if ((s->id = V_pf_stateid[curcpu]++) == PFID_MAXID) - V_pf_stateid[curcpu] = 1; - s->id |= (uint64_t )curcpu << PFID_CPUSHIFT; - s->id = htobe64(s->id); - s->creatorid = V_pf_status.hostid; - } - - ih = &V_pf_idhash[PF_IDHASH(s)]; - PF_HASHROW_LOCK(ih); - LIST_FOREACH(cur, &ih->states, entry) - if (cur->id == s->id && cur->creatorid == s->creatorid) - break; - - if (cur != NULL) { - PF_HASHROW_UNLOCK(ih); - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: state insert failed: " - "id: %016llx creatorid: %08x", - (unsigned long long)be64toh(s->id), - ntohl(s->creatorid)); - printf("\n"); - } - pf_detach_state(s); - return (-1); - } - LIST_INSERT_HEAD(&ih->states, s, entry); - /* One for keys, one for ID hash. */ - refcount_init(&s->refs, 2); - - V_pf_status.fcounters[FCNT_STATE_INSERT]++; - if (pfsync_insert_state_ptr != NULL) - pfsync_insert_state_ptr(s); - - /* Returns locked. */ - return (0); -} - -/* - * Find state by ID: returns with locked row on success. - */ -struct pf_state * -pf_find_state_byid(uint64_t id, uint32_t creatorid) -{ - struct pf_idhash *ih; - struct pf_state *s; - - V_pf_status.fcounters[FCNT_STATE_SEARCH]++; - - ih = &V_pf_idhash[(be64toh(id) % (V_pf_hashmask + 1))]; - - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) - if (s->id == id && s->creatorid == creatorid) - break; - - if (s == NULL) - PF_HASHROW_UNLOCK(ih); - - return (s); -} - -/* - * Find state by key. - * Returns with ID hash slot locked on success. - */ -static struct pf_state * -pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir) -{ - struct pf_keyhash *kh; - struct pf_state_key *sk; - struct pf_state *s; - int idx; - - V_pf_status.fcounters[FCNT_STATE_SEARCH]++; - - kh = &V_pf_keyhash[pf_hashkey((struct pf_state_key *)key)]; - - PF_HASHROW_LOCK(kh); - LIST_FOREACH(sk, &kh->keys, entry) - if (bcmp(sk, key, sizeof(struct pf_state_key_cmp)) == 0) - break; - if (sk == NULL) { - PF_HASHROW_UNLOCK(kh); - return (NULL); - } - - idx = (dir == PF_IN ? PF_SK_WIRE : PF_SK_STACK); - - /* List is sorted, if-bound states before floating ones. */ - TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) - if (s->kif == V_pfi_all || s->kif == kif) { - PF_STATE_LOCK(s); - PF_HASHROW_UNLOCK(kh); - if (s->timeout == PFTM_UNLINKED) { - /* - * State is being processed - * by pf_unlink_state() in - * an other thread. - */ - PF_STATE_UNLOCK(s); - return (NULL); - } - return (s); - } - PF_HASHROW_UNLOCK(kh); - - return (NULL); -} - -struct pf_state * -pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more) -{ - struct pf_keyhash *kh; - struct pf_state_key *sk; - struct pf_state *s, *ret = NULL; - int idx, inout = 0; - - V_pf_status.fcounters[FCNT_STATE_SEARCH]++; - - kh = &V_pf_keyhash[pf_hashkey((struct pf_state_key *)key)]; - - PF_HASHROW_LOCK(kh); - LIST_FOREACH(sk, &kh->keys, entry) - if (bcmp(sk, key, sizeof(struct pf_state_key_cmp)) == 0) - break; - if (sk == NULL) { - PF_HASHROW_UNLOCK(kh); - return (NULL); - } - switch (dir) { - case PF_IN: - idx = PF_SK_WIRE; - break; - case PF_OUT: - idx = PF_SK_STACK; - break; - case PF_INOUT: - idx = PF_SK_WIRE; - inout = 1; - break; - default: - panic("%s: dir %u", __func__, dir); - } -second_run: - TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) { - if (more == NULL) { - PF_HASHROW_UNLOCK(kh); - return (s); - } - - if (ret) - (*more)++; - else - ret = s; - } - if (inout == 1) { - inout = 0; - idx = PF_SK_STACK; - goto second_run; - } - PF_HASHROW_UNLOCK(kh); - - return (ret); -} - -/* END state table stuff */ - -static void -pf_send(struct pf_send_entry *pfse) -{ - - PF_SENDQ_LOCK(); - STAILQ_INSERT_TAIL(&V_pf_sendqueue, pfse, pfse_next); - PF_SENDQ_UNLOCK(); - swi_sched(V_pf_swi_cookie, 0); -} - -void -pf_intr(void *v) -{ - struct pf_send_head queue; - struct pf_send_entry *pfse, *next; - - CURVNET_SET((struct vnet *)v); - - PF_SENDQ_LOCK(); - queue = V_pf_sendqueue; - STAILQ_INIT(&V_pf_sendqueue); - PF_SENDQ_UNLOCK(); - - STAILQ_FOREACH_SAFE(pfse, &queue, pfse_next, next) { - switch (pfse->pfse_type) { -#ifdef INET - case PFSE_IP: - ip_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL); - break; - case PFSE_ICMP: - icmp_error(pfse->pfse_m, pfse->pfse_icmp_type, - pfse->pfse_icmp_code, 0, pfse->pfse_icmp_mtu); - break; -#endif /* INET */ -#ifdef INET6 - case PFSE_IP6: - ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL, - NULL); - break; - case PFSE_ICMP6: - icmp6_error(pfse->pfse_m, pfse->pfse_icmp_type, - pfse->pfse_icmp_code, pfse->pfse_icmp_mtu); - break; -#endif /* INET6 */ - default: - panic("%s: unknown type", __func__); - } - free(pfse, M_PFTEMP); - } - CURVNET_RESTORE(); -} - -void -pf_purge_thread(void *v) -{ - int fullrun; - - CURVNET_SET((struct vnet *)v); - - for (;;) { - PF_RULES_RLOCK(); - rw_sleep(pf_purge_thread, &pf_rules_lock, 0, "pftm", hz / 10); - - if (V_pf_end_threads) { - /* - * To cleanse up all kifs and rules we need - * two runs: first one clears reference flags, - * then pf_purge_expired_states() doesn't - * raise them, and then second run frees. - */ - PF_RULES_RUNLOCK(); - pf_purge_unlinked_rules(); - pfi_kif_purge(); - - /* - * Now purge everything. - */ - pf_purge_expired_states(V_pf_hashmask + 1); - pf_purge_expired_fragments(); - pf_purge_expired_src_nodes(); - - /* - * Now all kifs & rules should be unreferenced, - * thus should be successfully freed. - */ - pf_purge_unlinked_rules(); - pfi_kif_purge(); - - /* - * Announce success and exit. - */ - PF_RULES_RLOCK(); - V_pf_end_threads++; - PF_RULES_RUNLOCK(); - wakeup(pf_purge_thread); - kproc_exit(0); - } - PF_RULES_RUNLOCK(); - - /* Process 1/interval fraction of the state table every run. */ - fullrun = pf_purge_expired_states(V_pf_hashmask / - (V_pf_default_rule.timeout[PFTM_INTERVAL] * 10)); - - /* Purge other expired types every PFTM_INTERVAL seconds. */ - if (fullrun) { - /* - * Order is important: - * - states and src nodes reference rules - * - states and rules reference kifs - */ - pf_purge_expired_fragments(); - pf_purge_expired_src_nodes(); - pf_purge_unlinked_rules(); - pfi_kif_purge(); - } - } - /* not reached */ - CURVNET_RESTORE(); -} - -u_int32_t -pf_state_expires(const struct pf_state *state) -{ - u_int32_t timeout; - u_int32_t start; - u_int32_t end; - u_int32_t states; - - /* handle all PFTM_* > PFTM_MAX here */ - if (state->timeout == PFTM_PURGE) - return (time_uptime); - if (state->timeout == PFTM_UNTIL_PACKET) - return (0); - KASSERT(state->timeout != PFTM_UNLINKED, - ("pf_state_expires: timeout == PFTM_UNLINKED")); - KASSERT((state->timeout < PFTM_MAX), - ("pf_state_expires: timeout > PFTM_MAX")); - timeout = state->rule.ptr->timeout[state->timeout]; - if (!timeout) - timeout = V_pf_default_rule.timeout[state->timeout]; - start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; - if (start) { - end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; - states = state->rule.ptr->states_cur; /* XXXGL */ - } else { - start = V_pf_default_rule.timeout[PFTM_ADAPTIVE_START]; - end = V_pf_default_rule.timeout[PFTM_ADAPTIVE_END]; - states = V_pf_status.states; - } - if (end && states > start && start < end) { - if (states < end) - return (state->expire + timeout * (end - states) / - (end - start)); - else - return (time_uptime); - } - return (state->expire + timeout); -} - -void -pf_purge_expired_src_nodes() -{ - struct pf_srchash *sh; - struct pf_src_node *cur, *next; - int i; - - for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) { - PF_HASHROW_LOCK(sh); - LIST_FOREACH_SAFE(cur, &sh->nodes, entry, next) - if (cur->states <= 0 && cur->expire <= time_uptime) { - if (cur->rule.ptr != NULL) - cur->rule.ptr->src_nodes--; - LIST_REMOVE(cur, entry); - V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - V_pf_status.src_nodes--; - uma_zfree(V_pf_sources_z, cur); - } else if (cur->rule.ptr != NULL) - cur->rule.ptr->rule_flag |= PFRULE_REFS; - PF_HASHROW_UNLOCK(sh); - } -} - -static void -pf_src_tree_remove_state(struct pf_state *s) -{ - u_int32_t timeout; - - if (s->src_node != NULL) { - if (s->src.tcp_est) - --s->src_node->conn; - if (--s->src_node->states <= 0) { - timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; - if (!timeout) - timeout = - V_pf_default_rule.timeout[PFTM_SRC_NODE]; - s->src_node->expire = time_uptime + timeout; - } - } - if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { - if (--s->nat_src_node->states <= 0) { - timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; - if (!timeout) - timeout = - V_pf_default_rule.timeout[PFTM_SRC_NODE]; - s->nat_src_node->expire = time_uptime + timeout; - } - } - s->src_node = s->nat_src_node = NULL; -} - -/* - * Unlink and potentilly free a state. Function may be - * called with ID hash row locked, but always returns - * unlocked, since it needs to go through key hash locking. - */ -int -pf_unlink_state(struct pf_state *s, u_int flags) -{ - struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(s)]; - - if ((flags & PF_ENTER_LOCKED) == 0) - PF_HASHROW_LOCK(ih); - else - PF_HASHROW_ASSERT(ih); - - if (s->timeout == PFTM_UNLINKED) { - /* - * State is being processed - * by pf_unlink_state() in - * an other thread. - */ - PF_HASHROW_UNLOCK(ih); - return (0); /* XXXGL: undefined actually */ - } - - s->timeout = PFTM_UNLINKED; - - if (s->src.state == PF_TCPS_PROXY_DST) { - /* XXX wire key the right one? */ - pf_send_tcp(NULL, s->rule.ptr, s->key[PF_SK_WIRE]->af, - &s->key[PF_SK_WIRE]->addr[1], - &s->key[PF_SK_WIRE]->addr[0], - s->key[PF_SK_WIRE]->port[1], - s->key[PF_SK_WIRE]->port[0], - s->src.seqhi, s->src.seqlo + 1, - TH_RST|TH_ACK, 0, 0, 0, 1, s->tag, NULL); - } - - LIST_REMOVE(s, entry); - pf_src_tree_remove_state(s); - PF_HASHROW_UNLOCK(ih); - - if (pfsync_delete_state_ptr != NULL) - pfsync_delete_state_ptr(s); - - pf_detach_state(s); - refcount_release(&s->refs); - - return (pf_release_state(s)); -} - -void -pf_free_state(struct pf_state *cur) -{ - - KASSERT(cur->refs == 0, ("%s: %p has refs", __func__, cur)); - KASSERT(cur->timeout == PFTM_UNLINKED, ("%s: timeout %u", __func__, - cur->timeout)); - --cur->rule.ptr->states_cur; - if (cur->nat_rule.ptr != NULL) - --cur->nat_rule.ptr->states_cur; - if (cur->anchor.ptr != NULL) - --cur->anchor.ptr->states_cur; - pf_normalize_tcp_cleanup(cur); - uma_zfree(V_pf_state_z, cur); - V_pf_status.fcounters[FCNT_STATE_REMOVALS]++; -} - -/* - * Called only from pf_purge_thread(), thus serialized. - */ -static int -pf_purge_expired_states(int maxcheck) -{ - static u_int i = 0; - - struct pf_idhash *ih; - struct pf_state *s; - int rv = 0; - - V_pf_status.states = uma_zone_get_cur(V_pf_state_z); - - /* - * Go through hash and unlink states that expire now. - */ - while (maxcheck > 0) { - - /* Wrap to start of hash when we hit the end. */ - if (i > V_pf_hashmask) { - i = 0; - rv = 1; - } - - ih = &V_pf_idhash[i]; -relock: - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - if (pf_state_expires(s) <= time_uptime) { - V_pf_status.states -= - pf_unlink_state(s, PF_ENTER_LOCKED); - goto relock; - } - s->rule.ptr->rule_flag |= PFRULE_REFS; - if (s->nat_rule.ptr != NULL) - s->nat_rule.ptr->rule_flag |= PFRULE_REFS; - if (s->anchor.ptr != NULL) - s->anchor.ptr->rule_flag |= PFRULE_REFS; - s->kif->pfik_flags |= PFI_IFLAG_REFS; - if (s->rt_kif) - s->rt_kif->pfik_flags |= PFI_IFLAG_REFS; - } - PF_HASHROW_UNLOCK(ih); - i++; - maxcheck--; - } - - V_pf_status.states = uma_zone_get_cur(V_pf_state_z); - - return (rv); -} - -static void -pf_purge_unlinked_rules() -{ - struct pf_rulequeue tmpq; - struct pf_rule *r, *r1; - - /* - * Do naive mark-and-sweep garbage collecting of old rules. - * Reference flag is raised by pf_purge_expired_states() - * and pf_purge_expired_src_nodes(). - * - * To avoid LOR between PF_UNLNKDRULES_LOCK/PF_RULES_WLOCK, - * use a temporary queue. - */ - TAILQ_INIT(&tmpq); - PF_UNLNKDRULES_LOCK(); - TAILQ_FOREACH_SAFE(r, &V_pf_unlinked_rules, entries, r1) { - if (!(r->rule_flag & PFRULE_REFS)) { - TAILQ_REMOVE(&V_pf_unlinked_rules, r, entries); - TAILQ_INSERT_TAIL(&tmpq, r, entries); - } else - r->rule_flag &= ~PFRULE_REFS; - } - PF_UNLNKDRULES_UNLOCK(); - - if (!TAILQ_EMPTY(&tmpq)) { - PF_RULES_WLOCK(); - TAILQ_FOREACH_SAFE(r, &tmpq, entries, r1) { - TAILQ_REMOVE(&tmpq, r, entries); - pf_free_rule(r); - } - PF_RULES_WUNLOCK(); - } -} - -void -pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: { - u_int32_t a = ntohl(addr->addr32[0]); - printf("%u.%u.%u.%u", (a>>24)&255, (a>>16)&255, - (a>>8)&255, a&255); - if (p) { - p = ntohs(p); - printf(":%u", p); - } - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: { - u_int16_t b; - u_int8_t i, curstart, curend, maxstart, maxend; - curstart = curend = maxstart = maxend = 255; - for (i = 0; i < 8; i++) { - if (!addr->addr16[i]) { - if (curstart == 255) - curstart = i; - curend = i; - } else { - if ((curend - curstart) > - (maxend - maxstart)) { - maxstart = curstart; - maxend = curend; - } - curstart = curend = 255; - } - } - if ((curend - curstart) > - (maxend - maxstart)) { - maxstart = curstart; - maxend = curend; - } - for (i = 0; i < 8; i++) { - if (i >= maxstart && i <= maxend) { - if (i == 0) - printf(":"); - if (i == maxend) - printf(":"); - } else { - b = ntohs(addr->addr16[i]); - printf("%x", b); - if (i < 7) - printf(":"); - } - } - if (p) { - p = ntohs(p); - printf("[%u]", p); - } - break; - } -#endif /* INET6 */ - } -} - -void -pf_print_state(struct pf_state *s) -{ - pf_print_state_parts(s, NULL, NULL); -} - -static void -pf_print_state_parts(struct pf_state *s, - struct pf_state_key *skwp, struct pf_state_key *sksp) -{ - struct pf_state_key *skw, *sks; - u_int8_t proto, dir; - - /* Do our best to fill these, but they're skipped if NULL */ - skw = skwp ? skwp : (s ? s->key[PF_SK_WIRE] : NULL); - sks = sksp ? sksp : (s ? s->key[PF_SK_STACK] : NULL); - proto = skw ? skw->proto : (sks ? sks->proto : 0); - dir = s ? s->direction : 0; - - switch (proto) { - case IPPROTO_IPV4: - printf("IPv4"); - break; - case IPPROTO_IPV6: - printf("IPv6"); - break; - case IPPROTO_TCP: - printf("TCP"); - break; - case IPPROTO_UDP: - printf("UDP"); - break; - case IPPROTO_ICMP: - printf("ICMP"); - break; - case IPPROTO_ICMPV6: - printf("ICMPv6"); - break; - default: - printf("%u", skw->proto); - break; - } - switch (dir) { - case PF_IN: - printf(" in"); - break; - case PF_OUT: - printf(" out"); - break; - } - if (skw) { - printf(" wire: "); - pf_print_host(&skw->addr[0], skw->port[0], skw->af); - printf(" "); - pf_print_host(&skw->addr[1], skw->port[1], skw->af); - } - if (sks) { - printf(" stack: "); - if (sks != skw) { - pf_print_host(&sks->addr[0], sks->port[0], sks->af); - printf(" "); - pf_print_host(&sks->addr[1], sks->port[1], sks->af); - } else - printf("-"); - } - if (s) { - if (proto == IPPROTO_TCP) { - printf(" [lo=%u high=%u win=%u modulator=%u", - s->src.seqlo, s->src.seqhi, - s->src.max_win, s->src.seqdiff); - if (s->src.wscale && s->dst.wscale) - printf(" wscale=%u", - s->src.wscale & PF_WSCALE_MASK); - printf("]"); - printf(" [lo=%u high=%u win=%u modulator=%u", - s->dst.seqlo, s->dst.seqhi, - s->dst.max_win, s->dst.seqdiff); - if (s->src.wscale && s->dst.wscale) - printf(" wscale=%u", - s->dst.wscale & PF_WSCALE_MASK); - printf("]"); - } - printf(" %u:%u", s->src.state, s->dst.state); - } -} - -void -pf_print_flags(u_int8_t f) -{ - if (f) - printf(" "); - if (f & TH_FIN) - printf("F"); - if (f & TH_SYN) - printf("S"); - if (f & TH_RST) - printf("R"); - if (f & TH_PUSH) - printf("P"); - if (f & TH_ACK) - printf("A"); - if (f & TH_URG) - printf("U"); - if (f & TH_ECE) - printf("E"); - if (f & TH_CWR) - printf("W"); -} - -#define PF_SET_SKIP_STEPS(i) \ - do { \ - while (head[i] != cur) { \ - head[i]->skip[i].ptr = cur; \ - head[i] = TAILQ_NEXT(head[i], entries); \ - } \ - } while (0) - -void -pf_calc_skip_steps(struct pf_rulequeue *rules) -{ - struct pf_rule *cur, *prev, *head[PF_SKIP_COUNT]; - int i; - - cur = TAILQ_FIRST(rules); - prev = cur; - for (i = 0; i < PF_SKIP_COUNT; ++i) - head[i] = cur; - while (cur != NULL) { - - if (cur->kif != prev->kif || cur->ifnot != prev->ifnot) - PF_SET_SKIP_STEPS(PF_SKIP_IFP); - if (cur->direction != prev->direction) - PF_SET_SKIP_STEPS(PF_SKIP_DIR); - if (cur->af != prev->af) - PF_SET_SKIP_STEPS(PF_SKIP_AF); - if (cur->proto != prev->proto) - PF_SET_SKIP_STEPS(PF_SKIP_PROTO); - if (cur->src.neg != prev->src.neg || - pf_addr_wrap_neq(&cur->src.addr, &prev->src.addr)) - PF_SET_SKIP_STEPS(PF_SKIP_SRC_ADDR); - if (cur->src.port[0] != prev->src.port[0] || - cur->src.port[1] != prev->src.port[1] || - cur->src.port_op != prev->src.port_op) - PF_SET_SKIP_STEPS(PF_SKIP_SRC_PORT); - if (cur->dst.neg != prev->dst.neg || - pf_addr_wrap_neq(&cur->dst.addr, &prev->dst.addr)) - PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR); - if (cur->dst.port[0] != prev->dst.port[0] || - cur->dst.port[1] != prev->dst.port[1] || - cur->dst.port_op != prev->dst.port_op) - PF_SET_SKIP_STEPS(PF_SKIP_DST_PORT); - - prev = cur; - cur = TAILQ_NEXT(cur, entries); - } - for (i = 0; i < PF_SKIP_COUNT; ++i) - PF_SET_SKIP_STEPS(i); -} - -static int -pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) -{ - if (aw1->type != aw2->type) - return (1); - switch (aw1->type) { - case PF_ADDR_ADDRMASK: - case PF_ADDR_RANGE: - if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, 0)) - return (1); - if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0)) - return (1); - return (0); - case PF_ADDR_DYNIFTL: - return (aw1->p.dyn->pfid_kt != aw2->p.dyn->pfid_kt); - case PF_ADDR_NOROUTE: - case PF_ADDR_URPFFAILED: - return (0); - case PF_ADDR_TABLE: - return (aw1->p.tbl != aw2->p.tbl); - default: - printf("invalid address type: %d\n", aw1->type); - return (1); - } -} - -u_int16_t -pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) -{ - u_int32_t l; - - if (udp && !cksum) - return (0x0000); - l = cksum + old - new; - l = (l >> 16) + (l & 65535); - l = l & 65535; - if (udp && !l) - return (0xFFFF); - return (l); -} - -static void -pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, - struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af) -{ - struct pf_addr ao; - u_int16_t po = *p; - - PF_ACPY(&ao, a, af); - PF_ACPY(a, an, af); - - *p = pn; - - switch (af) { -#ifdef INET - case AF_INET: - *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, - ao.addr16[0], an->addr16[0], 0), - ao.addr16[1], an->addr16[1], 0); - *p = pn; - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - po, pn, u); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - ao.addr16[2], an->addr16[2], u), - ao.addr16[3], an->addr16[3], u), - ao.addr16[4], an->addr16[4], u), - ao.addr16[5], an->addr16[5], u), - ao.addr16[6], an->addr16[6], u), - ao.addr16[7], an->addr16[7], u), - po, pn, u); - break; -#endif /* INET6 */ - } -} - - -/* Changes a u_int32_t. Uses a void * so there are no align restrictions */ -void -pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) -{ - u_int32_t ao; - - memcpy(&ao, a, sizeof(ao)); - memcpy(a, &an, sizeof(u_int32_t)); - *c = pf_cksum_fixup(pf_cksum_fixup(*c, ao / 65536, an / 65536, u), - ao % 65536, an % 65536, u); -} - -#ifdef INET6 -static void -pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) -{ - struct pf_addr ao; - - PF_ACPY(&ao, a, AF_INET6); - PF_ACPY(a, an, AF_INET6); - - *c = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*c, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - ao.addr16[2], an->addr16[2], u), - ao.addr16[3], an->addr16[3], u), - ao.addr16[4], an->addr16[4], u), - ao.addr16[5], an->addr16[5], u), - ao.addr16[6], an->addr16[6], u), - ao.addr16[7], an->addr16[7], u); -} -#endif /* INET6 */ - -static void -pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, - struct pf_addr *na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c, - u_int16_t *ic, u_int16_t *hc, u_int8_t u, sa_family_t af) -{ - struct pf_addr oia, ooa; - - PF_ACPY(&oia, ia, af); - if (oa) - PF_ACPY(&ooa, oa, af); - - /* Change inner protocol port, fix inner protocol checksum. */ - if (ip != NULL) { - u_int16_t oip = *ip; - u_int32_t opc; - - if (pc != NULL) - opc = *pc; - *ip = np; - if (pc != NULL) - *pc = pf_cksum_fixup(*pc, oip, *ip, u); - *ic = pf_cksum_fixup(*ic, oip, *ip, 0); - if (pc != NULL) - *ic = pf_cksum_fixup(*ic, opc, *pc, 0); - } - /* Change inner ip address, fix inner ip and icmp checksums. */ - PF_ACPY(ia, na, af); - switch (af) { -#ifdef INET - case AF_INET: { - u_int32_t oh2c = *h2c; - - *h2c = pf_cksum_fixup(pf_cksum_fixup(*h2c, - oia.addr16[0], ia->addr16[0], 0), - oia.addr16[1], ia->addr16[1], 0); - *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, - oia.addr16[0], ia->addr16[0], 0), - oia.addr16[1], ia->addr16[1], 0); - *ic = pf_cksum_fixup(*ic, oh2c, *h2c, 0); - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*ic, - oia.addr16[0], ia->addr16[0], u), - oia.addr16[1], ia->addr16[1], u), - oia.addr16[2], ia->addr16[2], u), - oia.addr16[3], ia->addr16[3], u), - oia.addr16[4], ia->addr16[4], u), - oia.addr16[5], ia->addr16[5], u), - oia.addr16[6], ia->addr16[6], u), - oia.addr16[7], ia->addr16[7], u); - break; -#endif /* INET6 */ - } - /* Outer ip address, fix outer ip or icmpv6 checksum, if necessary. */ - if (oa) { - PF_ACPY(oa, na, af); - switch (af) { -#ifdef INET - case AF_INET: - *hc = pf_cksum_fixup(pf_cksum_fixup(*hc, - ooa.addr16[0], oa->addr16[0], 0), - ooa.addr16[1], oa->addr16[1], 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*ic, - ooa.addr16[0], oa->addr16[0], u), - ooa.addr16[1], oa->addr16[1], u), - ooa.addr16[2], oa->addr16[2], u), - ooa.addr16[3], oa->addr16[3], u), - ooa.addr16[4], oa->addr16[4], u), - ooa.addr16[5], oa->addr16[5], u), - ooa.addr16[6], oa->addr16[6], u), - ooa.addr16[7], oa->addr16[7], u); - break; -#endif /* INET6 */ - } - } -} - - -/* - * Need to modulate the sequence numbers in the TCP SACK option - * (credits to Krzysztof Pfaff for report and patch) - */ -static int -pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, - struct tcphdr *th, struct pf_state_peer *dst) -{ - int hlen = (th->th_off << 2) - sizeof(*th), thoptlen = hlen; - u_int8_t opts[TCP_MAXOLEN], *opt = opts; - int copyback = 0, i, olen; - struct sackblk sack; - -#define TCPOLEN_SACKLEN (TCPOLEN_SACK + 2) - if (hlen < TCPOLEN_SACKLEN || - !pf_pull_hdr(m, off + sizeof(*th), opts, hlen, NULL, NULL, pd->af)) - return 0; - - while (hlen >= TCPOLEN_SACKLEN) { - olen = opt[1]; - switch (*opt) { - case TCPOPT_EOL: /* FALLTHROUGH */ - case TCPOPT_NOP: - opt++; - hlen--; - break; - case TCPOPT_SACK: - if (olen > hlen) - olen = hlen; - if (olen >= TCPOLEN_SACKLEN) { - for (i = 2; i + TCPOLEN_SACK <= olen; - i += TCPOLEN_SACK) { - memcpy(&sack, &opt[i], sizeof(sack)); - pf_change_a(&sack.start, &th->th_sum, - htonl(ntohl(sack.start) - - dst->seqdiff), 0); - pf_change_a(&sack.end, &th->th_sum, - htonl(ntohl(sack.end) - - dst->seqdiff), 0); - memcpy(&opt[i], &sack, sizeof(sack)); - } - copyback = 1; - } - /* FALLTHROUGH */ - default: - if (olen < 2) - olen = 2; - hlen -= olen; - opt += olen; - } - } - - if (copyback) - m_copyback(m, off + sizeof(*th), thoptlen, (caddr_t)opts); - return (copyback); -} - -static void -pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af, - const struct pf_addr *saddr, const struct pf_addr *daddr, - u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, - u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag, - u_int16_t rtag, struct ifnet *ifp) -{ - struct pf_send_entry *pfse; - struct mbuf *m; - int len, tlen; -#ifdef INET - struct ip *h = NULL; -#endif /* INET */ -#ifdef INET6 - struct ip6_hdr *h6 = NULL; -#endif /* INET6 */ - struct tcphdr *th; - char *opt; - struct pf_mtag *pf_mtag; - - len = 0; - th = NULL; - - /* maximum segment size tcp option */ - tlen = sizeof(struct tcphdr); - if (mss) - tlen += 4; - - switch (af) { -#ifdef INET - case AF_INET: - len = sizeof(struct ip) + tlen; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - len = sizeof(struct ip6_hdr) + tlen; - break; -#endif /* INET6 */ - default: - panic("%s: unsupported af %d", __func__, af); - } - - /* Allocate outgoing queue entry, mbuf and mbuf tag. */ - pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); - if (pfse == NULL) - return; - m = m_gethdr(M_NOWAIT, MT_HEADER); - if (m == NULL) { - free(pfse, M_PFTEMP); - return; - } -#ifdef MAC - mac_netinet_firewall_send(m); -#endif - if ((pf_mtag = pf_get_mtag(m)) == NULL) { - free(pfse, M_PFTEMP); - m_freem(m); - return; - } - if (tag) - m->m_flags |= M_SKIP_FIREWALL; - pf_mtag->tag = rtag; - - if (r != NULL && r->rtableid >= 0) - M_SETFIB(m, r->rtableid); - -#ifdef ALTQ - if (r != NULL && r->qid) { - pf_mtag->qid = r->qid; - - /* add hints for ecn */ - pf_mtag->hdr = mtod(m, struct ip *); - } -#endif /* ALTQ */ - m->m_data += max_linkhdr; - m->m_pkthdr.len = m->m_len = len; - m->m_pkthdr.rcvif = NULL; - bzero(m->m_data, len); - switch (af) { -#ifdef INET - case AF_INET: - h = mtod(m, struct ip *); - - /* IP header fields included in the TCP checksum */ - h->ip_p = IPPROTO_TCP; - h->ip_len = htons(tlen); - h->ip_src.s_addr = saddr->v4.s_addr; - h->ip_dst.s_addr = daddr->v4.s_addr; - - th = (struct tcphdr *)((caddr_t)h + sizeof(struct ip)); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - h6 = mtod(m, struct ip6_hdr *); - - /* IP header fields included in the TCP checksum */ - h6->ip6_nxt = IPPROTO_TCP; - h6->ip6_plen = htons(tlen); - memcpy(&h6->ip6_src, &saddr->v6, sizeof(struct in6_addr)); - memcpy(&h6->ip6_dst, &daddr->v6, sizeof(struct in6_addr)); - - th = (struct tcphdr *)((caddr_t)h6 + sizeof(struct ip6_hdr)); - break; -#endif /* INET6 */ - } - - /* TCP header */ - th->th_sport = sport; - th->th_dport = dport; - th->th_seq = htonl(seq); - th->th_ack = htonl(ack); - th->th_off = tlen >> 2; - th->th_flags = flags; - th->th_win = htons(win); - - if (mss) { - opt = (char *)(th + 1); - opt[0] = TCPOPT_MAXSEG; - opt[1] = 4; - HTONS(mss); - bcopy((caddr_t)&mss, (caddr_t)(opt + 2), 2); - } - - switch (af) { -#ifdef INET - case AF_INET: - /* TCP checksum */ - th->th_sum = in_cksum(m, len); - - /* Finish the IP header */ - h->ip_v = 4; - h->ip_hl = sizeof(*h) >> 2; - h->ip_tos = IPTOS_LOWDELAY; - h->ip_off = V_path_mtu_discovery ? IP_DF : 0; - h->ip_len = len; - h->ip_ttl = ttl ? ttl : V_ip_defttl; - h->ip_sum = 0; - - pfse->pfse_type = PFSE_IP; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - /* TCP checksum */ - th->th_sum = in6_cksum(m, IPPROTO_TCP, - sizeof(struct ip6_hdr), tlen); - - h6->ip6_vfc |= IPV6_VERSION; - h6->ip6_hlim = IPV6_DEFHLIM; - - pfse->pfse_type = PFSE_IP6; - break; -#endif /* INET6 */ - } - pfse->pfse_m = m; - pf_send(pfse); -} - -static void -pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, - struct pf_rule *r) -{ - struct pf_send_entry *pfse; - struct mbuf *m0; - struct pf_mtag *pf_mtag; - - /* Allocate outgoing queue entry, mbuf and mbuf tag. */ - pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); - if (pfse == NULL) - return; - - if ((m0 = m_copypacket(m, M_NOWAIT)) == NULL) { - free(pfse, M_PFTEMP); - return; - } - - if ((pf_mtag = pf_get_mtag(m0)) == NULL) { - free(pfse, M_PFTEMP); - return; - } - /* XXX: revisit */ - m0->m_flags |= M_SKIP_FIREWALL; - - if (r->rtableid >= 0) - M_SETFIB(m0, r->rtableid); - -#ifdef ALTQ - if (r->qid) { - pf_mtag->qid = r->qid; - /* add hints for ecn */ - pf_mtag->hdr = mtod(m0, struct ip *); - } -#endif /* ALTQ */ - - switch (af) { -#ifdef INET - case AF_INET: - { - struct ip *ip; - - /* icmp_error() expects host byte ordering */ - ip = mtod(m0, struct ip *); - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); - - pfse->pfse_type = PFSE_ICMP; - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - pfse->pfse_type = PFSE_ICMP6; - break; -#endif /* INET6 */ - } - pfse->pfse_m = m0; - pfse->pfse_icmp_type = type; - pfse->pfse_icmp_code = code; - pf_send(pfse); -} - -/* - * Return 1 if the addresses a and b match (with mask m), otherwise return 0. - * If n is 0, they match if they are equal. If n is != 0, they match if they - * are different. - */ -int -pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m, - struct pf_addr *b, sa_family_t af) -{ - int match = 0; - - switch (af) { -#ifdef INET - case AF_INET: - if ((a->addr32[0] & m->addr32[0]) == - (b->addr32[0] & m->addr32[0])) - match++; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (((a->addr32[0] & m->addr32[0]) == - (b->addr32[0] & m->addr32[0])) && - ((a->addr32[1] & m->addr32[1]) == - (b->addr32[1] & m->addr32[1])) && - ((a->addr32[2] & m->addr32[2]) == - (b->addr32[2] & m->addr32[2])) && - ((a->addr32[3] & m->addr32[3]) == - (b->addr32[3] & m->addr32[3]))) - match++; - break; -#endif /* INET6 */ - } - if (match) { - if (n) - return (0); - else - return (1); - } else { - if (n) - return (1); - else - return (0); - } -} - -/* - * Return 1 if b <= a <= e, otherwise return 0. - */ -int -pf_match_addr_range(struct pf_addr *b, struct pf_addr *e, - struct pf_addr *a, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: - if ((a->addr32[0] < b->addr32[0]) || - (a->addr32[0] > e->addr32[0])) - return (0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: { - int i; - - /* check a >= b */ - for (i = 0; i < 4; ++i) - if (a->addr32[i] > b->addr32[i]) - break; - else if (a->addr32[i] < b->addr32[i]) - return (0); - /* check a <= e */ - for (i = 0; i < 4; ++i) - if (a->addr32[i] < e->addr32[i]) - break; - else if (a->addr32[i] > e->addr32[i]) - return (0); - break; - } -#endif /* INET6 */ - } - return (1); -} - -static int -pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p) -{ - switch (op) { - case PF_OP_IRG: - return ((p > a1) && (p < a2)); - case PF_OP_XRG: - return ((p < a1) || (p > a2)); - case PF_OP_RRG: - return ((p >= a1) && (p <= a2)); - case PF_OP_EQ: - return (p == a1); - case PF_OP_NE: - return (p != a1); - case PF_OP_LT: - return (p < a1); - case PF_OP_LE: - return (p <= a1); - case PF_OP_GT: - return (p > a1); - case PF_OP_GE: - return (p >= a1); - } - return (0); /* never reached */ -} - -int -pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) -{ - NTOHS(a1); - NTOHS(a2); - NTOHS(p); - return (pf_match(op, a1, a2, p)); -} - -static int -pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) -{ - if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE) - return (0); - return (pf_match(op, a1, a2, u)); -} - -static int -pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) -{ - if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) - return (0); - return (pf_match(op, a1, a2, g)); -} - -int -pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag, int mtag) -{ - if (*tag == -1) - *tag = mtag; - - return ((!r->match_tag_not && r->match_tag == *tag) || - (r->match_tag_not && r->match_tag != *tag)); -} - -int -pf_tag_packet(struct mbuf *m, struct pf_pdesc *pd, int tag) -{ - - KASSERT(tag > 0, ("%s: tag %d", __func__, tag)); - - if (pd->pf_mtag == NULL && ((pd->pf_mtag = pf_get_mtag(m)) == NULL)) - return (ENOMEM); - - pd->pf_mtag->tag = tag; - - return (0); -} - -void -pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n, - struct pf_rule **r, struct pf_rule **a, int *match) -{ - struct pf_anchor_stackframe *f; - - PF_RULES_RASSERT(); - - (*r)->anchor->match = 0; - if (match) - *match = 0; - if (*depth >= sizeof(V_pf_anchor_stack) / - sizeof(V_pf_anchor_stack[0])) { - printf("pf_step_into_anchor: stack overflow\n"); - *r = TAILQ_NEXT(*r, entries); - return; - } else if (*depth == 0 && a != NULL) - *a = *r; - f = V_pf_anchor_stack + (*depth)++; - f->rs = *rs; - f->r = *r; - if ((*r)->anchor_wildcard) { - f->parent = &(*r)->anchor->children; - if ((f->child = RB_MIN(pf_anchor_node, f->parent)) == - NULL) { - *r = NULL; - return; - } - *rs = &f->child->ruleset; - } else { - f->parent = NULL; - f->child = NULL; - *rs = &(*r)->anchor->ruleset; - } - *r = TAILQ_FIRST((*rs)->rules[n].active.ptr); -} - -int -pf_step_out_of_anchor(int *depth, struct pf_ruleset **rs, int n, - struct pf_rule **r, struct pf_rule **a, int *match) -{ - struct pf_anchor_stackframe *f; - int quick = 0; - - PF_RULES_RASSERT(); - - do { - if (*depth <= 0) - break; - f = V_pf_anchor_stack + *depth - 1; - if (f->parent != NULL && f->child != NULL) { - if (f->child->match || - (match != NULL && *match)) { - f->r->anchor->match = 1; - *match = 0; - } - f->child = RB_NEXT(pf_anchor_node, f->parent, f->child); - if (f->child != NULL) { - *rs = &f->child->ruleset; - *r = TAILQ_FIRST((*rs)->rules[n].active.ptr); - if (*r == NULL) - continue; - else - break; - } - } - (*depth)--; - if (*depth == 0 && a != NULL) - *a = NULL; - *rs = f->rs; - if (f->r->anchor->match || (match != NULL && *match)) - quick = f->r->quick; - *r = TAILQ_NEXT(f->r, entries); - } while (*r == NULL); - - return (quick); -} - -#ifdef INET6 -void -pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr, - struct pf_addr *rmask, struct pf_addr *saddr, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: - naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | - ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); - break; -#endif /* INET */ - case AF_INET6: - naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | - ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); - naddr->addr32[1] = (raddr->addr32[1] & rmask->addr32[1]) | - ((rmask->addr32[1] ^ 0xffffffff ) & saddr->addr32[1]); - naddr->addr32[2] = (raddr->addr32[2] & rmask->addr32[2]) | - ((rmask->addr32[2] ^ 0xffffffff ) & saddr->addr32[2]); - naddr->addr32[3] = (raddr->addr32[3] & rmask->addr32[3]) | - ((rmask->addr32[3] ^ 0xffffffff ) & saddr->addr32[3]); - break; - } -} - -void -pf_addr_inc(struct pf_addr *addr, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: - addr->addr32[0] = htonl(ntohl(addr->addr32[0]) + 1); - break; -#endif /* INET */ - case AF_INET6: - if (addr->addr32[3] == 0xffffffff) { - addr->addr32[3] = 0; - if (addr->addr32[2] == 0xffffffff) { - addr->addr32[2] = 0; - if (addr->addr32[1] == 0xffffffff) { - addr->addr32[1] = 0; - addr->addr32[0] = - htonl(ntohl(addr->addr32[0]) + 1); - } else - addr->addr32[1] = - htonl(ntohl(addr->addr32[1]) + 1); - } else - addr->addr32[2] = - htonl(ntohl(addr->addr32[2]) + 1); - } else - addr->addr32[3] = - htonl(ntohl(addr->addr32[3]) + 1); - break; - } -} -#endif /* INET6 */ - -int -pf_socket_lookup(int direction, struct pf_pdesc *pd, struct mbuf *m) -{ - struct pf_addr *saddr, *daddr; - u_int16_t sport, dport; - struct inpcbinfo *pi; - struct inpcb *inp; - - pd->lookup.uid = UID_MAX; - pd->lookup.gid = GID_MAX; - - switch (pd->proto) { - case IPPROTO_TCP: - if (pd->hdr.tcp == NULL) - return (-1); - sport = pd->hdr.tcp->th_sport; - dport = pd->hdr.tcp->th_dport; - pi = &V_tcbinfo; - break; - case IPPROTO_UDP: - if (pd->hdr.udp == NULL) - return (-1); - sport = pd->hdr.udp->uh_sport; - dport = pd->hdr.udp->uh_dport; - pi = &V_udbinfo; - break; - default: - return (-1); - } - if (direction == PF_IN) { - saddr = pd->src; - daddr = pd->dst; - } else { - u_int16_t p; - - p = sport; - sport = dport; - dport = p; - saddr = pd->dst; - daddr = pd->src; - } - switch (pd->af) { -#ifdef INET - case AF_INET: - inp = in_pcblookup_mbuf(pi, saddr->v4, sport, daddr->v4, - dport, INPLOOKUP_RLOCKPCB, NULL, m); - if (inp == NULL) { - inp = in_pcblookup_mbuf(pi, saddr->v4, sport, - daddr->v4, dport, INPLOOKUP_WILDCARD | - INPLOOKUP_RLOCKPCB, NULL, m); - if (inp == NULL) - return (-1); - } - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - inp = in6_pcblookup_mbuf(pi, &saddr->v6, sport, &daddr->v6, - dport, INPLOOKUP_RLOCKPCB, NULL, m); - if (inp == NULL) { - inp = in6_pcblookup_mbuf(pi, &saddr->v6, sport, - &daddr->v6, dport, INPLOOKUP_WILDCARD | - INPLOOKUP_RLOCKPCB, NULL, m); - if (inp == NULL) - return (-1); - } - break; -#endif /* INET6 */ - - default: - return (-1); - } - INP_RLOCK_ASSERT(inp); - pd->lookup.uid = inp->inp_cred->cr_uid; - pd->lookup.gid = inp->inp_cred->cr_groups[0]; - INP_RUNLOCK(inp); - - return (1); -} - -static u_int8_t -pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) -{ - int hlen; - u_int8_t hdr[60]; - u_int8_t *opt, optlen; - u_int8_t wscale = 0; - - hlen = th_off << 2; /* hlen <= sizeof(hdr) */ - if (hlen <= sizeof(struct tcphdr)) - return (0); - if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) - return (0); - opt = hdr + sizeof(struct tcphdr); - hlen -= sizeof(struct tcphdr); - while (hlen >= 3) { - switch (*opt) { - case TCPOPT_EOL: - case TCPOPT_NOP: - ++opt; - --hlen; - break; - case TCPOPT_WINDOW: - wscale = opt[2]; - if (wscale > TCP_MAX_WINSHIFT) - wscale = TCP_MAX_WINSHIFT; - wscale |= PF_WSCALE_FLAG; - /* FALLTHROUGH */ - default: - optlen = opt[1]; - if (optlen < 2) - optlen = 2; - hlen -= optlen; - opt += optlen; - break; - } - } - return (wscale); -} - -static u_int16_t -pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) -{ - int hlen; - u_int8_t hdr[60]; - u_int8_t *opt, optlen; - u_int16_t mss = V_tcp_mssdflt; - - hlen = th_off << 2; /* hlen <= sizeof(hdr) */ - if (hlen <= sizeof(struct tcphdr)) - return (0); - if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) - return (0); - opt = hdr + sizeof(struct tcphdr); - hlen -= sizeof(struct tcphdr); - while (hlen >= TCPOLEN_MAXSEG) { - switch (*opt) { - case TCPOPT_EOL: - case TCPOPT_NOP: - ++opt; - --hlen; - break; - case TCPOPT_MAXSEG: - bcopy((caddr_t)(opt + 2), (caddr_t)&mss, 2); - NTOHS(mss); - /* FALLTHROUGH */ - default: - optlen = opt[1]; - if (optlen < 2) - optlen = 2; - hlen -= optlen; - opt += optlen; - break; - } - } - return (mss); -} - -static u_int16_t -pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) -{ -#ifdef INET - struct sockaddr_in *dst; - struct route ro; -#endif /* INET */ -#ifdef INET6 - struct sockaddr_in6 *dst6; - struct route_in6 ro6; -#endif /* INET6 */ - struct rtentry *rt = NULL; - int hlen = 0; - u_int16_t mss = V_tcp_mssdflt; - - switch (af) { -#ifdef INET - case AF_INET: - hlen = sizeof(struct ip); - bzero(&ro, sizeof(ro)); - dst = (struct sockaddr_in *)&ro.ro_dst; - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = addr->v4; - in_rtalloc_ign(&ro, 0, rtableid); - rt = ro.ro_rt; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - hlen = sizeof(struct ip6_hdr); - bzero(&ro6, sizeof(ro6)); - dst6 = (struct sockaddr_in6 *)&ro6.ro_dst; - dst6->sin6_family = AF_INET6; - dst6->sin6_len = sizeof(*dst6); - dst6->sin6_addr = addr->v6; - in6_rtalloc_ign(&ro6, 0, rtableid); - rt = ro6.ro_rt; - break; -#endif /* INET6 */ - } - - if (rt && rt->rt_ifp) { - mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); - mss = max(V_tcp_mssdflt, mss); - RTFREE(rt); - } - mss = min(mss, offer); - mss = max(mss, 64); /* sanity - at least max opt space */ - return (mss); -} - -static void -pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) -{ - struct pf_rule *r = s->rule.ptr; - struct pf_src_node *sn = NULL; - - s->rt_kif = NULL; - if (!r->rt || r->rt == PF_FASTROUTE) - return; - switch (s->key[PF_SK_WIRE]->af) { -#ifdef INET - case AF_INET: - pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, &sn); - s->rt_kif = r->rpool.cur->kif; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, &sn); - s->rt_kif = r->rpool.cur->kif; - break; -#endif /* INET6 */ - } -} - -static u_int32_t -pf_tcp_iss(struct pf_pdesc *pd) -{ - MD5_CTX ctx; - u_int32_t digest[4]; - - if (V_pf_tcp_secret_init == 0) { - read_random(&V_pf_tcp_secret, sizeof(V_pf_tcp_secret)); - MD5Init(&V_pf_tcp_secret_ctx); - MD5Update(&V_pf_tcp_secret_ctx, V_pf_tcp_secret, - sizeof(V_pf_tcp_secret)); - V_pf_tcp_secret_init = 1; - } - - ctx = V_pf_tcp_secret_ctx; - - MD5Update(&ctx, (char *)&pd->hdr.tcp->th_sport, sizeof(u_short)); - MD5Update(&ctx, (char *)&pd->hdr.tcp->th_dport, sizeof(u_short)); - if (pd->af == AF_INET6) { - MD5Update(&ctx, (char *)&pd->src->v6, sizeof(struct in6_addr)); - MD5Update(&ctx, (char *)&pd->dst->v6, sizeof(struct in6_addr)); - } else { - MD5Update(&ctx, (char *)&pd->src->v4, sizeof(struct in_addr)); - MD5Update(&ctx, (char *)&pd->dst->v4, sizeof(struct in_addr)); - } - MD5Final((u_char *)digest, &ctx); - V_pf_tcp_iss_off += 4096; -#define ISN_RANDOM_INCREMENT (4096 - 1) - return (digest[0] + (arc4random() & ISN_RANDOM_INCREMENT) + - V_pf_tcp_iss_off); -#undef ISN_RANDOM_INCREMENT -} - -static int -pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, - struct pfi_kif *kif, struct mbuf *m, int off, struct pf_pdesc *pd, - struct pf_rule **am, struct pf_ruleset **rsm, struct inpcb *inp) -{ - struct pf_rule *nr = NULL; - struct pf_addr * const saddr = pd->src; - struct pf_addr * const daddr = pd->dst; - sa_family_t af = pd->af; - struct pf_rule *r, *a = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_src_node *nsn = NULL; - struct tcphdr *th = pd->hdr.tcp; - struct pf_state_key *sk = NULL, *nk = NULL; - u_short reason; - int rewrite = 0, hdrlen = 0; - int tag = -1, rtableid = -1; - int asd = 0; - int match = 0; - int state_icmp = 0; - u_int16_t sport = 0, dport = 0; - u_int16_t bproto_sum = 0, bip_sum = 0; - u_int8_t icmptype = 0, icmpcode = 0; - - PF_RULES_RASSERT(); - - if (inp != NULL) { - INP_LOCK_ASSERT(inp); - pd->lookup.uid = inp->inp_cred->cr_uid; - pd->lookup.gid = inp->inp_cred->cr_groups[0]; - pd->lookup.done = 1; - } - - switch (pd->proto) { - case IPPROTO_TCP: - sport = th->th_sport; - dport = th->th_dport; - hdrlen = sizeof(*th); - break; - case IPPROTO_UDP: - sport = pd->hdr.udp->uh_sport; - dport = pd->hdr.udp->uh_dport; - hdrlen = sizeof(*pd->hdr.udp); - break; -#ifdef INET - case IPPROTO_ICMP: - if (pd->af != AF_INET) - break; - sport = dport = pd->hdr.icmp->icmp_id; - hdrlen = sizeof(*pd->hdr.icmp); - icmptype = pd->hdr.icmp->icmp_type; - icmpcode = pd->hdr.icmp->icmp_code; - - if (icmptype == ICMP_UNREACH || - icmptype == ICMP_SOURCEQUENCH || - icmptype == ICMP_REDIRECT || - icmptype == ICMP_TIMXCEED || - icmptype == ICMP_PARAMPROB) - state_icmp++; - break; -#endif /* INET */ -#ifdef INET6 - case IPPROTO_ICMPV6: - if (af != AF_INET6) - break; - sport = dport = pd->hdr.icmp6->icmp6_id; - hdrlen = sizeof(*pd->hdr.icmp6); - icmptype = pd->hdr.icmp6->icmp6_type; - icmpcode = pd->hdr.icmp6->icmp6_code; - - if (icmptype == ICMP6_DST_UNREACH || - icmptype == ICMP6_PACKET_TOO_BIG || - icmptype == ICMP6_TIME_EXCEEDED || - icmptype == ICMP6_PARAM_PROB) - state_icmp++; - break; -#endif /* INET6 */ - default: - sport = dport = hdrlen = 0; - break; - } - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - - /* check packet for BINAT/NAT/RDR */ - if ((nr = pf_get_translation(pd, m, off, direction, kif, &nsn, &sk, - &nk, saddr, daddr, sport, dport)) != NULL) { - KASSERT(sk != NULL, ("%s: null sk", __func__)); - KASSERT(nk != NULL, ("%s: null nk", __func__)); - - if (pd->ip_sum) - bip_sum = *pd->ip_sum; - - switch (pd->proto) { - case IPPROTO_TCP: - bproto_sum = th->th_sum; - pd->proto_sum = &th->th_sum; - - if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || - nk->port[pd->sidx] != sport) { - pf_change_ap(saddr, &th->th_sport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 0, af); - pd->sport = &th->th_sport; - sport = th->th_sport; - } - - if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || - nk->port[pd->didx] != dport) { - pf_change_ap(daddr, &th->th_dport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 0, af); - dport = th->th_dport; - pd->dport = &th->th_dport; - } - rewrite++; - break; - case IPPROTO_UDP: - bproto_sum = pd->hdr.udp->uh_sum; - pd->proto_sum = &pd->hdr.udp->uh_sum; - - if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || - nk->port[pd->sidx] != sport) { - pf_change_ap(saddr, &pd->hdr.udp->uh_sport, - pd->ip_sum, &pd->hdr.udp->uh_sum, - &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, af); - sport = pd->hdr.udp->uh_sport; - pd->sport = &pd->hdr.udp->uh_sport; - } - - if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || - nk->port[pd->didx] != dport) { - pf_change_ap(daddr, &pd->hdr.udp->uh_dport, - pd->ip_sum, &pd->hdr.udp->uh_sum, - &nk->addr[pd->didx], - nk->port[pd->didx], 1, af); - dport = pd->hdr.udp->uh_dport; - pd->dport = &pd->hdr.udp->uh_dport; - } - rewrite++; - break; -#ifdef INET - case IPPROTO_ICMP: - nk->port[0] = nk->port[1]; - if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET)) - pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - nk->addr[pd->sidx].v4.s_addr, 0); - - if (PF_ANEQ(daddr, &nk->addr[pd->didx], AF_INET)) - pf_change_a(&daddr->v4.s_addr, pd->ip_sum, - nk->addr[pd->didx].v4.s_addr, 0); - - if (nk->port[1] != pd->hdr.icmp->icmp_id) { - pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( - pd->hdr.icmp->icmp_cksum, sport, - nk->port[1], 0); - pd->hdr.icmp->icmp_id = nk->port[1]; - pd->sport = &pd->hdr.icmp->icmp_id; - } - m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); - break; -#endif /* INET */ -#ifdef INET6 - case IPPROTO_ICMPV6: - nk->port[0] = nk->port[1]; - if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET6)) - pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, - &nk->addr[pd->sidx], 0); - - if (PF_ANEQ(daddr, &nk->addr[pd->didx], AF_INET6)) - pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, - &nk->addr[pd->didx], 0); - rewrite++; - break; -#endif /* INET */ - default: - switch (af) { -#ifdef INET - case AF_INET: - if (PF_ANEQ(saddr, - &nk->addr[pd->sidx], AF_INET)) - pf_change_a(&saddr->v4.s_addr, - pd->ip_sum, - nk->addr[pd->sidx].v4.s_addr, 0); - - if (PF_ANEQ(daddr, - &nk->addr[pd->didx], AF_INET)) - pf_change_a(&daddr->v4.s_addr, - pd->ip_sum, - nk->addr[pd->didx].v4.s_addr, 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (PF_ANEQ(saddr, - &nk->addr[pd->sidx], AF_INET6)) - PF_ACPY(saddr, &nk->addr[pd->sidx], af); - - if (PF_ANEQ(daddr, - &nk->addr[pd->didx], AF_INET6)) - PF_ACPY(saddr, &nk->addr[pd->didx], af); - break; -#endif /* INET */ - } - break; - } - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } - - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, saddr, af, - r->src.neg, kif, M_GETFIB(m))) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - /* tcp/udp only. port_op always 0 in other cases */ - else if (r->src.port_op && !pf_match_port(r->src.port_op, - r->src.port[0], r->src.port[1], sport)) - r = r->skip[PF_SKIP_SRC_PORT].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, - r->dst.neg, NULL, M_GETFIB(m))) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - /* tcp/udp only. port_op always 0 in other cases */ - else if (r->dst.port_op && !pf_match_port(r->dst.port_op, - r->dst.port[0], r->dst.port[1], dport)) - r = r->skip[PF_SKIP_DST_PORT].ptr; - /* icmp only. type always 0 in other cases */ - else if (r->type && r->type != icmptype + 1) - r = TAILQ_NEXT(r, entries); - /* icmp only. type always 0 in other cases */ - else if (r->code && r->code != icmpcode + 1) - r = TAILQ_NEXT(r, entries); - else if (r->tos && !(r->tos == pd->tos)) - r = TAILQ_NEXT(r, entries); - else if (r->rule_flag & PFRULE_FRAGMENT) - r = TAILQ_NEXT(r, entries); - else if (pd->proto == IPPROTO_TCP && - (r->flagset & th->th_flags) != r->flags) - r = TAILQ_NEXT(r, entries); - /* tcp/udp only. uid.op always 0 in other cases */ - else if (r->uid.op && (pd->lookup.done || (pd->lookup.done = - pf_socket_lookup(direction, pd, m), 1)) && - !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], - pd->lookup.uid)) - r = TAILQ_NEXT(r, entries); - /* tcp/udp only. gid.op always 0 in other cases */ - else if (r->gid.op && (pd->lookup.done || (pd->lookup.done = - pf_socket_lookup(direction, pd, m), 1)) && - !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], - pd->lookup.gid)) - r = TAILQ_NEXT(r, entries); - else if (r->prob && - r->prob <= arc4random()) - r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, &tag, - pd->pf_mtag ? pd->pf_mtag->tag : 0)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY && - (pd->proto != IPPROTO_TCP || !pf_osfp_match( - pf_osfp_fingerprint(pd, m, off, th), - r->os_fingerprint))) - r = TAILQ_NEXT(r, entries); - else { - if (r->tag) - tag = r->tag; - if (r->rtableid >= 0) - rtableid = r->rtableid; - if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; - if ((*rm)->quick) - break; - r = TAILQ_NEXT(r, entries); - } else - pf_step_into_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match); - } - if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match)) - break; - } - r = *rm; - a = *am; - ruleset = *rsm; - - REASON_SET(&reason, PFRES_MATCH); - - if (r->log || (nr != NULL && nr->log)) { - if (rewrite) - m_copyback(m, off, hdrlen, pd->hdr.any); - PFLOG_PACKET(kif, m, af, direction, reason, r->log ? r : nr, a, - ruleset, pd, 1); - } - - if ((r->action == PF_DROP) && - ((r->rule_flag & PFRULE_RETURNRST) || - (r->rule_flag & PFRULE_RETURNICMP) || - (r->rule_flag & PFRULE_RETURN))) { - /* undo NAT changes, if they have taken place */ - if (nr != NULL) { - PF_ACPY(saddr, &sk->addr[pd->sidx], af); - PF_ACPY(daddr, &sk->addr[pd->didx], af); - if (pd->sport) - *pd->sport = sk->port[pd->sidx]; - if (pd->dport) - *pd->dport = sk->port[pd->didx]; - if (pd->proto_sum) - *pd->proto_sum = bproto_sum; - if (pd->ip_sum) - *pd->ip_sum = bip_sum; - m_copyback(m, off, hdrlen, pd->hdr.any); - } - if (pd->proto == IPPROTO_TCP && - ((r->rule_flag & PFRULE_RETURNRST) || - (r->rule_flag & PFRULE_RETURN)) && - !(th->th_flags & TH_RST)) { - u_int32_t ack = ntohl(th->th_seq) + pd->p_len; - int len = 0; -#ifdef INET - struct ip *h4; -#endif -#ifdef INET6 - struct ip6_hdr *h6; -#endif - - switch (af) { -#ifdef INET - case AF_INET: - h4 = mtod(m, struct ip *); - len = ntohs(h4->ip_len) - off; - break; -#endif -#ifdef INET6 - case AF_INET6: - h6 = mtod(m, struct ip6_hdr *); - len = ntohs(h6->ip6_plen) - (off - sizeof(*h6)); - break; -#endif - } - - if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, af)) - REASON_SET(&reason, PFRES_PROTCKSUM); - else { - if (th->th_flags & TH_SYN) - ack++; - if (th->th_flags & TH_FIN) - ack++; - pf_send_tcp(m, r, af, pd->dst, - pd->src, th->th_dport, th->th_sport, - ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, - r->return_ttl, 1, 0, kif->pfik_ifp); - } - } else if (pd->proto != IPPROTO_ICMP && af == AF_INET && - r->return_icmp) - pf_send_icmp(m, r->return_icmp >> 8, - r->return_icmp & 255, af, r); - else if (pd->proto != IPPROTO_ICMPV6 && af == AF_INET6 && - r->return_icmp6) - pf_send_icmp(m, r->return_icmp6 >> 8, - r->return_icmp6 & 255, af, r); - } - - if (r->action == PF_DROP) - goto cleanup; - - if (tag > 0 && pf_tag_packet(m, pd, tag)) { - REASON_SET(&reason, PFRES_MEMORY); - goto cleanup; - } - if (rtableid >= 0) - M_SETFIB(m, rtableid); - - if (!state_icmp && (r->keep_state || nr != NULL || - (pd->flags & PFDESC_TCP_NORM))) { - int action; - action = pf_create_state(r, nr, a, pd, nsn, nk, sk, m, off, - sport, dport, &rewrite, kif, sm, tag, bproto_sum, bip_sum, - hdrlen); - if (action != PF_PASS) - return (action); - } else { - if (sk != NULL) - uma_zfree(V_pf_state_key_z, sk); - if (nk != NULL) - uma_zfree(V_pf_state_key_z, nk); - } - - /* copy back packet headers if we performed NAT operations */ - if (rewrite) - m_copyback(m, off, hdrlen, pd->hdr.any); - - if (*sm != NULL && !((*sm)->state_flags & PFSTATE_NOSYNC) && - direction == PF_OUT && - pfsync_defer_ptr != NULL && pfsync_defer_ptr(*sm, m)) - /* - * We want the state created, but we dont - * want to send this in case a partner - * firewall has to know about it to allow - * replies through it. - */ - return (PF_DEFER); - - return (PF_PASS); - -cleanup: - if (sk != NULL) - uma_zfree(V_pf_state_key_z, sk); - if (nk != NULL) - uma_zfree(V_pf_state_key_z, nk); - return (PF_DROP); -} - -static int -pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, - struct pf_pdesc *pd, struct pf_src_node *nsn, struct pf_state_key *nk, - struct pf_state_key *sk, struct mbuf *m, int off, u_int16_t sport, - u_int16_t dport, int *rewrite, struct pfi_kif *kif, struct pf_state **sm, - int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen) -{ - struct pf_state *s = NULL; - struct pf_src_node *sn = NULL; - struct tcphdr *th = pd->hdr.tcp; - u_int16_t mss = V_tcp_mssdflt; - u_short reason; - - /* check maximums */ - if (r->max_states && (r->states_cur >= r->max_states)) { - V_pf_status.lcounters[LCNT_STATES]++; - REASON_SET(&reason, PFRES_MAXSTATES); - return (PF_DROP); - } - /* src node for filter rule */ - if ((r->rule_flag & PFRULE_SRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) && - pf_insert_src_node(&sn, r, pd->src, pd->af) != 0) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto csfailed; - } - /* src node for translation rule */ - if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && - pf_insert_src_node(&nsn, nr, &sk->addr[pd->sidx], pd->af)) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto csfailed; - } - s = uma_zalloc(V_pf_state_z, M_NOWAIT | M_ZERO); - if (s == NULL) { - REASON_SET(&reason, PFRES_MEMORY); - goto csfailed; - } - s->rule.ptr = r; - s->nat_rule.ptr = nr; - s->anchor.ptr = a; - STATE_INC_COUNTERS(s); - if (r->allow_opts) - s->state_flags |= PFSTATE_ALLOWOPTS; - if (r->rule_flag & PFRULE_STATESLOPPY) - s->state_flags |= PFSTATE_SLOPPY; - s->log = r->log & PF_LOG_ALL; - s->sync_state = PFSYNC_S_NONE; - if (nr != NULL) - s->log |= nr->log & PF_LOG_ALL; - switch (pd->proto) { - case IPPROTO_TCP: - s->src.seqlo = ntohl(th->th_seq); - s->src.seqhi = s->src.seqlo + pd->p_len + 1; - if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && - r->keep_state == PF_STATE_MODULATE) { - /* Generate sequence number modulator */ - if ((s->src.seqdiff = pf_tcp_iss(pd) - s->src.seqlo) == - 0) - s->src.seqdiff = 1; - pf_change_a(&th->th_seq, &th->th_sum, - htonl(s->src.seqlo + s->src.seqdiff), 0); - *rewrite = 1; - } else - s->src.seqdiff = 0; - if (th->th_flags & TH_SYN) { - s->src.seqhi++; - s->src.wscale = pf_get_wscale(m, off, - th->th_off, pd->af); - } - s->src.max_win = MAX(ntohs(th->th_win), 1); - if (s->src.wscale & PF_WSCALE_MASK) { - /* Remove scale factor from initial window */ - int win = s->src.max_win; - win += 1 << (s->src.wscale & PF_WSCALE_MASK); - s->src.max_win = (win - 1) >> - (s->src.wscale & PF_WSCALE_MASK); - } - if (th->th_flags & TH_FIN) - s->src.seqhi++; - s->dst.seqhi = 1; - s->dst.max_win = 1; - s->src.state = TCPS_SYN_SENT; - s->dst.state = TCPS_CLOSED; - s->timeout = PFTM_TCP_FIRST_PACKET; - break; - case IPPROTO_UDP: - s->src.state = PFUDPS_SINGLE; - s->dst.state = PFUDPS_NO_TRAFFIC; - s->timeout = PFTM_UDP_FIRST_PACKET; - break; - case IPPROTO_ICMP: -#ifdef INET6 - case IPPROTO_ICMPV6: -#endif - s->timeout = PFTM_ICMP_FIRST_PACKET; - break; - default: - s->src.state = PFOTHERS_SINGLE; - s->dst.state = PFOTHERS_NO_TRAFFIC; - s->timeout = PFTM_OTHER_FIRST_PACKET; - } - - s->creation = time_uptime; - s->expire = time_uptime; - - if (sn != NULL) { - s->src_node = sn; - s->src_node->states++; - } - if (nsn != NULL) { - /* XXX We only modify one side for now. */ - PF_ACPY(&nsn->raddr, &nk->addr[1], pd->af); - s->nat_src_node = nsn; - s->nat_src_node->states++; - } - if (pd->proto == IPPROTO_TCP) { - if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, - off, pd, th, &s->src, &s->dst)) { - REASON_SET(&reason, PFRES_MEMORY); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - uma_zfree(V_pf_state_z, s); - return (PF_DROP); - } - if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub && - pf_normalize_tcp_stateful(m, off, pd, &reason, th, s, - &s->src, &s->dst, rewrite)) { - /* This really shouldn't happen!!! */ - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_normalize_tcp_stateful failed on first pkt")); - pf_normalize_tcp_cleanup(s); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - uma_zfree(V_pf_state_z, s); - return (PF_DROP); - } - } - s->direction = pd->dir; - - /* - * sk/nk could already been setup by pf_get_translation(). - */ - if (nr == NULL) { - KASSERT((sk == NULL && nk == NULL), ("%s: nr %p sk %p, nk %p", - __func__, nr, sk, nk)); - sk = pf_state_key_setup(pd, pd->src, pd->dst, sport, dport); - if (sk == NULL) - goto csfailed; - nk = sk; - } else - KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", - __func__, nr, sk, nk)); - - /* Swap sk/nk for PF_OUT. */ - if (pf_state_insert(BOUND_IFACE(r, kif), - (pd->dir == PF_IN) ? sk : nk, - (pd->dir == PF_IN) ? nk : sk, s)) { - if (pd->proto == IPPROTO_TCP) - pf_normalize_tcp_cleanup(s); - REASON_SET(&reason, PFRES_STATEINS); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - uma_zfree(V_pf_state_z, s); - return (PF_DROP); - } else - *sm = s; - - pf_set_rt_ifp(s, pd->src); /* needs s->state_key set */ - if (tag > 0) - s->tag = tag; - if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) == - TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { - s->src.state = PF_TCPS_PROXY_SRC; - /* undo NAT changes, if they have taken place */ - if (nr != NULL) { - struct pf_state_key *skt = s->key[PF_SK_WIRE]; - if (pd->dir == PF_OUT) - skt = s->key[PF_SK_STACK]; - PF_ACPY(pd->src, &skt->addr[pd->sidx], pd->af); - PF_ACPY(pd->dst, &skt->addr[pd->didx], pd->af); - if (pd->sport) - *pd->sport = skt->port[pd->sidx]; - if (pd->dport) - *pd->dport = skt->port[pd->didx]; - if (pd->proto_sum) - *pd->proto_sum = bproto_sum; - if (pd->ip_sum) - *pd->ip_sum = bip_sum; - m_copyback(m, off, hdrlen, pd->hdr.any); - } - s->src.seqhi = htonl(arc4random()); - /* Find mss option */ - int rtid = M_GETFIB(m); - mss = pf_get_mss(m, off, th->th_off, pd->af); - mss = pf_calc_mss(pd->src, pd->af, rtid, mss); - mss = pf_calc_mss(pd->dst, pd->af, rtid, mss); - s->src.mss = mss; - pf_send_tcp(NULL, r, pd->af, pd->dst, pd->src, th->th_dport, - th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL); - REASON_SET(&reason, PFRES_SYNPROXY); - return (PF_SYNPROXY_DROP); - } - - return (PF_PASS); - -csfailed: - if (sk != NULL) - uma_zfree(V_pf_state_key_z, sk); - if (nk != NULL) - uma_zfree(V_pf_state_key_z, nk); - - if (sn != NULL && sn->states == 0 && sn->expire == 0) { - pf_remove_src_node(sn); - V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - V_pf_status.src_nodes--; - uma_zfree(V_pf_sources_z, sn); - } - if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { - pf_remove_src_node(nsn); - V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - V_pf_status.src_nodes--; - uma_zfree(V_pf_sources_z, nsn); - } - return (PF_DROP); -} - -static int -pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, - struct mbuf *m, void *h, struct pf_pdesc *pd, struct pf_rule **am, - struct pf_ruleset **rsm) -{ - struct pf_rule *r, *a = NULL; - struct pf_ruleset *ruleset = NULL; - sa_family_t af = pd->af; - u_short reason; - int tag = -1; - int asd = 0; - int match = 0; - - PF_RULES_RASSERT(); - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, - r->src.neg, kif, M_GETFIB(m))) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, - r->dst.neg, NULL, M_GETFIB(m))) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (r->tos && !(r->tos == pd->tos)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY) - r = TAILQ_NEXT(r, entries); - else if (pd->proto == IPPROTO_UDP && - (r->src.port_op || r->dst.port_op)) - r = TAILQ_NEXT(r, entries); - else if (pd->proto == IPPROTO_TCP && - (r->src.port_op || r->dst.port_op || r->flagset)) - r = TAILQ_NEXT(r, entries); - else if ((pd->proto == IPPROTO_ICMP || - pd->proto == IPPROTO_ICMPV6) && - (r->type || r->code)) - r = TAILQ_NEXT(r, entries); - else if (r->prob && r->prob <= - (arc4random() % (UINT_MAX - 1) + 1)) - r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, &tag, - pd->pf_mtag ? pd->pf_mtag->tag : 0)) - r = TAILQ_NEXT(r, entries); - else { - if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; - if ((*rm)->quick) - break; - r = TAILQ_NEXT(r, entries); - } else - pf_step_into_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match); - } - if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match)) - break; - } - r = *rm; - a = *am; - ruleset = *rsm; - - REASON_SET(&reason, PFRES_MATCH); - - if (r->log) - PFLOG_PACKET(kif, m, af, direction, reason, r, a, ruleset, pd, - 1); - - if (r->action != PF_PASS) - return (PF_DROP); - - if (tag > 0 && pf_tag_packet(m, pd, tag)) { - REASON_SET(&reason, PFRES_MEMORY); - return (PF_DROP); - } - - return (PF_PASS); -} - -static int -pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, - struct pf_state **state, struct pfi_kif *kif, struct mbuf *m, int off, - struct pf_pdesc *pd, u_short *reason, int *copyback) -{ - struct tcphdr *th = pd->hdr.tcp; - u_int16_t win = ntohs(th->th_win); - u_int32_t ack, end, seq, orig_seq; - u_int8_t sws, dws; - int ackskew; - - if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) { - sws = src->wscale & PF_WSCALE_MASK; - dws = dst->wscale & PF_WSCALE_MASK; - } else - sws = dws = 0; - - /* - * Sequence tracking algorithm from Guido van Rooij's paper: - * http://www.madison-gurkha.com/publications/tcp_filtering/ - * tcp_filtering.ps - */ - - orig_seq = seq = ntohl(th->th_seq); - if (src->seqlo == 0) { - /* First packet from this end. Set its state */ - - if ((pd->flags & PFDESC_TCP_NORM || dst->scrub) && - src->scrub == NULL) { - if (pf_normalize_tcp_init(m, off, pd, th, src, dst)) { - REASON_SET(reason, PFRES_MEMORY); - return (PF_DROP); - } - } - - /* Deferred generation of sequence number modulator */ - if (dst->seqdiff && !src->seqdiff) { - /* use random iss for the TCP server */ - while ((src->seqdiff = arc4random() - seq) == 0) - ; - ack = ntohl(th->th_ack) - dst->seqdiff; - pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + - src->seqdiff), 0); - pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); - *copyback = 1; - } else { - ack = ntohl(th->th_ack); - } - - end = seq + pd->p_len; - if (th->th_flags & TH_SYN) { - end++; - if (dst->wscale & PF_WSCALE_FLAG) { - src->wscale = pf_get_wscale(m, off, th->th_off, - pd->af); - if (src->wscale & PF_WSCALE_FLAG) { - /* Remove scale factor from initial - * window */ - sws = src->wscale & PF_WSCALE_MASK; - win = ((u_int32_t)win + (1 << sws) - 1) - >> sws; - dws = dst->wscale & PF_WSCALE_MASK; - } else { - /* fixup other window */ - dst->max_win <<= dst->wscale & - PF_WSCALE_MASK; - /* in case of a retrans SYN|ACK */ - dst->wscale = 0; - } - } - } - if (th->th_flags & TH_FIN) - end++; - - src->seqlo = seq; - if (src->state < TCPS_SYN_SENT) - src->state = TCPS_SYN_SENT; - - /* - * May need to slide the window (seqhi may have been set by - * the crappy stack check or if we picked up the connection - * after establishment) - */ - if (src->seqhi == 1 || - SEQ_GEQ(end + MAX(1, dst->max_win << dws), src->seqhi)) - src->seqhi = end + MAX(1, dst->max_win << dws); - if (win > src->max_win) - src->max_win = win; - - } else { - ack = ntohl(th->th_ack) - dst->seqdiff; - if (src->seqdiff) { - /* Modulate sequence numbers */ - pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + - src->seqdiff), 0); - pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); - *copyback = 1; - } - end = seq + pd->p_len; - if (th->th_flags & TH_SYN) - end++; - if (th->th_flags & TH_FIN) - end++; - } - - if ((th->th_flags & TH_ACK) == 0) { - /* Let it pass through the ack skew check */ - ack = dst->seqlo; - } else if ((ack == 0 && - (th->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) || - /* broken tcp stacks do not set ack */ - (dst->state < TCPS_SYN_SENT)) { - /* - * Many stacks (ours included) will set the ACK number in an - * FIN|ACK if the SYN times out -- no sequence to ACK. - */ - ack = dst->seqlo; - } - - if (seq == end) { - /* Ease sequencing restrictions on no data packets */ - seq = src->seqlo; - end = seq; - } - - ackskew = dst->seqlo - ack; - - - /* - * Need to demodulate the sequence numbers in any TCP SACK options - * (Selective ACK). We could optionally validate the SACK values - * against the current ACK window, either forwards or backwards, but - * I'm not confident that SACK has been implemented properly - * everywhere. It wouldn't surprise me if several stacks accidently - * SACK too far backwards of previously ACKed data. There really aren't - * any security implications of bad SACKing unless the target stack - * doesn't validate the option length correctly. Someone trying to - * spoof into a TCP connection won't bother blindly sending SACK - * options anyway. - */ - if (dst->seqdiff && (th->th_off << 2) > sizeof(struct tcphdr)) { - if (pf_modulate_sack(m, off, pd, th, dst)) - *copyback = 1; - } - - -#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */ - if (SEQ_GEQ(src->seqhi, end) && - /* Last octet inside other's window space */ - SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) && - /* Retrans: not more than one window back */ - (ackskew >= -MAXACKWINDOW) && - /* Acking not more than one reassembled fragment backwards */ - (ackskew <= (MAXACKWINDOW << sws)) && - /* Acking not more than one window forward */ - ((th->th_flags & TH_RST) == 0 || orig_seq == src->seqlo || - (orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo) || - (pd->flags & PFDESC_IP_REAS) == 0)) { - /* Require an exact/+1 sequence match on resets when possible */ - - if (dst->scrub || src->scrub) { - if (pf_normalize_tcp_stateful(m, off, pd, reason, th, - *state, src, dst, copyback)) - return (PF_DROP); - } - - /* update max window */ - if (src->max_win < win) - src->max_win = win; - /* synchronize sequencing */ - if (SEQ_GT(end, src->seqlo)) - src->seqlo = end; - /* slide the window of what the other end can send */ - if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) - dst->seqhi = ack + MAX((win << sws), 1); - - - /* update states */ - if (th->th_flags & TH_SYN) - if (src->state < TCPS_SYN_SENT) - src->state = TCPS_SYN_SENT; - if (th->th_flags & TH_FIN) - if (src->state < TCPS_CLOSING) - src->state = TCPS_CLOSING; - if (th->th_flags & TH_ACK) { - if (dst->state == TCPS_SYN_SENT) { - dst->state = TCPS_ESTABLISHED; - if (src->state == TCPS_ESTABLISHED && - (*state)->src_node != NULL && - pf_src_connlimit(state)) { - REASON_SET(reason, PFRES_SRCLIMIT); - return (PF_DROP); - } - } else if (dst->state == TCPS_CLOSING) - dst->state = TCPS_FIN_WAIT_2; - } - if (th->th_flags & TH_RST) - src->state = dst->state = TCPS_TIME_WAIT; - - /* update expire time */ - (*state)->expire = time_uptime; - if (src->state >= TCPS_FIN_WAIT_2 && - dst->state >= TCPS_FIN_WAIT_2) - (*state)->timeout = PFTM_TCP_CLOSED; - else if (src->state >= TCPS_CLOSING && - dst->state >= TCPS_CLOSING) - (*state)->timeout = PFTM_TCP_FIN_WAIT; - else if (src->state < TCPS_ESTABLISHED || - dst->state < TCPS_ESTABLISHED) - (*state)->timeout = PFTM_TCP_OPENING; - else if (src->state >= TCPS_CLOSING || - dst->state >= TCPS_CLOSING) - (*state)->timeout = PFTM_TCP_CLOSING; - else - (*state)->timeout = PFTM_TCP_ESTABLISHED; - - /* Fall through to PASS packet */ - - } else if ((dst->state < TCPS_SYN_SENT || - dst->state >= TCPS_FIN_WAIT_2 || - src->state >= TCPS_FIN_WAIT_2) && - SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) && - /* Within a window forward of the originating packet */ - SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW)) { - /* Within a window backward of the originating packet */ - - /* - * This currently handles three situations: - * 1) Stupid stacks will shotgun SYNs before their peer - * replies. - * 2) When PF catches an already established stream (the - * firewall rebooted, the state table was flushed, routes - * changed...) - * 3) Packets get funky immediately after the connection - * closes (this should catch Solaris spurious ACK|FINs - * that web servers like to spew after a close) - * - * This must be a little more careful than the above code - * since packet floods will also be caught here. We don't - * update the TTL here to mitigate the damage of a packet - * flood and so the same code can handle awkward establishment - * and a loosened connection close. - * In the establishment case, a correct peer response will - * validate the connection, go through the normal state code - * and keep updating the state TTL. - */ - - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: loose state match: "); - pf_print_state(*state); - pf_print_flags(th->th_flags); - printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " - "pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack, - pd->p_len, ackskew, (unsigned long long)(*state)->packets[0], - (unsigned long long)(*state)->packets[1], - pd->dir == PF_IN ? "in" : "out", - pd->dir == (*state)->direction ? "fwd" : "rev"); - } - - if (dst->scrub || src->scrub) { - if (pf_normalize_tcp_stateful(m, off, pd, reason, th, - *state, src, dst, copyback)) - return (PF_DROP); - } - - /* update max window */ - if (src->max_win < win) - src->max_win = win; - /* synchronize sequencing */ - if (SEQ_GT(end, src->seqlo)) - src->seqlo = end; - /* slide the window of what the other end can send */ - if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) - dst->seqhi = ack + MAX((win << sws), 1); - - /* - * Cannot set dst->seqhi here since this could be a shotgunned - * SYN and not an already established connection. - */ - - if (th->th_flags & TH_FIN) - if (src->state < TCPS_CLOSING) - src->state = TCPS_CLOSING; - if (th->th_flags & TH_RST) - src->state = dst->state = TCPS_TIME_WAIT; - - /* Fall through to PASS packet */ - - } else { - if ((*state)->dst.state == TCPS_SYN_SENT && - (*state)->src.state == TCPS_SYN_SENT) { - /* Send RST for state mismatches during handshake */ - if (!(th->th_flags & TH_RST)) - pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, - pd->dst, pd->src, th->th_dport, - th->th_sport, ntohl(th->th_ack), 0, - TH_RST, 0, 0, - (*state)->rule.ptr->return_ttl, 1, 0, - kif->pfik_ifp); - src->seqlo = 0; - src->seqhi = 1; - src->max_win = 1; - } else if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: BAD state: "); - pf_print_state(*state); - pf_print_flags(th->th_flags); - printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " - "pkts=%llu:%llu dir=%s,%s\n", - seq, orig_seq, ack, pd->p_len, ackskew, - (unsigned long long)(*state)->packets[0], - (unsigned long long)(*state)->packets[1], - pd->dir == PF_IN ? "in" : "out", - pd->dir == (*state)->direction ? "fwd" : "rev"); - printf("pf: State failure on: %c %c %c %c | %c %c\n", - SEQ_GEQ(src->seqhi, end) ? ' ' : '1', - SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ? - ' ': '2', - (ackskew >= -MAXACKWINDOW) ? ' ' : '3', - (ackskew <= (MAXACKWINDOW << sws)) ? ' ' : '4', - SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) ?' ' :'5', - SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW) ?' ' :'6'); - } - REASON_SET(reason, PFRES_BADSTATE); - return (PF_DROP); - } - - return (PF_PASS); -} - -static int -pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst, - struct pf_state **state, struct pf_pdesc *pd, u_short *reason) -{ - struct tcphdr *th = pd->hdr.tcp; - - if (th->th_flags & TH_SYN) - if (src->state < TCPS_SYN_SENT) - src->state = TCPS_SYN_SENT; - if (th->th_flags & TH_FIN) - if (src->state < TCPS_CLOSING) - src->state = TCPS_CLOSING; - if (th->th_flags & TH_ACK) { - if (dst->state == TCPS_SYN_SENT) { - dst->state = TCPS_ESTABLISHED; - if (src->state == TCPS_ESTABLISHED && - (*state)->src_node != NULL && - pf_src_connlimit(state)) { - REASON_SET(reason, PFRES_SRCLIMIT); - return (PF_DROP); - } - } else if (dst->state == TCPS_CLOSING) { - dst->state = TCPS_FIN_WAIT_2; - } else if (src->state == TCPS_SYN_SENT && - dst->state < TCPS_SYN_SENT) { - /* - * Handle a special sloppy case where we only see one - * half of the connection. If there is a ACK after - * the initial SYN without ever seeing a packet from - * the destination, set the connection to established. - */ - dst->state = src->state = TCPS_ESTABLISHED; - if ((*state)->src_node != NULL && - pf_src_connlimit(state)) { - REASON_SET(reason, PFRES_SRCLIMIT); - return (PF_DROP); - } - } else if (src->state == TCPS_CLOSING && - dst->state == TCPS_ESTABLISHED && - dst->seqlo == 0) { - /* - * Handle the closing of half connections where we - * don't see the full bidirectional FIN/ACK+ACK - * handshake. - */ - dst->state = TCPS_CLOSING; - } - } - if (th->th_flags & TH_RST) - src->state = dst->state = TCPS_TIME_WAIT; - - /* update expire time */ - (*state)->expire = time_uptime; - if (src->state >= TCPS_FIN_WAIT_2 && - dst->state >= TCPS_FIN_WAIT_2) - (*state)->timeout = PFTM_TCP_CLOSED; - else if (src->state >= TCPS_CLOSING && - dst->state >= TCPS_CLOSING) - (*state)->timeout = PFTM_TCP_FIN_WAIT; - else if (src->state < TCPS_ESTABLISHED || - dst->state < TCPS_ESTABLISHED) - (*state)->timeout = PFTM_TCP_OPENING; - else if (src->state >= TCPS_CLOSING || - dst->state >= TCPS_CLOSING) - (*state)->timeout = PFTM_TCP_CLOSING; - else - (*state)->timeout = PFTM_TCP_ESTABLISHED; - - return (PF_PASS); -} - -static int -pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, - struct mbuf *m, int off, void *h, struct pf_pdesc *pd, - u_short *reason) -{ - struct pf_state_key_cmp key; - struct tcphdr *th = pd->hdr.tcp; - int copyback = 0; - struct pf_state_peer *src, *dst; - struct pf_state_key *sk; - - bzero(&key, sizeof(key)); - key.af = pd->af; - key.proto = IPPROTO_TCP; - if (direction == PF_IN) { /* wire side, straight */ - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - key.port[0] = th->th_sport; - key.port[1] = th->th_dport; - } else { /* stack side, reverse */ - PF_ACPY(&key.addr[1], pd->src, key.af); - PF_ACPY(&key.addr[0], pd->dst, key.af); - key.port[1] = th->th_sport; - key.port[0] = th->th_dport; - } - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - if (direction == (*state)->direction) { - src = &(*state)->src; - dst = &(*state)->dst; - } else { - src = &(*state)->dst; - dst = &(*state)->src; - } - - sk = (*state)->key[pd->didx]; - - if ((*state)->src.state == PF_TCPS_PROXY_SRC) { - if (direction != (*state)->direction) { - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_SYNPROXY_DROP); - } - if (th->th_flags & TH_SYN) { - if (ntohl(th->th_seq) != (*state)->src.seqlo) { - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_DROP); - } - pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, - pd->src, th->th_dport, th->th_sport, - (*state)->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1, 0, NULL); - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_SYNPROXY_DROP); - } else if (!(th->th_flags & TH_ACK) || - (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || - (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) { - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_DROP); - } else if ((*state)->src_node != NULL && - pf_src_connlimit(state)) { - REASON_SET(reason, PFRES_SRCLIMIT); - return (PF_DROP); - } else - (*state)->src.state = PF_TCPS_PROXY_DST; - } - if ((*state)->src.state == PF_TCPS_PROXY_DST) { - if (direction == (*state)->direction) { - if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) || - (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || - (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) { - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_DROP); - } - (*state)->src.max_win = MAX(ntohs(th->th_win), 1); - if ((*state)->dst.seqhi == 1) - (*state)->dst.seqhi = htonl(arc4random()); - pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, - &sk->addr[pd->sidx], &sk->addr[pd->didx], - sk->port[pd->sidx], sk->port[pd->didx], - (*state)->dst.seqhi, 0, TH_SYN, 0, - (*state)->src.mss, 0, 0, (*state)->tag, NULL); - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_SYNPROXY_DROP); - } else if (((th->th_flags & (TH_SYN|TH_ACK)) != - (TH_SYN|TH_ACK)) || - (ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) { - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_DROP); - } else { - (*state)->dst.max_win = MAX(ntohs(th->th_win), 1); - (*state)->dst.seqlo = ntohl(th->th_seq); - pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, - pd->src, th->th_dport, th->th_sport, - ntohl(th->th_ack), ntohl(th->th_seq) + 1, - TH_ACK, (*state)->src.max_win, 0, 0, 0, - (*state)->tag, NULL); - pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, - &sk->addr[pd->sidx], &sk->addr[pd->didx], - sk->port[pd->sidx], sk->port[pd->didx], - (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, - TH_ACK, (*state)->dst.max_win, 0, 0, 1, 0, NULL); - (*state)->src.seqdiff = (*state)->dst.seqhi - - (*state)->src.seqlo; - (*state)->dst.seqdiff = (*state)->src.seqhi - - (*state)->dst.seqlo; - (*state)->src.seqhi = (*state)->src.seqlo + - (*state)->dst.max_win; - (*state)->dst.seqhi = (*state)->dst.seqlo + - (*state)->src.max_win; - (*state)->src.wscale = (*state)->dst.wscale = 0; - (*state)->src.state = (*state)->dst.state = - TCPS_ESTABLISHED; - REASON_SET(reason, PFRES_SYNPROXY); - return (PF_SYNPROXY_DROP); - } - } - - if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) && - dst->state >= TCPS_FIN_WAIT_2 && - src->state >= TCPS_FIN_WAIT_2) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: state reuse "); - pf_print_state(*state); - pf_print_flags(th->th_flags); - printf("\n"); - } - /* XXX make sure it's the same direction ?? */ - (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; - pf_unlink_state(*state, PF_ENTER_LOCKED); - *state = NULL; - return (PF_DROP); - } - - if ((*state)->state_flags & PFSTATE_SLOPPY) { - if (pf_tcp_track_sloppy(src, dst, state, pd, reason) == PF_DROP) - return (PF_DROP); - } else { - if (pf_tcp_track_full(src, dst, state, kif, m, off, pd, reason, - ©back) == PF_DROP) - return (PF_DROP); - } - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; - - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || - nk->port[pd->sidx] != th->th_sport) - pf_change_ap(pd->src, &th->th_sport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 0, pd->af); - - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || - nk->port[pd->didx] != th->th_dport) - pf_change_ap(pd->dst, &th->th_dport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 0, pd->af); - copyback = 1; - } - - /* Copyback sequence modulation or stateful scrub changes if needed */ - if (copyback) - m_copyback(m, off, sizeof(*th), (caddr_t)th); - - return (PF_PASS); -} - -static int -pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, - struct mbuf *m, int off, void *h, struct pf_pdesc *pd) -{ - struct pf_state_peer *src, *dst; - struct pf_state_key_cmp key; - struct udphdr *uh = pd->hdr.udp; - - bzero(&key, sizeof(key)); - key.af = pd->af; - key.proto = IPPROTO_UDP; - if (direction == PF_IN) { /* wire side, straight */ - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - key.port[0] = uh->uh_sport; - key.port[1] = uh->uh_dport; - } else { /* stack side, reverse */ - PF_ACPY(&key.addr[1], pd->src, key.af); - PF_ACPY(&key.addr[0], pd->dst, key.af); - key.port[1] = uh->uh_sport; - key.port[0] = uh->uh_dport; - } - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - if (direction == (*state)->direction) { - src = &(*state)->src; - dst = &(*state)->dst; - } else { - src = &(*state)->dst; - dst = &(*state)->src; - } - - /* update states */ - if (src->state < PFUDPS_SINGLE) - src->state = PFUDPS_SINGLE; - if (dst->state == PFUDPS_SINGLE) - dst->state = PFUDPS_MULTIPLE; - - /* update expire time */ - (*state)->expire = time_uptime; - if (src->state == PFUDPS_MULTIPLE && dst->state == PFUDPS_MULTIPLE) - (*state)->timeout = PFTM_UDP_MULTIPLE; - else - (*state)->timeout = PFTM_UDP_SINGLE; - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; - - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || - nk->port[pd->sidx] != uh->uh_sport) - pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); - - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || - nk->port[pd->didx] != uh->uh_dport) - pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); - m_copyback(m, off, sizeof(*uh), (caddr_t)uh); - } - - return (PF_PASS); -} - -static int -pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, - struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) -{ - struct pf_addr *saddr = pd->src, *daddr = pd->dst; - u_int16_t icmpid = 0, *icmpsum; - u_int8_t icmptype; - int state_icmp = 0; - struct pf_state_key_cmp key; - - bzero(&key, sizeof(key)); - switch (pd->proto) { -#ifdef INET - case IPPROTO_ICMP: - icmptype = pd->hdr.icmp->icmp_type; - icmpid = pd->hdr.icmp->icmp_id; - icmpsum = &pd->hdr.icmp->icmp_cksum; - - if (icmptype == ICMP_UNREACH || - icmptype == ICMP_SOURCEQUENCH || - icmptype == ICMP_REDIRECT || - icmptype == ICMP_TIMXCEED || - icmptype == ICMP_PARAMPROB) - state_icmp++; - break; -#endif /* INET */ -#ifdef INET6 - case IPPROTO_ICMPV6: - icmptype = pd->hdr.icmp6->icmp6_type; - icmpid = pd->hdr.icmp6->icmp6_id; - icmpsum = &pd->hdr.icmp6->icmp6_cksum; - - if (icmptype == ICMP6_DST_UNREACH || - icmptype == ICMP6_PACKET_TOO_BIG || - icmptype == ICMP6_TIME_EXCEEDED || - icmptype == ICMP6_PARAM_PROB) - state_icmp++; - break; -#endif /* INET6 */ - } - - if (!state_icmp) { - - /* - * ICMP query/reply message not related to a TCP/UDP packet. - * Search for an ICMP state. - */ - key.af = pd->af; - key.proto = pd->proto; - key.port[0] = key.port[1] = icmpid; - if (direction == PF_IN) { /* wire side, straight */ - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - } else { /* stack side, reverse */ - PF_ACPY(&key.addr[1], pd->src, key.af); - PF_ACPY(&key.addr[0], pd->dst, key.af); - } - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - (*state)->expire = time_uptime; - (*state)->timeout = PFTM_ICMP_ERROR_REPLY; - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; - - switch (pd->af) { -#ifdef INET - case AF_INET: - if (PF_ANEQ(pd->src, - &nk->addr[pd->sidx], AF_INET)) - pf_change_a(&saddr->v4.s_addr, - pd->ip_sum, - nk->addr[pd->sidx].v4.s_addr, 0); - - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], - AF_INET)) - pf_change_a(&daddr->v4.s_addr, - pd->ip_sum, - nk->addr[pd->didx].v4.s_addr, 0); - - if (nk->port[0] != - pd->hdr.icmp->icmp_id) { - pd->hdr.icmp->icmp_cksum = - pf_cksum_fixup( - pd->hdr.icmp->icmp_cksum, icmpid, - nk->port[pd->sidx], 0); - pd->hdr.icmp->icmp_id = - nk->port[pd->sidx]; - } - - m_copyback(m, off, ICMP_MINLEN, - (caddr_t )pd->hdr.icmp); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (PF_ANEQ(pd->src, - &nk->addr[pd->sidx], AF_INET6)) - pf_change_a6(saddr, - &pd->hdr.icmp6->icmp6_cksum, - &nk->addr[pd->sidx], 0); - - if (PF_ANEQ(pd->dst, - &nk->addr[pd->didx], AF_INET6)) - pf_change_a6(daddr, - &pd->hdr.icmp6->icmp6_cksum, - &nk->addr[pd->didx], 0); - - m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t )pd->hdr.icmp6); - break; -#endif /* INET6 */ - } - } - return (PF_PASS); - - } else { - /* - * ICMP error message in response to a TCP/UDP packet. - * Extract the inner TCP/UDP header and search for that state. - */ - - struct pf_pdesc pd2; - bzero(&pd2, sizeof pd2); -#ifdef INET - struct ip h2; -#endif /* INET */ -#ifdef INET6 - struct ip6_hdr h2_6; - int terminal = 0; -#endif /* INET6 */ - int ipoff2 = 0; - int off2 = 0; - - pd2.af = pd->af; - /* Payload packet is from the opposite direction. */ - pd2.sidx = (direction == PF_IN) ? 1 : 0; - pd2.didx = (direction == PF_IN) ? 0 : 1; - switch (pd->af) { -#ifdef INET - case AF_INET: - /* offset of h2 in mbuf chain */ - ipoff2 = off + ICMP_MINLEN; - - if (!pf_pull_hdr(m, ipoff2, &h2, sizeof(h2), - NULL, reason, pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMP error message too short " - "(ip)\n")); - return (PF_DROP); - } - /* - * ICMP error messages don't refer to non-first - * fragments - */ - if (h2.ip_off & htons(IP_OFFMASK)) { - REASON_SET(reason, PFRES_FRAG); - return (PF_DROP); - } - - /* offset of protocol header that follows h2 */ - off2 = ipoff2 + (h2.ip_hl << 2); - - pd2.proto = h2.ip_p; - pd2.src = (struct pf_addr *)&h2.ip_src; - pd2.dst = (struct pf_addr *)&h2.ip_dst; - pd2.ip_sum = &h2.ip_sum; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - ipoff2 = off + sizeof(struct icmp6_hdr); - - if (!pf_pull_hdr(m, ipoff2, &h2_6, sizeof(h2_6), - NULL, reason, pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMP error message too short " - "(ip6)\n")); - return (PF_DROP); - } - pd2.proto = h2_6.ip6_nxt; - pd2.src = (struct pf_addr *)&h2_6.ip6_src; - pd2.dst = (struct pf_addr *)&h2_6.ip6_dst; - pd2.ip_sum = NULL; - off2 = ipoff2 + sizeof(h2_6); - do { - switch (pd2.proto) { - case IPPROTO_FRAGMENT: - /* - * ICMPv6 error messages for - * non-first fragments - */ - REASON_SET(reason, PFRES_FRAG); - return (PF_DROP); - case IPPROTO_AH: - case IPPROTO_HOPOPTS: - case IPPROTO_ROUTING: - case IPPROTO_DSTOPTS: { - /* get next header and header length */ - struct ip6_ext opt6; - - if (!pf_pull_hdr(m, off2, &opt6, - sizeof(opt6), NULL, reason, - pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMPv6 short opt\n")); - return (PF_DROP); - } - if (pd2.proto == IPPROTO_AH) - off2 += (opt6.ip6e_len + 2) * 4; - else - off2 += (opt6.ip6e_len + 1) * 8; - pd2.proto = opt6.ip6e_nxt; - /* goto the next header */ - break; - } - default: - terminal++; - break; - } - } while (!terminal); - break; -#endif /* INET6 */ - } - - switch (pd2.proto) { - case IPPROTO_TCP: { - struct tcphdr th; - u_int32_t seq; - struct pf_state_peer *src, *dst; - u_int8_t dws; - int copyback = 0; - - /* - * Only the first 8 bytes of the TCP header can be - * expected. Don't access any TCP header fields after - * th_seq, an ackskew test is not possible. - */ - if (!pf_pull_hdr(m, off2, &th, 8, NULL, reason, - pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMP error message too short " - "(tcp)\n")); - return (PF_DROP); - } - - key.af = pd2.af; - key.proto = IPPROTO_TCP; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[pd2.sidx] = th.th_sport; - key.port[pd2.didx] = th.th_dport; - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - if (direction == (*state)->direction) { - src = &(*state)->dst; - dst = &(*state)->src; - } else { - src = &(*state)->src; - dst = &(*state)->dst; - } - - if (src->wscale && dst->wscale) - dws = dst->wscale & PF_WSCALE_MASK; - else - dws = 0; - - /* Demodulate sequence number */ - seq = ntohl(th.th_seq) - src->seqdiff; - if (src->seqdiff) { - pf_change_a(&th.th_seq, icmpsum, - htonl(seq), 0); - copyback = 1; - } - - if (!((*state)->state_flags & PFSTATE_SLOPPY) && - (!SEQ_GEQ(src->seqhi, seq) || - !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)))) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: BAD ICMP %d:%d ", - icmptype, pd->hdr.icmp->icmp_code); - pf_print_host(pd->src, 0, pd->af); - printf(" -> "); - pf_print_host(pd->dst, 0, pd->af); - printf(" state: "); - pf_print_state(*state); - printf(" seq=%u\n", seq); - } - REASON_SET(reason, PFRES_BADSTATE); - return (PF_DROP); - } else { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: OK ICMP %d:%d ", - icmptype, pd->hdr.icmp->icmp_code); - pf_print_host(pd->src, 0, pd->af); - printf(" -> "); - pf_print_host(pd->dst, 0, pd->af); - printf(" state: "); - pf_print_state(*state); - printf(" seq=%u\n", seq); - } - } - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != - (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; - - if (PF_ANEQ(pd2.src, - &nk->addr[pd2.sidx], pd2.af) || - nk->port[pd2.sidx] != th.th_sport) - pf_change_icmp(pd2.src, &th.th_sport, - daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, pd2.af); - - if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af) || - nk->port[pd2.didx] != th.th_dport) - pf_change_icmp(pd2.dst, &th.th_dport, - NULL, /* XXX Inbound NAT? */ - &nk->addr[pd2.didx], - nk->port[pd2.didx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, pd2.af); - copyback = 1; - } - - if (copyback) { - switch (pd2.af) { -#ifdef INET - case AF_INET: - m_copyback(m, off, ICMP_MINLEN, - (caddr_t )pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), - (caddr_t )&h2); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - m_copyback(m, off, - sizeof(struct icmp6_hdr), - (caddr_t )pd->hdr.icmp6); - m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t )&h2_6); - break; -#endif /* INET6 */ - } - m_copyback(m, off2, 8, (caddr_t)&th); - } - - return (PF_PASS); - break; - } - case IPPROTO_UDP: { - struct udphdr uh; - - if (!pf_pull_hdr(m, off2, &uh, sizeof(uh), - NULL, reason, pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMP error message too short " - "(udp)\n")); - return (PF_DROP); - } - - key.af = pd2.af; - key.proto = IPPROTO_UDP; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[pd2.sidx] = uh.uh_sport; - key.port[pd2.didx] = uh.uh_dport; - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != - (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; - - if (PF_ANEQ(pd2.src, - &nk->addr[pd2.sidx], pd2.af) || - nk->port[pd2.sidx] != uh.uh_sport) - pf_change_icmp(pd2.src, &uh.uh_sport, - daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], &uh.uh_sum, - pd2.ip_sum, icmpsum, - pd->ip_sum, 1, pd2.af); - - if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af) || - nk->port[pd2.didx] != uh.uh_dport) - pf_change_icmp(pd2.dst, &uh.uh_dport, - NULL, /* XXX Inbound NAT? */ - &nk->addr[pd2.didx], - nk->port[pd2.didx], &uh.uh_sum, - pd2.ip_sum, icmpsum, - pd->ip_sum, 1, pd2.af); - - switch (pd2.af) { -#ifdef INET - case AF_INET: - m_copyback(m, off, ICMP_MINLEN, - (caddr_t )pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - m_copyback(m, off, - sizeof(struct icmp6_hdr), - (caddr_t )pd->hdr.icmp6); - m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t )&h2_6); - break; -#endif /* INET6 */ - } - m_copyback(m, off2, sizeof(uh), (caddr_t)&uh); - } - return (PF_PASS); - break; - } -#ifdef INET - case IPPROTO_ICMP: { - struct icmp iih; - - if (!pf_pull_hdr(m, off2, &iih, ICMP_MINLEN, - NULL, reason, pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMP error message too short i" - "(icmp)\n")); - return (PF_DROP); - } - - key.af = pd2.af; - key.proto = IPPROTO_ICMP; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[0] = key.port[1] = iih.icmp_id; - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != - (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; - - if (PF_ANEQ(pd2.src, - &nk->addr[pd2.sidx], pd2.af) || - nk->port[pd2.sidx] != iih.icmp_id) - pf_change_icmp(pd2.src, &iih.icmp_id, - daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, AF_INET); - - if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af) || - nk->port[pd2.didx] != iih.icmp_id) - pf_change_icmp(pd2.dst, &iih.icmp_id, - NULL, /* XXX Inbound NAT? */ - &nk->addr[pd2.didx], - nk->port[pd2.didx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, AF_INET); - - m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); - m_copyback(m, off2, ICMP_MINLEN, (caddr_t)&iih); - } - return (PF_PASS); - break; - } -#endif /* INET */ -#ifdef INET6 - case IPPROTO_ICMPV6: { - struct icmp6_hdr iih; - - if (!pf_pull_hdr(m, off2, &iih, - sizeof(struct icmp6_hdr), NULL, reason, pd2.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: ICMP error message too short " - "(icmp6)\n")); - return (PF_DROP); - } - - key.af = pd2.af; - key.proto = IPPROTO_ICMPV6; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[0] = key.port[1] = iih.icmp6_id; - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != - (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; - - if (PF_ANEQ(pd2.src, - &nk->addr[pd2.sidx], pd2.af) || - nk->port[pd2.sidx] != iih.icmp6_id) - pf_change_icmp(pd2.src, &iih.icmp6_id, - daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, AF_INET6); - - if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af) || - nk->port[pd2.didx] != iih.icmp6_id) - pf_change_icmp(pd2.dst, &iih.icmp6_id, - NULL, /* XXX Inbound NAT? */ - &nk->addr[pd2.didx], - nk->port[pd2.didx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, AF_INET6); - - m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); - m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); - m_copyback(m, off2, sizeof(struct icmp6_hdr), - (caddr_t)&iih); - } - return (PF_PASS); - break; - } -#endif /* INET6 */ - default: { - key.af = pd2.af; - key.proto = pd2.proto; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[0] = key.port[1] = 0; - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != - (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; - - if (PF_ANEQ(pd2.src, - &nk->addr[pd2.sidx], pd2.af)) - pf_change_icmp(pd2.src, NULL, daddr, - &nk->addr[pd2.sidx], 0, NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, pd2.af); - - if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af)) - pf_change_icmp(pd2.src, NULL, - NULL, /* XXX Inbound NAT? */ - &nk->addr[pd2.didx], 0, NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, pd2.af); - - switch (pd2.af) { -#ifdef INET - case AF_INET: - m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - m_copyback(m, off, - sizeof(struct icmp6_hdr), - (caddr_t )pd->hdr.icmp6); - m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t )&h2_6); - break; -#endif /* INET6 */ - } - } - return (PF_PASS); - break; - } - } - } -} - -static int -pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, - struct mbuf *m, struct pf_pdesc *pd) -{ - struct pf_state_peer *src, *dst; - struct pf_state_key_cmp key; - - bzero(&key, sizeof(key)); - key.af = pd->af; - key.proto = pd->proto; - if (direction == PF_IN) { - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - key.port[0] = key.port[1] = 0; - } else { - PF_ACPY(&key.addr[1], pd->src, key.af); - PF_ACPY(&key.addr[0], pd->dst, key.af); - key.port[1] = key.port[0] = 0; - } - - STATE_LOOKUP(kif, &key, direction, *state, pd); - - if (direction == (*state)->direction) { - src = &(*state)->src; - dst = &(*state)->dst; - } else { - src = &(*state)->dst; - dst = &(*state)->src; - } - - /* update states */ - if (src->state < PFOTHERS_SINGLE) - src->state = PFOTHERS_SINGLE; - if (dst->state == PFOTHERS_SINGLE) - dst->state = PFOTHERS_MULTIPLE; - - /* update expire time */ - (*state)->expire = time_uptime; - if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE) - (*state)->timeout = PFTM_OTHER_MULTIPLE; - else - (*state)->timeout = PFTM_OTHER_SINGLE; - - /* translate source/destination address, if necessary */ - if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; - - KASSERT(nk, ("%s: nk is null", __func__)); - KASSERT(pd, ("%s: pd is null", __func__)); - KASSERT(pd->src, ("%s: pd->src is null", __func__)); - KASSERT(pd->dst, ("%s: pd->dst is null", __func__)); - switch (pd->af) { -#ifdef INET - case AF_INET: - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) - pf_change_a(&pd->src->v4.s_addr, - pd->ip_sum, - nk->addr[pd->sidx].v4.s_addr, - 0); - - - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) - pf_change_a(&pd->dst->v4.s_addr, - pd->ip_sum, - nk->addr[pd->didx].v4.s_addr, - 0); - - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) - PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); - - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) - PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); -#endif /* INET6 */ - } - } - return (PF_PASS); -} - -/* - * ipoff and off are measured from the start of the mbuf chain. - * h must be at "ipoff" on the mbuf chain. - */ -void * -pf_pull_hdr(struct mbuf *m, int off, void *p, int len, - u_short *actionp, u_short *reasonp, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: { - struct ip *h = mtod(m, struct ip *); - u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; - - if (fragoff) { - if (fragoff >= len) - ACTION_SET(actionp, PF_PASS); - else { - ACTION_SET(actionp, PF_DROP); - REASON_SET(reasonp, PFRES_FRAG); - } - return (NULL); - } - if (m->m_pkthdr.len < off + len || - ntohs(h->ip_len) < off + len) { - ACTION_SET(actionp, PF_DROP); - REASON_SET(reasonp, PFRES_SHORT); - return (NULL); - } - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: { - struct ip6_hdr *h = mtod(m, struct ip6_hdr *); - - if (m->m_pkthdr.len < off + len || - (ntohs(h->ip6_plen) + sizeof(struct ip6_hdr)) < - (unsigned)(off + len)) { - ACTION_SET(actionp, PF_DROP); - REASON_SET(reasonp, PFRES_SHORT); - return (NULL); - } - break; - } -#endif /* INET6 */ - } - m_copydata(m, off, len, p); - return (p); -} - -int -pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif, - int rtableid) -{ -#ifdef RADIX_MPATH - struct radix_node_head *rnh; -#endif - struct sockaddr_in *dst; - int ret = 1; - int check_mpath; -#ifdef INET6 - struct sockaddr_in6 *dst6; - struct route_in6 ro; -#else - struct route ro; -#endif - struct radix_node *rn; - struct rtentry *rt; - struct ifnet *ifp; - - check_mpath = 0; -#ifdef RADIX_MPATH - /* XXX: stick to table 0 for now */ - rnh = rt_tables_get_rnh(0, af); - if (rnh != NULL && rn_mpath_capable(rnh)) - check_mpath = 1; -#endif - bzero(&ro, sizeof(ro)); - switch (af) { - case AF_INET: - dst = satosin(&ro.ro_dst); - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = addr->v4; - break; -#ifdef INET6 - case AF_INET6: - /* - * Skip check for addresses with embedded interface scope, - * as they would always match anyway. - */ - if (IN6_IS_SCOPE_EMBED(&addr->v6)) - goto out; - dst6 = (struct sockaddr_in6 *)&ro.ro_dst; - dst6->sin6_family = AF_INET6; - dst6->sin6_len = sizeof(*dst6); - dst6->sin6_addr = addr->v6; - break; -#endif /* INET6 */ - default: - return (0); - } - - /* Skip checks for ipsec interfaces */ - if (kif != NULL && kif->pfik_ifp->if_type == IFT_ENC) - goto out; - - switch (af) { -#ifdef INET6 - case AF_INET6: - in6_rtalloc_ign(&ro, 0, rtableid); - break; -#endif -#ifdef INET - case AF_INET: - in_rtalloc_ign((struct route *)&ro, 0, rtableid); - break; -#endif - default: - rtalloc_ign((struct route *)&ro, 0); /* No/default FIB. */ - break; - } - - if (ro.ro_rt != NULL) { - /* No interface given, this is a no-route check */ - if (kif == NULL) - goto out; - - if (kif->pfik_ifp == NULL) { - ret = 0; - goto out; - } - - /* Perform uRPF check if passed input interface */ - ret = 0; - rn = (struct radix_node *)ro.ro_rt; - do { - rt = (struct rtentry *)rn; - ifp = rt->rt_ifp; - - if (kif->pfik_ifp == ifp) - ret = 1; -#ifdef RADIX_MPATH - rn = rn_mpath_next(rn); -#endif - } while (check_mpath == 1 && rn != NULL && ret == 0); - } else - ret = 0; -out: - if (ro.ro_rt != NULL) - RTFREE(ro.ro_rt); - return (ret); -} - -#ifdef INET -static void -pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, - struct pf_state *s, struct pf_pdesc *pd) -{ - struct mbuf *m0, *m1; - struct sockaddr_in dst; - struct ip *ip; - struct ifnet *ifp = NULL; - struct pf_addr naddr; - struct pf_src_node *sn = NULL; - int error = 0; - int sw_csum; - - KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); - KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", - __func__)); - - if ((pd->pf_mtag == NULL && - ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || - pd->pf_mtag->routed++ > 3) { - m0 = *m; - *m = NULL; - goto bad_locked; - } - - if (r->rt == PF_DUPTO) { - if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { - if (s) - PF_STATE_UNLOCK(s); - return; - } - } else { - if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { - if (s) - PF_STATE_UNLOCK(s); - return; - } - m0 = *m; - } - - ip = mtod(m0, struct ip *); - - bzero(&dst, sizeof(dst)); - dst.sin_family = AF_INET; - dst.sin_len = sizeof(dst); - dst.sin_addr = ip->ip_dst; - - if (r->rt == PF_FASTROUTE) { - struct rtentry *rt; - - if (s) - PF_STATE_UNLOCK(s); - rt = rtalloc1_fib(sintosa(&dst), 0, 0, M_GETFIB(m0)); - if (rt == NULL) { - RTFREE_LOCKED(rt); - KMOD_IPSTAT_INC(ips_noroute); - error = EHOSTUNREACH; - goto bad; - } - - ifp = rt->rt_ifp; - rt->rt_rmx.rmx_pksent++; - - if (rt->rt_flags & RTF_GATEWAY) - bcopy(satosin(rt->rt_gateway), &dst, sizeof(dst)); - RTFREE_LOCKED(rt); - } else { - if (TAILQ_EMPTY(&r->rpool.list)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); - goto bad_locked; - } - if (s == NULL) { - pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, - &naddr, NULL, &sn); - if (!PF_AZERO(&naddr, AF_INET)) - dst.sin_addr.s_addr = naddr.v4.s_addr; - ifp = r->rpool.cur->kif ? - r->rpool.cur->kif->pfik_ifp : NULL; - } else { - if (!PF_AZERO(&s->rt_addr, AF_INET)) - dst.sin_addr.s_addr = - s->rt_addr.v4.s_addr; - ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; - PF_STATE_UNLOCK(s); - } - } - if (ifp == NULL) - goto bad; - - if (oifp != ifp) { - if (pf_test(PF_OUT, ifp, &m0, NULL) != PF_PASS) - goto bad; - else if (m0 == NULL) - goto done; - if (m0->m_len < sizeof(struct ip)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: m0->m_len < sizeof(struct ip)\n", __func__)); - goto bad; - } - ip = mtod(m0, struct ip *); - } - - if (ifp->if_flags & IFF_LOOPBACK) - m0->m_flags |= M_SKIP_FIREWALL; - - /* Back to host byte order. */ - ip->ip_len = ntohs(ip->ip_len); - ip->ip_off = ntohs(ip->ip_off); - - /* Copied from FreeBSD 10.0-CURRENT ip_output. */ - m0->m_pkthdr.csum_flags |= CSUM_IP; - sw_csum = m0->m_pkthdr.csum_flags & ~ifp->if_hwassist; - if (sw_csum & CSUM_DELAY_DATA) { - in_delayed_cksum(m0); - sw_csum &= ~CSUM_DELAY_DATA; - } -#ifdef SCTP - if (sw_csum & CSUM_SCTP) { - sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2)); - sw_csum &= ~CSUM_SCTP; - } -#endif - m0->m_pkthdr.csum_flags &= ifp->if_hwassist; - - /* - * If small enough for interface, or the interface will take - * care of the fragmentation for us, we can just send directly. - */ - if (ip->ip_len <= ifp->if_mtu || - (m0->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0 || - ((ip->ip_off & IP_DF) == 0 && (ifp->if_hwassist & CSUM_FRAGMENT))) { - ip->ip_len = htons(ip->ip_len); - ip->ip_off = htons(ip->ip_off); - ip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) - ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); - m0->m_flags &= ~(M_PROTOFLAGS); - error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); - goto done; - } - - /* Balk when DF bit is set or the interface didn't support TSO. */ - if ((ip->ip_off & IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) { - error = EMSGSIZE; - KMOD_IPSTAT_INC(ips_cantfrag); - if (r->rt != PF_DUPTO) { - icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, - ifp->if_mtu); - goto done; - } else - goto bad; - } - - error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist, sw_csum); - if (error) - goto bad; - - for (; m0; m0 = m1) { - m1 = m0->m_nextpkt; - m0->m_nextpkt = NULL; - if (error == 0) { - m0->m_flags &= ~(M_PROTOFLAGS); - error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); - } else - m_freem(m0); - } - - if (error == 0) - KMOD_IPSTAT_INC(ips_fragmented); - -done: - if (r->rt != PF_DUPTO) - *m = NULL; - return; - -bad_locked: - if (s) - PF_STATE_UNLOCK(s); -bad: - m_freem(m0); - goto done; -} -#endif /* INET */ - -#ifdef INET6 -static void -pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, - struct pf_state *s, struct pf_pdesc *pd) -{ - struct mbuf *m0; - struct sockaddr_in6 dst; - struct ip6_hdr *ip6; - struct ifnet *ifp = NULL; - struct pf_addr naddr; - struct pf_src_node *sn = NULL; - - KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); - KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", - __func__)); - - if ((pd->pf_mtag == NULL && - ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || - pd->pf_mtag->routed++ > 3) { - m0 = *m; - *m = NULL; - goto bad_locked; - } - - if (r->rt == PF_DUPTO) { - if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { - if (s) - PF_STATE_UNLOCK(s); - return; - } - } else { - if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { - if (s) - PF_STATE_UNLOCK(s); - return; - } - m0 = *m; - } - - ip6 = mtod(m0, struct ip6_hdr *); - - bzero(&dst, sizeof(dst)); - dst.sin6_family = AF_INET6; - dst.sin6_len = sizeof(dst); - dst.sin6_addr = ip6->ip6_dst; - - /* Cheat. XXX why only in the v6 case??? */ - if (r->rt == PF_FASTROUTE) { - if (s) - PF_STATE_UNLOCK(s); - m0->m_flags |= M_SKIP_FIREWALL; - ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); - return; - } - - if (TAILQ_EMPTY(&r->rpool.list)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); - goto bad_locked; - } - if (s == NULL) { - pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, - &naddr, NULL, &sn); - if (!PF_AZERO(&naddr, AF_INET6)) - PF_ACPY((struct pf_addr *)&dst.sin6_addr, - &naddr, AF_INET6); - ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; - } else { - if (!PF_AZERO(&s->rt_addr, AF_INET6)) - PF_ACPY((struct pf_addr *)&dst.sin6_addr, - &s->rt_addr, AF_INET6); - ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; - } - - if (s) - PF_STATE_UNLOCK(s); - - if (ifp == NULL) - goto bad; - - if (oifp != ifp) { - if (pf_test6(PF_OUT, ifp, &m0, NULL) != PF_PASS) - goto bad; - else if (m0 == NULL) - goto done; - if (m0->m_len < sizeof(struct ip6_hdr)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: m0->m_len < sizeof(struct ip6_hdr)\n", - __func__)); - goto bad; - } - ip6 = mtod(m0, struct ip6_hdr *); - } - - if (ifp->if_flags & IFF_LOOPBACK) - m0->m_flags |= M_SKIP_FIREWALL; - - /* - * If the packet is too large for the outgoing interface, - * send back an icmp6 error. - */ - if (IN6_IS_SCOPE_EMBED(&dst.sin6_addr)) - dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) - nd6_output(ifp, ifp, m0, &dst, NULL); - else { - in6_ifstat_inc(ifp, ifs6_in_toobig); - if (r->rt != PF_DUPTO) - icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); - else - goto bad; - } - -done: - if (r->rt != PF_DUPTO) - *m = NULL; - return; - -bad_locked: - if (s) - PF_STATE_UNLOCK(s); -bad: - m_freem(m0); - goto done; -} -#endif /* INET6 */ - -/* - * FreeBSD supports cksum offloads for the following drivers. - * em(4), fxp(4), ixgb(4), lge(4), ndis(4), nge(4), re(4), - * ti(4), txp(4), xl(4) - * - * CSUM_DATA_VALID | CSUM_PSEUDO_HDR : - * network driver performed cksum including pseudo header, need to verify - * csum_data - * CSUM_DATA_VALID : - * network driver performed cksum, needs to additional pseudo header - * cksum computation with partial csum_data(i.e. lack of H/W support for - * pseudo header, for instance hme(4), sk(4) and possibly gem(4)) - * - * After validating the cksum of packet, set both flag CSUM_DATA_VALID and - * CSUM_PSEUDO_HDR in order to avoid recomputation of the cksum in upper - * TCP/UDP layer. - * Also, set csum_data to 0xffff to force cksum validation. - */ -static int -pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) -{ - u_int16_t sum = 0; - int hw_assist = 0; - struct ip *ip; - - if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) - return (1); - if (m->m_pkthdr.len < off + len) - return (1); - - switch (p) { - case IPPROTO_TCP: - if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { - if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { - sum = m->m_pkthdr.csum_data; - } else { - ip = mtod(m, struct ip *); - sum = in_pseudo(ip->ip_src.s_addr, - ip->ip_dst.s_addr, htonl((u_short)len + - m->m_pkthdr.csum_data + IPPROTO_TCP)); - } - sum ^= 0xffff; - ++hw_assist; - } - break; - case IPPROTO_UDP: - if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { - if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { - sum = m->m_pkthdr.csum_data; - } else { - ip = mtod(m, struct ip *); - sum = in_pseudo(ip->ip_src.s_addr, - ip->ip_dst.s_addr, htonl((u_short)len + - m->m_pkthdr.csum_data + IPPROTO_UDP)); - } - sum ^= 0xffff; - ++hw_assist; - } - break; - case IPPROTO_ICMP: -#ifdef INET6 - case IPPROTO_ICMPV6: -#endif /* INET6 */ - break; - default: - return (1); - } - - if (!hw_assist) { - switch (af) { - case AF_INET: - if (p == IPPROTO_ICMP) { - if (m->m_len < off) - return (1); - m->m_data += off; - m->m_len -= off; - sum = in_cksum(m, len); - m->m_data -= off; - m->m_len += off; - } else { - if (m->m_len < sizeof(struct ip)) - return (1); - sum = in4_cksum(m, p, off, len); - } - break; -#ifdef INET6 - case AF_INET6: - if (m->m_len < sizeof(struct ip6_hdr)) - return (1); - sum = in6_cksum(m, p, off, len); - break; -#endif /* INET6 */ - default: - return (1); - } - } - if (sum) { - switch (p) { - case IPPROTO_TCP: - { - KMOD_TCPSTAT_INC(tcps_rcvbadsum); - break; - } - case IPPROTO_UDP: - { - KMOD_UDPSTAT_INC(udps_badsum); - break; - } -#ifdef INET - case IPPROTO_ICMP: - { - KMOD_ICMPSTAT_INC(icps_checksum); - break; - } -#endif -#ifdef INET6 - case IPPROTO_ICMPV6: - { - KMOD_ICMP6STAT_INC(icp6s_checksum); - break; - } -#endif /* INET6 */ - } - return (1); - } else { - if (p == IPPROTO_TCP || p == IPPROTO_UDP) { - m->m_pkthdr.csum_flags |= - (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); - m->m_pkthdr.csum_data = 0xffff; - } - } - return (0); -} - - -#ifdef INET -int -pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) -{ - struct pfi_kif *kif; - u_short action, reason = 0, log = 0; - struct mbuf *m = *m0; - struct ip *h = NULL; - struct m_tag *ipfwtag; - struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; - struct pf_state *s = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_pdesc pd; - int off, dirndx, pqid = 0; - - M_ASSERTPKTHDR(m); - - if (!V_pf_status.running) - return (PF_PASS); - - memset(&pd, 0, sizeof(pd)); - - kif = (struct pfi_kif *)ifp->if_pf_kif; - - if (kif == NULL) { - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_test: kif == NULL, if_xname %s\n", ifp->if_xname)); - return (PF_DROP); - } - if (kif->pfik_flags & PFI_IFLAG_SKIP) - return (PF_PASS); - - if (m->m_flags & M_SKIP_FIREWALL) - return (PF_PASS); - - if (m->m_pkthdr.len < (int)sizeof(struct ip)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_SHORT); - log = 1; - goto done; - } - - pd.pf_mtag = pf_find_mtag(m); - - PF_RULES_RLOCK(); - - if (ip_divert_ptr != NULL && - ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { - struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); - if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - goto done; - } - pd.pf_mtag->flags |= PF_PACKET_LOOPED; - m_tag_delete(m, ipfwtag); - } - if (pd.pf_mtag && pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { - m->m_flags |= M_FASTFWD_OURS; - pd.pf_mtag->flags &= ~PF_FASTFWD_OURS_PRESENT; - } - } else if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { - /* We do IP header normalization and packet reassembly here */ - action = PF_DROP; - goto done; - } - m = *m0; /* pf_normalize messes with m0 */ - h = mtod(m, struct ip *); - - off = h->ip_hl << 2; - if (off < (int)sizeof(struct ip)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_SHORT); - log = 1; - goto done; - } - - pd.src = (struct pf_addr *)&h->ip_src; - pd.dst = (struct pf_addr *)&h->ip_dst; - pd.sport = pd.dport = NULL; - pd.ip_sum = &h->ip_sum; - pd.proto_sum = NULL; - pd.proto = h->ip_p; - pd.dir = dir; - pd.sidx = (dir == PF_IN) ? 0 : 1; - pd.didx = (dir == PF_IN) ? 1 : 0; - pd.af = AF_INET; - pd.tos = h->ip_tos; - pd.tot_len = ntohs(h->ip_len); - - /* handle fragments that didn't get reassembled by normalization */ - if (h->ip_off & htons(IP_MF | IP_OFFMASK)) { - action = pf_test_fragment(&r, dir, kif, m, h, - &pd, &a, &ruleset); - goto done; - } - - switch (h->ip_p) { - - case IPPROTO_TCP: { - struct tcphdr th; - - pd.hdr.tcp = &th; - if (!pf_pull_hdr(m, off, &th, sizeof(th), - &action, &reason, AF_INET)) { - log = action != PF_PASS; - goto done; - } - pd.p_len = pd.tot_len - off - (th.th_off << 2); - if ((th.th_flags & TH_ACK) && pd.p_len == 0) - pqid = 1; - action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); - if (action == PF_DROP) - goto done; - action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, - &reason); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - - case IPPROTO_UDP: { - struct udphdr uh; - - pd.hdr.udp = &uh; - if (!pf_pull_hdr(m, off, &uh, sizeof(uh), - &action, &reason, AF_INET)) { - log = action != PF_PASS; - goto done; - } - if (uh.uh_dport == 0 || - ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || - ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_SHORT); - goto done; - } - action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - - case IPPROTO_ICMP: { - struct icmp ih; - - pd.hdr.icmp = &ih; - if (!pf_pull_hdr(m, off, &ih, ICMP_MINLEN, - &action, &reason, AF_INET)) { - log = action != PF_PASS; - goto done; - } - action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, - &reason); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - -#ifdef INET6 - case IPPROTO_ICMPV6: { - action = PF_DROP; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: dropping IPv4 packet with ICMPv6 payload\n")); - goto done; - } -#endif - - default: - action = pf_test_state_other(&s, dir, kif, m, &pd); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - -done: - PF_RULES_RUNLOCK(); - if (action == PF_PASS && h->ip_hl > 5 && - !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_IPOPTIONS); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: dropping packet with ip options\n")); - } - - if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - } - if (r->rtableid >= 0) - M_SETFIB(m, r->rtableid); - -#ifdef ALTQ - if (action == PF_PASS && r->qid) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - } - if (pqid || (pd.tos & IPTOS_LOWDELAY)) - pd.pf_mtag->qid = r->pqid; - else - pd.pf_mtag->qid = r->qid; - /* add hints for ecn */ - pd.pf_mtag->hdr = h; - - } -#endif /* ALTQ */ - - /* - * connections redirected to loopback should not match sockets - * bound specifically to loopback due to security implications, - * see tcp_input() and in_pcblookup_listen(). - */ - if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || - pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && - (s->nat_rule.ptr->action == PF_RDR || - s->nat_rule.ptr->action == PF_BINAT) && - (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) - m->m_flags |= M_SKIP_FIREWALL; - - if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && - !PACKET_LOOPED(&pd)) { - - ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, - sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); - if (ipfwtag != NULL) { - ((struct ipfw_rule_ref *)(ipfwtag+1))->info = - ntohs(r->divert.port); - ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; - - if (s) - PF_STATE_UNLOCK(s); - - m_tag_prepend(m, ipfwtag); - if (m->m_flags & M_FASTFWD_OURS) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: failed to allocate tag\n")); - } - pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; - m->m_flags &= ~M_FASTFWD_OURS; - } - ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); - *m0 = NULL; - - return (action); - } else { - /* XXX: ipfw has the same behaviour! */ - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: failed to allocate divert tag\n")); - } - } - - if (log) { - struct pf_rule *lr; - - if (s != NULL && s->nat_rule.ptr != NULL && - s->nat_rule.ptr->log & PF_LOG_ALL) - lr = s->nat_rule.ptr; - else - lr = r; - PFLOG_PACKET(kif, m, AF_INET, dir, reason, lr, a, ruleset, &pd, - (s == NULL)); - } - - kif->pfik_bytes[0][dir == PF_OUT][action != PF_PASS] += pd.tot_len; - kif->pfik_packets[0][dir == PF_OUT][action != PF_PASS]++; - - if (action == PF_PASS || r->action == PF_DROP) { - dirndx = (dir == PF_OUT); - r->packets[dirndx]++; - r->bytes[dirndx] += pd.tot_len; - if (a != NULL) { - a->packets[dirndx]++; - a->bytes[dirndx] += pd.tot_len; - } - if (s != NULL) { - if (s->nat_rule.ptr != NULL) { - s->nat_rule.ptr->packets[dirndx]++; - s->nat_rule.ptr->bytes[dirndx] += pd.tot_len; - } - if (s->src_node != NULL) { - s->src_node->packets[dirndx]++; - s->src_node->bytes[dirndx] += pd.tot_len; - } - if (s->nat_src_node != NULL) { - s->nat_src_node->packets[dirndx]++; - s->nat_src_node->bytes[dirndx] += pd.tot_len; - } - dirndx = (dir == s->direction) ? 0 : 1; - s->packets[dirndx]++; - s->bytes[dirndx] += pd.tot_len; - } - tr = r; - nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; - if (nr != NULL && r == &V_pf_default_rule) - tr = nr; - if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, - (s == NULL) ? pd.src : - &s->key[(s->direction == PF_IN)]-> - addr[(s->direction == PF_OUT)], - pd.af, pd.tot_len, dir == PF_OUT, - r->action == PF_PASS, tr->src.neg); - if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, - (s == NULL) ? pd.dst : - &s->key[(s->direction == PF_IN)]-> - addr[(s->direction == PF_IN)], - pd.af, pd.tot_len, dir == PF_OUT, - r->action == PF_PASS, tr->dst.neg); - } - - switch (action) { - case PF_SYNPROXY_DROP: - m_freem(*m0); - case PF_DEFER: - *m0 = NULL; - action = PF_PASS; - break; - default: - /* pf_route() returns unlocked. */ - if (r->rt) { - pf_route(m0, r, dir, kif->pfik_ifp, s, &pd); - return (action); - } - break; - } - if (s) - PF_STATE_UNLOCK(s); - - return (action); -} -#endif /* INET */ - -#ifdef INET6 -int -pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) -{ - struct pfi_kif *kif; - u_short action, reason = 0, log = 0; - struct mbuf *m = *m0, *n = NULL; - struct ip6_hdr *h = NULL; - struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; - struct pf_state *s = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_pdesc pd; - int off, terminal = 0, dirndx, rh_cnt = 0; - - M_ASSERTPKTHDR(m); - - if (!V_pf_status.running) - return (PF_PASS); - - memset(&pd, 0, sizeof(pd)); - pd.pf_mtag = pf_find_mtag(m); - - if (pd.pf_mtag && pd.pf_mtag->flags & PF_TAG_GENERATED) - return (PF_PASS); - - kif = (struct pfi_kif *)ifp->if_pf_kif; - if (kif == NULL) { - DPFPRINTF(PF_DEBUG_URGENT, - ("pf_test6: kif == NULL, if_xname %s\n", ifp->if_xname)); - return (PF_DROP); - } - if (kif->pfik_flags & PFI_IFLAG_SKIP) - return (PF_PASS); - - if (m->m_pkthdr.len < (int)sizeof(*h)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_SHORT); - log = 1; - goto done; - } - - PF_RULES_RLOCK(); - - /* We do IP header normalization and packet reassembly here */ - if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { - action = PF_DROP; - goto done; - } - m = *m0; /* pf_normalize messes with m0 */ - h = mtod(m, struct ip6_hdr *); - -#if 1 - /* - * we do not support jumbogram yet. if we keep going, zero ip6_plen - * will do something bad, so drop the packet for now. - */ - if (htons(h->ip6_plen) == 0) { - action = PF_DROP; - REASON_SET(&reason, PFRES_NORM); /*XXX*/ - goto done; - } -#endif - - pd.src = (struct pf_addr *)&h->ip6_src; - pd.dst = (struct pf_addr *)&h->ip6_dst; - pd.sport = pd.dport = NULL; - pd.ip_sum = NULL; - pd.proto_sum = NULL; - pd.dir = dir; - pd.sidx = (dir == PF_IN) ? 0 : 1; - pd.didx = (dir == PF_IN) ? 1 : 0; - pd.af = AF_INET6; - pd.tos = 0; - pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); - - off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr); - pd.proto = h->ip6_nxt; - do { - switch (pd.proto) { - case IPPROTO_FRAGMENT: - action = pf_test_fragment(&r, dir, kif, m, h, - &pd, &a, &ruleset); - if (action == PF_DROP) - REASON_SET(&reason, PFRES_FRAG); - goto done; - case IPPROTO_ROUTING: { - struct ip6_rthdr rthdr; - - if (rh_cnt++) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: IPv6 more than one rthdr\n")); - action = PF_DROP; - REASON_SET(&reason, PFRES_IPOPTIONS); - log = 1; - goto done; - } - if (!pf_pull_hdr(m, off, &rthdr, sizeof(rthdr), NULL, - &reason, pd.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: IPv6 short rthdr\n")); - action = PF_DROP; - REASON_SET(&reason, PFRES_SHORT); - log = 1; - goto done; - } - if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: IPv6 rthdr0\n")); - action = PF_DROP; - REASON_SET(&reason, PFRES_IPOPTIONS); - log = 1; - goto done; - } - /* FALLTHROUGH */ - } - case IPPROTO_AH: - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: { - /* get next header and header length */ - struct ip6_ext opt6; - - if (!pf_pull_hdr(m, off, &opt6, sizeof(opt6), - NULL, &reason, pd.af)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: IPv6 short opt\n")); - action = PF_DROP; - log = 1; - goto done; - } - if (pd.proto == IPPROTO_AH) - off += (opt6.ip6e_len + 2) * 4; - else - off += (opt6.ip6e_len + 1) * 8; - pd.proto = opt6.ip6e_nxt; - /* goto the next header */ - break; - } - default: - terminal++; - break; - } - } while (!terminal); - - /* if there's no routing header, use unmodified mbuf for checksumming */ - if (!n) - n = m; - - switch (pd.proto) { - - case IPPROTO_TCP: { - struct tcphdr th; - - pd.hdr.tcp = &th; - if (!pf_pull_hdr(m, off, &th, sizeof(th), - &action, &reason, AF_INET6)) { - log = action != PF_PASS; - goto done; - } - pd.p_len = pd.tot_len - off - (th.th_off << 2); - action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); - if (action == PF_DROP) - goto done; - action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, - &reason); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - - case IPPROTO_UDP: { - struct udphdr uh; - - pd.hdr.udp = &uh; - if (!pf_pull_hdr(m, off, &uh, sizeof(uh), - &action, &reason, AF_INET6)) { - log = action != PF_PASS; - goto done; - } - if (uh.uh_dport == 0 || - ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || - ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_SHORT); - goto done; - } - action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - - case IPPROTO_ICMP: { - action = PF_DROP; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: dropping IPv6 packet with ICMPv4 payload\n")); - goto done; - } - - case IPPROTO_ICMPV6: { - struct icmp6_hdr ih; - - pd.hdr.icmp6 = &ih; - if (!pf_pull_hdr(m, off, &ih, sizeof(ih), - &action, &reason, AF_INET6)) { - log = action != PF_PASS; - goto done; - } - action = pf_test_state_icmp(&s, dir, kif, - m, off, h, &pd, &reason); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - - default: - action = pf_test_state_other(&s, dir, kif, m, &pd); - if (action == PF_PASS) { - if (pfsync_update_state_ptr != NULL) - pfsync_update_state_ptr(s); - r = s->rule.ptr; - a = s->anchor.ptr; - log = s->log; - } else if (s == NULL) - action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, - &a, &ruleset, inp); - break; - } - -done: - PF_RULES_RUNLOCK(); - if (n != m) { - m_freem(n); - n = NULL; - } - - /* handle dangerous IPv6 extension headers. */ - if (action == PF_PASS && rh_cnt && - !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_IPOPTIONS); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: dropping packet with dangerous v6 headers\n")); - } - - if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - } - if (r->rtableid >= 0) - M_SETFIB(m, r->rtableid); - -#ifdef ALTQ - if (action == PF_PASS && r->qid) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - } - if (pd.tos & IPTOS_LOWDELAY) - pd.pf_mtag->qid = r->pqid; - else - pd.pf_mtag->qid = r->qid; - /* add hints for ecn */ - pd.pf_mtag->hdr = h; - } -#endif /* ALTQ */ - - if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || - pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && - (s->nat_rule.ptr->action == PF_RDR || - s->nat_rule.ptr->action == PF_BINAT) && - IN6_IS_ADDR_LOOPBACK(&pd.dst->v6)) - m->m_flags |= M_SKIP_FIREWALL; - - /* XXX: Anybody working on it?! */ - if (r->divert.port) - printf("pf: divert(9) is not supported for IPv6\n"); - - if (log) { - struct pf_rule *lr; - - if (s != NULL && s->nat_rule.ptr != NULL && - s->nat_rule.ptr->log & PF_LOG_ALL) - lr = s->nat_rule.ptr; - else - lr = r; - PFLOG_PACKET(kif, m, AF_INET6, dir, reason, lr, a, ruleset, - &pd, (s == NULL)); - } - - kif->pfik_bytes[1][dir == PF_OUT][action != PF_PASS] += pd.tot_len; - kif->pfik_packets[1][dir == PF_OUT][action != PF_PASS]++; - - if (action == PF_PASS || r->action == PF_DROP) { - dirndx = (dir == PF_OUT); - r->packets[dirndx]++; - r->bytes[dirndx] += pd.tot_len; - if (a != NULL) { - a->packets[dirndx]++; - a->bytes[dirndx] += pd.tot_len; - } - if (s != NULL) { - if (s->nat_rule.ptr != NULL) { - s->nat_rule.ptr->packets[dirndx]++; - s->nat_rule.ptr->bytes[dirndx] += pd.tot_len; - } - if (s->src_node != NULL) { - s->src_node->packets[dirndx]++; - s->src_node->bytes[dirndx] += pd.tot_len; - } - if (s->nat_src_node != NULL) { - s->nat_src_node->packets[dirndx]++; - s->nat_src_node->bytes[dirndx] += pd.tot_len; - } - dirndx = (dir == s->direction) ? 0 : 1; - s->packets[dirndx]++; - s->bytes[dirndx] += pd.tot_len; - } - tr = r; - nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; - if (nr != NULL && r == &V_pf_default_rule) - tr = nr; - if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, - (s == NULL) ? pd.src : - &s->key[(s->direction == PF_IN)]->addr[0], - pd.af, pd.tot_len, dir == PF_OUT, - r->action == PF_PASS, tr->src.neg); - if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, - (s == NULL) ? pd.dst : - &s->key[(s->direction == PF_IN)]->addr[1], - pd.af, pd.tot_len, dir == PF_OUT, - r->action == PF_PASS, tr->dst.neg); - } - - switch (action) { - case PF_SYNPROXY_DROP: - m_freem(*m0); - case PF_DEFER: - *m0 = NULL; - action = PF_PASS; - break; - default: - /* pf_route6() returns unlocked. */ - if (r->rt) { - pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd); - return (action); - } - break; - } - - if (s) - PF_STATE_UNLOCK(s); - - return (action); -} -#endif /* INET6 */ diff --git a/sys/contrib/pf/net/pf_if.c b/sys/contrib/pf/net/pf_if.c deleted file mode 100644 index c010b654c0d02..0000000000000 --- a/sys/contrib/pf/net/pf_if.c +++ /dev/null @@ -1,859 +0,0 @@ -/* $OpenBSD: pf_if.c,v 1.54 2008/06/14 16:55:28 mk Exp $ */ - -/* - * Copyright 2005 Henning Brauer - * Copyright 2005 Ryan McBride - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2003 Cedric Berger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" - -#include -#include -#include - -#include -#include -#include - -VNET_DEFINE(struct pfi_kif *, pfi_all); -static VNET_DEFINE(long, pfi_update); -#define V_pfi_update VNET(pfi_update) -#define PFI_BUFFER_MAX 0x10000 - -static VNET_DEFINE(struct pfr_addr *, pfi_buffer); -static VNET_DEFINE(int, pfi_buffer_cnt); -static VNET_DEFINE(int, pfi_buffer_max); -#define V_pfi_buffer VNET(pfi_buffer) -#define V_pfi_buffer_cnt VNET(pfi_buffer_cnt) -#define V_pfi_buffer_max VNET(pfi_buffer_max) - -eventhandler_tag pfi_attach_cookie; -eventhandler_tag pfi_detach_cookie; -eventhandler_tag pfi_attach_group_cookie; -eventhandler_tag pfi_change_group_cookie; -eventhandler_tag pfi_detach_group_cookie; -eventhandler_tag pfi_ifaddr_event_cookie; - -static void pfi_attach_ifnet(struct ifnet *); -static void pfi_attach_ifgroup(struct ifg_group *); - -static void pfi_kif_update(struct pfi_kif *); -static void pfi_dynaddr_update(struct pfi_dynaddr *dyn); -static void pfi_table_update(struct pfr_ktable *, struct pfi_kif *, int, - int); -static void pfi_instance_add(struct ifnet *, int, int); -static void pfi_address_add(struct sockaddr *, int, int); -static int pfi_if_compare(struct pfi_kif *, struct pfi_kif *); -static int pfi_skip_if(const char *, struct pfi_kif *); -static int pfi_unmask(void *); -static void pfi_attach_ifnet_event(void * __unused, struct ifnet *); -static void pfi_detach_ifnet_event(void * __unused, struct ifnet *); -static void pfi_attach_group_event(void *, struct ifg_group *); -static void pfi_change_group_event(void *, char *); -static void pfi_detach_group_event(void *, struct ifg_group *); -static void pfi_ifaddr_event(void * __unused, struct ifnet *); - -RB_HEAD(pfi_ifhead, pfi_kif); -static RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); -static RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); -static VNET_DEFINE(struct pfi_ifhead, pfi_ifs); -#define V_pfi_ifs VNET(pfi_ifs) - -#define PFI_BUFFER_MAX 0x10000 -MALLOC_DEFINE(PFI_MTYPE, "pf_ifnet", "pf(4) interface database"); - -LIST_HEAD(pfi_list, pfi_kif); -static VNET_DEFINE(struct pfi_list, pfi_unlinked_kifs); -#define V_pfi_unlinked_kifs VNET(pfi_unlinked_kifs) -static struct mtx pfi_unlnkdkifs_mtx; - -void -pfi_initialize(void) -{ - struct ifg_group *ifg; - struct ifnet *ifp; - struct pfi_kif *kif; - - V_pfi_buffer_max = 64; - V_pfi_buffer = malloc(V_pfi_buffer_max * sizeof(*V_pfi_buffer), - PFI_MTYPE, M_WAITOK); - - mtx_init(&pfi_unlnkdkifs_mtx, "pf unlinked interfaces", NULL, MTX_DEF); - - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - PF_RULES_WLOCK(); - V_pfi_all = pfi_kif_attach(kif, IFG_ALL); - PF_RULES_WUNLOCK(); - - IFNET_RLOCK(); - TAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) - pfi_attach_ifgroup(ifg); - TAILQ_FOREACH(ifp, &V_ifnet, if_link) - pfi_attach_ifnet(ifp); - IFNET_RUNLOCK(); - - pfi_attach_cookie = EVENTHANDLER_REGISTER(ifnet_arrival_event, - pfi_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); - pfi_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, - pfi_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); - pfi_attach_group_cookie = EVENTHANDLER_REGISTER(group_attach_event, - pfi_attach_group_event, curvnet, EVENTHANDLER_PRI_ANY); - pfi_change_group_cookie = EVENTHANDLER_REGISTER(group_change_event, - pfi_change_group_event, curvnet, EVENTHANDLER_PRI_ANY); - pfi_detach_group_cookie = EVENTHANDLER_REGISTER(group_detach_event, - pfi_detach_group_event, curvnet, EVENTHANDLER_PRI_ANY); - pfi_ifaddr_event_cookie = EVENTHANDLER_REGISTER(ifaddr_event, - pfi_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY); -} - -void -pfi_cleanup(void) -{ - struct pfi_kif *p; - - EVENTHANDLER_DEREGISTER(ifnet_arrival_event, pfi_attach_cookie); - EVENTHANDLER_DEREGISTER(ifnet_departure_event, pfi_detach_cookie); - EVENTHANDLER_DEREGISTER(group_attach_event, pfi_attach_group_cookie); - EVENTHANDLER_DEREGISTER(group_change_event, pfi_change_group_cookie); - EVENTHANDLER_DEREGISTER(group_detach_event, pfi_detach_group_cookie); - EVENTHANDLER_DEREGISTER(ifaddr_event, pfi_ifaddr_event_cookie); - - V_pfi_all = NULL; - while ((p = RB_MIN(pfi_ifhead, &V_pfi_ifs))) { - RB_REMOVE(pfi_ifhead, &V_pfi_ifs, p); - free(p, PFI_MTYPE); - } - - while ((p = LIST_FIRST(&V_pfi_unlinked_kifs))) { - LIST_REMOVE(p, pfik_list); - free(p, PFI_MTYPE); - } - - mtx_destroy(&pfi_unlnkdkifs_mtx); - - free(V_pfi_buffer, PFI_MTYPE); -} - -struct pfi_kif * -pfi_kif_find(const char *kif_name) -{ - struct pfi_kif_cmp s; - - PF_RULES_ASSERT(); - - bzero(&s, sizeof(s)); - strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name)); - - return (RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&s)); -} - -struct pfi_kif * -pfi_kif_attach(struct pfi_kif *kif, const char *kif_name) -{ - struct pfi_kif *kif1; - - PF_RULES_WASSERT(); - KASSERT(kif != NULL, ("%s: null kif", __func__)); - - kif1 = pfi_kif_find(kif_name); - if (kif1 != NULL) { - free(kif, PFI_MTYPE); - return (kif1); - } - - bzero(kif, sizeof(*kif)); - strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name)); - /* - * It seems that the value of time_second is in unintialzied state - * when pf sets interface statistics clear time in boot phase if pf - * was statically linked to kernel. Instead of setting the bogus - * time value have pfi_get_ifaces handle this case. In - * pfi_get_ifaces it uses time_second if it sees the time is 0. - */ - kif->pfik_tzero = time_second > 1 ? time_second : 0; - TAILQ_INIT(&kif->pfik_dynaddrs); - - RB_INSERT(pfi_ifhead, &V_pfi_ifs, kif); - - return (kif); -} - -void -pfi_kif_ref(struct pfi_kif *kif) -{ - - PF_RULES_WASSERT(); - kif->pfik_rulerefs++; -} - -void -pfi_kif_unref(struct pfi_kif *kif) -{ - - PF_RULES_WASSERT(); - KASSERT(kif->pfik_rulerefs > 0, ("%s: %p has zero refs", __func__, kif)); - - kif->pfik_rulerefs--; - - if (kif->pfik_rulerefs > 0) - return; - - /* kif referencing an existing ifnet or group should exist. */ - if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == V_pfi_all) - return; - - RB_REMOVE(pfi_ifhead, &V_pfi_ifs, kif); - - kif->pfik_flags |= PFI_IFLAG_REFS; - - mtx_lock(&pfi_unlnkdkifs_mtx); - LIST_INSERT_HEAD(&V_pfi_unlinked_kifs, kif, pfik_list); - mtx_unlock(&pfi_unlnkdkifs_mtx); -} - -void -pfi_kif_purge(void) -{ - struct pfi_kif *kif, *kif1; - - /* - * Do naive mark-and-sweep garbage collecting of old kifs. - * Reference flag is raised by pf_purge_expired_states(). - */ - mtx_lock(&pfi_unlnkdkifs_mtx); - LIST_FOREACH_SAFE(kif, &V_pfi_unlinked_kifs, pfik_list, kif1) { - if (!(kif->pfik_flags & PFI_IFLAG_REFS)) { - LIST_REMOVE(kif, pfik_list); - free(kif, PFI_MTYPE); - } else - kif->pfik_flags &= ~PFI_IFLAG_REFS; - } - mtx_unlock(&pfi_unlnkdkifs_mtx); -} - -int -pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif) -{ - struct ifg_list *p; - - if (rule_kif == NULL || rule_kif == packet_kif) - return (1); - - if (rule_kif->pfik_group != NULL) - /* XXXGL: locking? */ - TAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next) - if (p->ifgl_group == rule_kif->pfik_group) - return (1); - - return (0); -} - -static void -pfi_attach_ifnet(struct ifnet *ifp) -{ - struct pfi_kif *kif; - - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - - PF_RULES_WLOCK(); - V_pfi_update++; - kif = pfi_kif_attach(kif, ifp->if_xname); - - kif->pfik_ifp = ifp; - ifp->if_pf_kif = kif; - - pfi_kif_update(kif); - PF_RULES_WUNLOCK(); -} - -static void -pfi_attach_ifgroup(struct ifg_group *ifg) -{ - struct pfi_kif *kif; - - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - - PF_RULES_WLOCK(); - V_pfi_update++; - kif = pfi_kif_attach(kif, ifg->ifg_group); - - kif->pfik_group = ifg; - ifg->ifg_pf_kif = kif; - PF_RULES_WUNLOCK(); -} - -int -pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af) -{ - switch (af) { -#ifdef INET - case AF_INET: - switch (dyn->pfid_acnt4) { - case 0: - return (0); - case 1: - return (PF_MATCHA(0, &dyn->pfid_addr4, - &dyn->pfid_mask4, a, AF_INET)); - default: - return (pfr_match_addr(dyn->pfid_kt, a, AF_INET)); - } - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - switch (dyn->pfid_acnt6) { - case 0: - return (0); - case 1: - return (PF_MATCHA(0, &dyn->pfid_addr6, - &dyn->pfid_mask6, a, AF_INET6)); - default: - return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6)); - } - break; -#endif /* INET6 */ - default: - return (0); - } -} - -int -pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) -{ - struct pfi_dynaddr *dyn; - char tblname[PF_TABLE_NAME_SIZE]; - struct pf_ruleset *ruleset = NULL; - struct pfi_kif *kif; - int rv = 0; - - PF_RULES_WASSERT(); - KASSERT(aw->type == PF_ADDR_DYNIFTL, ("%s: type %u", - __func__, aw->type)); - KASSERT(aw->p.dyn == NULL, ("%s: dyn is %p", __func__, aw->p.dyn)); - - if ((dyn = malloc(sizeof(*dyn), PFI_MTYPE, M_NOWAIT | M_ZERO)) == NULL) - return (ENOMEM); - - if ((kif = malloc(sizeof(*kif), PFI_MTYPE, M_NOWAIT)) == NULL) { - free(dyn, PFI_MTYPE); - return (ENOMEM); - } - - if (!strcmp(aw->v.ifname, "self")) - dyn->pfid_kif = pfi_kif_attach(kif, IFG_ALL); - else - dyn->pfid_kif = pfi_kif_attach(kif, aw->v.ifname); - pfi_kif_ref(dyn->pfid_kif); - - dyn->pfid_net = pfi_unmask(&aw->v.a.mask); - if (af == AF_INET && dyn->pfid_net == 32) - dyn->pfid_net = 128; - strlcpy(tblname, aw->v.ifname, sizeof(tblname)); - if (aw->iflags & PFI_AFLAG_NETWORK) - strlcat(tblname, ":network", sizeof(tblname)); - if (aw->iflags & PFI_AFLAG_BROADCAST) - strlcat(tblname, ":broadcast", sizeof(tblname)); - if (aw->iflags & PFI_AFLAG_PEER) - strlcat(tblname, ":peer", sizeof(tblname)); - if (aw->iflags & PFI_AFLAG_NOALIAS) - strlcat(tblname, ":0", sizeof(tblname)); - if (dyn->pfid_net != 128) - snprintf(tblname + strlen(tblname), - sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net); - if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == NULL) { - rv = ENOMEM; - goto _bad; - } - - if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname)) == NULL) { - rv = ENOMEM; - goto _bad; - } - - dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE; - dyn->pfid_iflags = aw->iflags; - dyn->pfid_af = af; - - TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); - aw->p.dyn = dyn; - pfi_kif_update(dyn->pfid_kif); - - return (0); - -_bad: - if (dyn->pfid_kt != NULL) - pfr_detach_table(dyn->pfid_kt); - if (ruleset != NULL) - pf_remove_if_empty_ruleset(ruleset); - if (dyn->pfid_kif != NULL) - pfi_kif_unref(dyn->pfid_kif); - free(dyn, PFI_MTYPE); - - return (rv); -} - -static void -pfi_kif_update(struct pfi_kif *kif) -{ - struct ifg_list *ifgl; - struct pfi_dynaddr *p; - - PF_RULES_WASSERT(); - - /* update all dynaddr */ - TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry) - pfi_dynaddr_update(p); - - /* again for all groups kif is member of */ - if (kif->pfik_ifp != NULL) { - IF_ADDR_RLOCK(kif->pfik_ifp); - TAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next) - pfi_kif_update((struct pfi_kif *) - ifgl->ifgl_group->ifg_pf_kif); - IF_ADDR_RUNLOCK(kif->pfik_ifp); - } -} - -static void -pfi_dynaddr_update(struct pfi_dynaddr *dyn) -{ - struct pfi_kif *kif; - struct pfr_ktable *kt; - - PF_RULES_WASSERT(); - KASSERT(dyn && dyn->pfid_kif && dyn->pfid_kt, - ("%s: bad argument", __func__)); - - kif = dyn->pfid_kif; - kt = dyn->pfid_kt; - - if (kt->pfrkt_larg != V_pfi_update) { - /* this table needs to be brought up-to-date */ - pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags); - kt->pfrkt_larg = V_pfi_update; - } - pfr_dynaddr_update(kt, dyn); -} - -static void -pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags) -{ - int e, size2 = 0; - struct ifg_member *ifgm; - - V_pfi_buffer_cnt = 0; - - if (kif->pfik_ifp != NULL) - pfi_instance_add(kif->pfik_ifp, net, flags); - else if (kif->pfik_group != NULL) { - IFNET_RLOCK(); - TAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next) - pfi_instance_add(ifgm->ifgm_ifp, net, flags); - IFNET_RUNLOCK(); - } - - if ((e = pfr_set_addrs(&kt->pfrkt_t, V_pfi_buffer, V_pfi_buffer_cnt, &size2, - NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK))) - printf("%s: cannot set %d new addresses into table %s: %d\n", - __func__, V_pfi_buffer_cnt, kt->pfrkt_name, e); -} - -static void -pfi_instance_add(struct ifnet *ifp, int net, int flags) -{ - struct ifaddr *ia; - int got4 = 0, got6 = 0; - int net2, af; - - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_list) { - if (ia->ifa_addr == NULL) - continue; - af = ia->ifa_addr->sa_family; - if (af != AF_INET && af != AF_INET6) - continue; - /* - * XXX: For point-to-point interfaces, (ifname:0) and IPv4, - * jump over addresses without a proper route to work - * around a problem with ppp not fully removing the - * address used during IPCP. - */ - if ((ifp->if_flags & IFF_POINTOPOINT) && - !(ia->ifa_flags & IFA_ROUTE) && - (flags & PFI_AFLAG_NOALIAS) && (af == AF_INET)) - continue; - if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6) - continue; - if ((flags & PFI_AFLAG_BROADCAST) && - !(ifp->if_flags & IFF_BROADCAST)) - continue; - if ((flags & PFI_AFLAG_PEER) && - !(ifp->if_flags & IFF_POINTOPOINT)) - continue; - if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 && - IN6_IS_ADDR_LINKLOCAL( - &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr)) - continue; - if (flags & PFI_AFLAG_NOALIAS) { - if (af == AF_INET && got4) - continue; - if (af == AF_INET6 && got6) - continue; - } - if (af == AF_INET) - got4 = 1; - else if (af == AF_INET6) - got6 = 1; - net2 = net; - if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) { - if (af == AF_INET) - net2 = pfi_unmask(&((struct sockaddr_in *) - ia->ifa_netmask)->sin_addr); - else if (af == AF_INET6) - net2 = pfi_unmask(&((struct sockaddr_in6 *) - ia->ifa_netmask)->sin6_addr); - } - if (af == AF_INET && net2 > 32) - net2 = 32; - if (flags & PFI_AFLAG_BROADCAST) - pfi_address_add(ia->ifa_broadaddr, af, net2); - else if (flags & PFI_AFLAG_PEER) - pfi_address_add(ia->ifa_dstaddr, af, net2); - else - pfi_address_add(ia->ifa_addr, af, net2); - } - IF_ADDR_RUNLOCK(ifp); -} - -static void -pfi_address_add(struct sockaddr *sa, int af, int net) -{ - struct pfr_addr *p; - int i; - - if (V_pfi_buffer_cnt >= V_pfi_buffer_max) { - int new_max = V_pfi_buffer_max * 2; - - if (new_max > PFI_BUFFER_MAX) { - printf("%s: address buffer full (%d/%d)\n", __func__, - V_pfi_buffer_cnt, PFI_BUFFER_MAX); - return; - } - p = malloc(new_max * sizeof(*V_pfi_buffer), PFI_MTYPE, - M_NOWAIT); - if (p == NULL) { - printf("%s: no memory to grow buffer (%d/%d)\n", - __func__, V_pfi_buffer_cnt, PFI_BUFFER_MAX); - return; - } - memcpy(V_pfi_buffer, p, V_pfi_buffer_cnt * sizeof(*V_pfi_buffer)); - /* no need to zero buffer */ - free(V_pfi_buffer, PFI_MTYPE); - V_pfi_buffer = p; - V_pfi_buffer_max = new_max; - } - if (af == AF_INET && net > 32) - net = 128; - p = V_pfi_buffer + V_pfi_buffer_cnt++; - bzero(p, sizeof(*p)); - p->pfra_af = af; - p->pfra_net = net; - if (af == AF_INET) - p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr; - else if (af == AF_INET6) { - p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr; - if (IN6_IS_SCOPE_EMBED(&p->pfra_ip6addr)) - p->pfra_ip6addr.s6_addr16[1] = 0; - } - /* mask network address bits */ - if (net < 128) - ((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8)); - for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++) - ((caddr_t)p)[i] = 0; -} - -void -pfi_dynaddr_remove(struct pfi_dynaddr *dyn) -{ - - KASSERT(dyn->pfid_kif != NULL, ("%s: null pfid_kif", __func__)); - KASSERT(dyn->pfid_kt != NULL, ("%s: null pfid_kt", __func__)); - - TAILQ_REMOVE(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); - pfi_kif_unref(dyn->pfid_kif); - pfr_detach_table(dyn->pfid_kt); - free(dyn, PFI_MTYPE); -} - -void -pfi_dynaddr_copyout(struct pf_addr_wrap *aw) -{ - - KASSERT(aw->type == PF_ADDR_DYNIFTL, - ("%s: type %u", __func__, aw->type)); - - if (aw->p.dyn == NULL || aw->p.dyn->pfid_kif == NULL) - return; - aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6; -} - -static int -pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q) -{ - return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ)); -} - -void -pfi_update_status(const char *name, struct pf_status *pfs) -{ - struct pfi_kif *p; - struct pfi_kif_cmp key; - struct ifg_member p_member, *ifgm; - TAILQ_HEAD(, ifg_member) ifg_members; - int i, j, k; - - strlcpy(key.pfik_name, name, sizeof(key.pfik_name)); - p = RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&key); - if (p == NULL) - return; - - if (p->pfik_group != NULL) { - bcopy(&p->pfik_group->ifg_members, &ifg_members, - sizeof(ifg_members)); - } else { - /* build a temporary list for p only */ - bzero(&p_member, sizeof(p_member)); - p_member.ifgm_ifp = p->pfik_ifp; - TAILQ_INIT(&ifg_members); - TAILQ_INSERT_TAIL(&ifg_members, &p_member, ifgm_next); - } - if (pfs) { - bzero(pfs->pcounters, sizeof(pfs->pcounters)); - bzero(pfs->bcounters, sizeof(pfs->bcounters)); - } - TAILQ_FOREACH(ifgm, &ifg_members, ifgm_next) { - if (ifgm->ifgm_ifp == NULL) - continue; - p = (struct pfi_kif *)ifgm->ifgm_ifp->if_pf_kif; - - /* just clear statistics */ - if (pfs == NULL) { - bzero(p->pfik_packets, sizeof(p->pfik_packets)); - bzero(p->pfik_bytes, sizeof(p->pfik_bytes)); - p->pfik_tzero = time_second; - continue; - } - for (i = 0; i < 2; i++) - for (j = 0; j < 2; j++) - for (k = 0; k < 2; k++) { - pfs->pcounters[i][j][k] += - p->pfik_packets[i][j][k]; - pfs->bcounters[i][j] += - p->pfik_bytes[i][j][k]; - } - } -} - -void -pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size) -{ - struct pfi_kif *p, *nextp; - int n = 0; - - for (p = RB_MIN(pfi_ifhead, &V_pfi_ifs); p; p = nextp) { - nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); - if (pfi_skip_if(name, p)) - continue; - if (*size <= n++) - break; - if (!p->pfik_tzero) - p->pfik_tzero = time_second; - bcopy(p, buf++, sizeof(*buf)); - nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); - } - *size = n; -} - -static int -pfi_skip_if(const char *filter, struct pfi_kif *p) -{ - int n; - - if (filter == NULL || !*filter) - return (0); - if (!strcmp(p->pfik_name, filter)) - return (0); /* exact match */ - n = strlen(filter); - if (n < 1 || n >= IFNAMSIZ) - return (1); /* sanity check */ - if (filter[n-1] >= '0' && filter[n-1] <= '9') - return (1); /* only do exact match in that case */ - if (strncmp(p->pfik_name, filter, n)) - return (1); /* prefix doesn't match */ - return (p->pfik_name[n] < '0' || p->pfik_name[n] > '9'); -} - -int -pfi_set_flags(const char *name, int flags) -{ - struct pfi_kif *p; - - RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) { - if (pfi_skip_if(name, p)) - continue; - p->pfik_flags |= flags; - } - return (0); -} - -int -pfi_clear_flags(const char *name, int flags) -{ - struct pfi_kif *p; - - RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) { - if (pfi_skip_if(name, p)) - continue; - p->pfik_flags &= ~flags; - } - return (0); -} - -/* from pf_print_state.c */ -static int -pfi_unmask(void *addr) -{ - struct pf_addr *m = addr; - int i = 31, j = 0, b = 0; - u_int32_t tmp; - - while (j < 4 && m->addr32[j] == 0xffffffff) { - b += 32; - j++; - } - if (j < 4) { - tmp = ntohl(m->addr32[j]); - for (i = 31; tmp & (1 << i); --i) - b++; - } - return (b); -} - -static void -pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) -{ - - CURVNET_SET(ifp->if_vnet); - pfi_attach_ifnet(ifp); -#ifdef ALTQ - PF_RULES_WLOCK(); - pf_altq_ifnet_event(ifp, 0); - PF_RULES_WUNLOCK(); -#endif - CURVNET_RESTORE(); -} - -static void -pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) -{ - struct pfi_kif *kif = (struct pfi_kif *)ifp->if_pf_kif; - - CURVNET_SET(ifp->if_vnet); - PF_RULES_WLOCK(); - V_pfi_update++; - pfi_kif_update(kif); - - kif->pfik_ifp = NULL; - ifp->if_pf_kif = NULL; -#ifdef ALTQ - pf_altq_ifnet_event(ifp, 1); -#endif - PF_RULES_WUNLOCK(); - CURVNET_RESTORE(); -} - -static void -pfi_attach_group_event(void *arg , struct ifg_group *ifg) -{ - - CURVNET_SET((struct vnet *)arg); - pfi_attach_ifgroup(ifg); - CURVNET_RESTORE(); -} - -static void -pfi_change_group_event(void *arg, char *gname) -{ - struct pfi_kif *kif; - - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - - CURVNET_SET((struct vnet *)arg); - PF_RULES_WLOCK(); - V_pfi_update++; - kif = pfi_kif_attach(kif, gname); - pfi_kif_update(kif); - PF_RULES_WUNLOCK(); - CURVNET_RESTORE(); -} - -static void -pfi_detach_group_event(void *arg, struct ifg_group *ifg) -{ - struct pfi_kif *kif = (struct pfi_kif *)ifg->ifg_pf_kif; - - CURVNET_SET((struct vnet *)arg); - PF_RULES_WLOCK(); - V_pfi_update++; - - kif->pfik_group = NULL; - ifg->ifg_pf_kif = NULL; - PF_RULES_WUNLOCK(); - CURVNET_RESTORE(); -} - -static void -pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp) -{ - - CURVNET_SET(ifp->if_vnet); - PF_RULES_WLOCK(); - if (ifp && ifp->if_pf_kif) { - V_pfi_update++; - pfi_kif_update(ifp->if_pf_kif); - } - PF_RULES_WUNLOCK(); - CURVNET_RESTORE(); -} diff --git a/sys/contrib/pf/net/pf_ioctl.c b/sys/contrib/pf/net/pf_ioctl.c deleted file mode 100644 index 032f051277159..0000000000000 --- a/sys/contrib/pf/net/pf_ioctl.c +++ /dev/null @@ -1,3774 +0,0 @@ -/* $OpenBSD: pf_ioctl.c,v 1.213 2009/02/15 21:46:12 mbalmer Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Effort sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F30602-01-2-0537. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_bpf.h" -#include "opt_pf.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef INET6 -#include -#endif /* INET6 */ - -#ifdef ALTQ -#include -#endif - -static int pfattach(void); -static struct pf_pool *pf_get_pool(char *, u_int32_t, u_int8_t, u_int32_t, - u_int8_t, u_int8_t, u_int8_t); - -static void pf_mv_pool(struct pf_palist *, struct pf_palist *); -static void pf_empty_pool(struct pf_palist *); -static int pfioctl(struct cdev *, u_long, caddr_t, int, - struct thread *); -#ifdef ALTQ -static int pf_begin_altq(u_int32_t *); -static int pf_rollback_altq(u_int32_t); -static int pf_commit_altq(u_int32_t); -static int pf_enable_altq(struct pf_altq *); -static int pf_disable_altq(struct pf_altq *); -static u_int32_t pf_qname2qid(char *); -static void pf_qid_unref(u_int32_t); -#endif /* ALTQ */ -static int pf_begin_rules(u_int32_t *, int, const char *); -static int pf_rollback_rules(u_int32_t, int, char *); -static int pf_setup_pfsync_matching(struct pf_ruleset *); -static void pf_hash_rule(MD5_CTX *, struct pf_rule *); -static void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *); -static int pf_commit_rules(u_int32_t, int, char *); -static int pf_addr_setup(struct pf_ruleset *, - struct pf_addr_wrap *, sa_family_t); -static void pf_addr_copyout(struct pf_addr_wrap *); - -VNET_DEFINE(struct pf_rule, pf_default_rule); - -#ifdef ALTQ -static VNET_DEFINE(int, pf_altq_running); -#define V_pf_altq_running VNET(pf_altq_running) -#endif - -#define TAGID_MAX 50000 -struct pf_tagname { - TAILQ_ENTRY(pf_tagname) entries; - char name[PF_TAG_NAME_SIZE]; - uint16_t tag; - int ref; -}; - -TAILQ_HEAD(pf_tags, pf_tagname); -#define V_pf_tags VNET(pf_tags) -VNET_DEFINE(struct pf_tags, pf_tags); -#define V_pf_qids VNET(pf_qids) -VNET_DEFINE(struct pf_tags, pf_qids); -static MALLOC_DEFINE(M_PFTAG, "pf_tag", "pf(4) tag names"); -static MALLOC_DEFINE(M_PFALTQ, "pf_altq", "pf(4) altq configuration db"); -static MALLOC_DEFINE(M_PFRULE, "pf_rule", "pf(4) rules"); - -#if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE) -#error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE -#endif - -static u_int16_t tagname2tag(struct pf_tags *, char *); -static u_int16_t pf_tagname2tag(char *); -static void tag_unref(struct pf_tags *, u_int16_t); - -#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x - -struct cdev *pf_dev; - -/* - * XXX - These are new and need to be checked when moveing to a new version - */ -static void pf_clear_states(void); -static int pf_clear_tables(void); -static void pf_clear_srcnodes(struct pf_src_node *); -static void pf_tbladdr_copyout(struct pf_addr_wrap *); - -/* - * Wrapper functions for pfil(9) hooks - */ -#ifdef INET -static int pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, - int dir, struct inpcb *inp); -static int pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, - int dir, struct inpcb *inp); -#endif -#ifdef INET6 -static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, - int dir, struct inpcb *inp); -static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, - int dir, struct inpcb *inp); -#endif - -static int hook_pf(void); -static int dehook_pf(void); -static int shutdown_pf(void); -static int pf_load(void); -static int pf_unload(void); - -static struct cdevsw pf_cdevsw = { - .d_ioctl = pfioctl, - .d_name = PF_NAME, - .d_version = D_VERSION, -}; - -static volatile VNET_DEFINE(int, pf_pfil_hooked); -#define V_pf_pfil_hooked VNET(pf_pfil_hooked) -VNET_DEFINE(int, pf_end_threads); - -struct rwlock pf_rules_lock; - -/* pfsync */ -pfsync_state_import_t *pfsync_state_import_ptr = NULL; -pfsync_insert_state_t *pfsync_insert_state_ptr = NULL; -pfsync_update_state_t *pfsync_update_state_ptr = NULL; -pfsync_delete_state_t *pfsync_delete_state_ptr = NULL; -pfsync_clear_states_t *pfsync_clear_states_ptr = NULL; -pfsync_defer_t *pfsync_defer_ptr = NULL; -/* pflog */ -pflog_packet_t *pflog_packet_ptr = NULL; - -static int -pfattach(void) -{ - u_int32_t *my_timeout = V_pf_default_rule.timeout; - int error; - - pf_initialize(); - pfr_initialize(); - pfi_initialize(); - pf_normalize_init(); - - V_pf_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT; - V_pf_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT; - - RB_INIT(&V_pf_anchors); - pf_init_ruleset(&pf_main_ruleset); - - /* default rule should never be garbage collected */ - V_pf_default_rule.entries.tqe_prev = &V_pf_default_rule.entries.tqe_next; - V_pf_default_rule.action = PF_PASS; - V_pf_default_rule.nr = -1; - V_pf_default_rule.rtableid = -1; - - /* initialize default timeouts */ - my_timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; - my_timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; - my_timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; - my_timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; - my_timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; - my_timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; - my_timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; - my_timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; - my_timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; - my_timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; - my_timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; - my_timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; - my_timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; - my_timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; - my_timeout[PFTM_FRAG] = PFTM_FRAG_VAL; - my_timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL; - my_timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL; - my_timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL; - my_timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; - my_timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; - - bzero(&V_pf_status, sizeof(V_pf_status)); - V_pf_status.debug = PF_DEBUG_URGENT; - - V_pf_pfil_hooked = 0; - - /* XXX do our best to avoid a conflict */ - V_pf_status.hostid = arc4random(); - - if ((error = kproc_create(pf_purge_thread, curvnet, NULL, 0, 0, - "pf purge")) != 0) - /* XXXGL: leaked all above. */ - return (error); - if ((error = swi_add(NULL, "pf send", pf_intr, curvnet, SWI_NET, - INTR_MPSAFE, &V_pf_swi_cookie)) != 0) - /* XXXGL: leaked all above. */ - return (error); - - return (0); -} - -static struct pf_pool * -pf_get_pool(char *anchor, u_int32_t ticket, u_int8_t rule_action, - u_int32_t rule_number, u_int8_t r_last, u_int8_t active, - u_int8_t check_ticket) -{ - struct pf_ruleset *ruleset; - struct pf_rule *rule; - int rs_num; - - ruleset = pf_find_ruleset(anchor); - if (ruleset == NULL) - return (NULL); - rs_num = pf_get_ruleset_number(rule_action); - if (rs_num >= PF_RULESET_MAX) - return (NULL); - if (active) { - if (check_ticket && ticket != - ruleset->rules[rs_num].active.ticket) - return (NULL); - if (r_last) - rule = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, - pf_rulequeue); - else - rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); - } else { - if (check_ticket && ticket != - ruleset->rules[rs_num].inactive.ticket) - return (NULL); - if (r_last) - rule = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, - pf_rulequeue); - else - rule = TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr); - } - if (!r_last) { - while ((rule != NULL) && (rule->nr != rule_number)) - rule = TAILQ_NEXT(rule, entries); - } - if (rule == NULL) - return (NULL); - - return (&rule->rpool); -} - -static void -pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb) -{ - struct pf_pooladdr *mv_pool_pa; - - while ((mv_pool_pa = TAILQ_FIRST(poola)) != NULL) { - TAILQ_REMOVE(poola, mv_pool_pa, entries); - TAILQ_INSERT_TAIL(poolb, mv_pool_pa, entries); - } -} - -static void -pf_empty_pool(struct pf_palist *poola) -{ - struct pf_pooladdr *pa; - - while ((pa = TAILQ_FIRST(poola)) != NULL) { - switch (pa->addr.type) { - case PF_ADDR_DYNIFTL: - pfi_dynaddr_remove(pa->addr.p.dyn); - break; - case PF_ADDR_TABLE: - pfr_detach_table(pa->addr.p.tbl); - break; - } - if (pa->kif) - pfi_kif_unref(pa->kif); - TAILQ_REMOVE(poola, pa, entries); - free(pa, M_PFRULE); - } -} - -static void -pf_unlink_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) -{ - - PF_RULES_WASSERT(); - - TAILQ_REMOVE(rulequeue, rule, entries); - - PF_UNLNKDRULES_LOCK(); - rule->rule_flag |= PFRULE_REFS; - TAILQ_INSERT_TAIL(&V_pf_unlinked_rules, rule, entries); - PF_UNLNKDRULES_UNLOCK(); -} - -void -pf_free_rule(struct pf_rule *rule) -{ - - PF_RULES_WASSERT(); - - if (rule->tag) - tag_unref(&V_pf_tags, rule->tag); - if (rule->match_tag) - tag_unref(&V_pf_tags, rule->match_tag); -#ifdef ALTQ - if (rule->pqid != rule->qid) - pf_qid_unref(rule->pqid); - pf_qid_unref(rule->qid); -#endif - switch (rule->src.addr.type) { - case PF_ADDR_DYNIFTL: - pfi_dynaddr_remove(rule->src.addr.p.dyn); - break; - case PF_ADDR_TABLE: - pfr_detach_table(rule->src.addr.p.tbl); - break; - } - switch (rule->dst.addr.type) { - case PF_ADDR_DYNIFTL: - pfi_dynaddr_remove(rule->dst.addr.p.dyn); - break; - case PF_ADDR_TABLE: - pfr_detach_table(rule->dst.addr.p.tbl); - break; - } - if (rule->overload_tbl) - pfr_detach_table(rule->overload_tbl); - if (rule->kif) - pfi_kif_unref(rule->kif); - pf_anchor_remove(rule); - pf_empty_pool(&rule->rpool.list); - free(rule, M_PFRULE); -} - -static u_int16_t -tagname2tag(struct pf_tags *head, char *tagname) -{ - struct pf_tagname *tag, *p = NULL; - u_int16_t new_tagid = 1; - - PF_RULES_WASSERT(); - - TAILQ_FOREACH(tag, head, entries) - if (strcmp(tagname, tag->name) == 0) { - tag->ref++; - return (tag->tag); - } - - /* - * to avoid fragmentation, we do a linear search from the beginning - * and take the first free slot we find. if there is none or the list - * is empty, append a new entry at the end. - */ - - /* new entry */ - if (!TAILQ_EMPTY(head)) - for (p = TAILQ_FIRST(head); p != NULL && - p->tag == new_tagid; p = TAILQ_NEXT(p, entries)) - new_tagid = p->tag + 1; - - if (new_tagid > TAGID_MAX) - return (0); - - /* allocate and fill new struct pf_tagname */ - tag = malloc(sizeof(*tag), M_PFTAG, M_NOWAIT|M_ZERO); - if (tag == NULL) - return (0); - strlcpy(tag->name, tagname, sizeof(tag->name)); - tag->tag = new_tagid; - tag->ref++; - - if (p != NULL) /* insert new entry before p */ - TAILQ_INSERT_BEFORE(p, tag, entries); - else /* either list empty or no free slot in between */ - TAILQ_INSERT_TAIL(head, tag, entries); - - return (tag->tag); -} - -static void -tag_unref(struct pf_tags *head, u_int16_t tag) -{ - struct pf_tagname *p, *next; - - PF_RULES_WASSERT(); - - for (p = TAILQ_FIRST(head); p != NULL; p = next) { - next = TAILQ_NEXT(p, entries); - if (tag == p->tag) { - if (--p->ref == 0) { - TAILQ_REMOVE(head, p, entries); - free(p, M_PFTAG); - } - break; - } - } -} - -static u_int16_t -pf_tagname2tag(char *tagname) -{ - return (tagname2tag(&V_pf_tags, tagname)); -} - -#ifdef ALTQ -static u_int32_t -pf_qname2qid(char *qname) -{ - return ((u_int32_t)tagname2tag(&V_pf_qids, qname)); -} - -static void -pf_qid_unref(u_int32_t qid) -{ - tag_unref(&V_pf_qids, (u_int16_t)qid); -} - -static int -pf_begin_altq(u_int32_t *ticket) -{ - struct pf_altq *altq; - int error = 0; - - PF_RULES_WASSERT(); - - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0 && - (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { - /* detach and destroy the discipline */ - error = altq_remove(altq); - } else - pf_qid_unref(altq->qid); - free(altq, M_PFALTQ); - } - if (error) - return (error); - *ticket = ++V_ticket_altqs_inactive; - V_altqs_inactive_open = 1; - return (0); -} - -static int -pf_rollback_altq(u_int32_t ticket) -{ - struct pf_altq *altq; - int error = 0; - - PF_RULES_WASSERT(); - - if (!V_altqs_inactive_open || ticket != V_ticket_altqs_inactive) - return (0); - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0 && - (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { - /* detach and destroy the discipline */ - error = altq_remove(altq); - } else - pf_qid_unref(altq->qid); - free(altq, M_PFALTQ); - } - V_altqs_inactive_open = 0; - return (error); -} - -static int -pf_commit_altq(u_int32_t ticket) -{ - struct pf_altqqueue *old_altqs; - struct pf_altq *altq; - int err, error = 0; - - PF_RULES_WASSERT(); - - if (!V_altqs_inactive_open || ticket != V_ticket_altqs_inactive) - return (EBUSY); - - /* swap altqs, keep the old. */ - old_altqs = V_pf_altqs_active; - V_pf_altqs_active = V_pf_altqs_inactive; - V_pf_altqs_inactive = old_altqs; - V_ticket_altqs_active = V_ticket_altqs_inactive; - - /* Attach new disciplines */ - TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { - if (altq->qname[0] == 0 && - (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { - /* attach the discipline */ - error = altq_pfattach(altq); - if (error == 0 && V_pf_altq_running) - error = pf_enable_altq(altq); - if (error != 0) - return (error); - } - } - - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0 && - (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { - /* detach and destroy the discipline */ - if (V_pf_altq_running) - error = pf_disable_altq(altq); - err = altq_pfdetach(altq); - if (err != 0 && error == 0) - error = err; - err = altq_remove(altq); - if (err != 0 && error == 0) - error = err; - } else - pf_qid_unref(altq->qid); - free(altq, M_PFALTQ); - } - - V_altqs_inactive_open = 0; - return (error); -} - -static int -pf_enable_altq(struct pf_altq *altq) -{ - struct ifnet *ifp; - struct tb_profile tb; - int error = 0; - - if ((ifp = ifunit(altq->ifname)) == NULL) - return (EINVAL); - - if (ifp->if_snd.altq_type != ALTQT_NONE) - error = altq_enable(&ifp->if_snd); - - /* set tokenbucket regulator */ - if (error == 0 && ifp != NULL && ALTQ_IS_ENABLED(&ifp->if_snd)) { - tb.rate = altq->ifbandwidth; - tb.depth = altq->tbrsize; - error = tbr_set(&ifp->if_snd, &tb); - } - - return (error); -} - -static int -pf_disable_altq(struct pf_altq *altq) -{ - struct ifnet *ifp; - struct tb_profile tb; - int error; - - if ((ifp = ifunit(altq->ifname)) == NULL) - return (EINVAL); - - /* - * when the discipline is no longer referenced, it was overridden - * by a new one. if so, just return. - */ - if (altq->altq_disc != ifp->if_snd.altq_disc) - return (0); - - error = altq_disable(&ifp->if_snd); - - if (error == 0) { - /* clear tokenbucket regulator */ - tb.rate = 0; - error = tbr_set(&ifp->if_snd, &tb); - } - - return (error); -} - -void -pf_altq_ifnet_event(struct ifnet *ifp, int remove) -{ - struct ifnet *ifp1; - struct pf_altq *a1, *a2, *a3; - u_int32_t ticket; - int error = 0; - - /* Interrupt userland queue modifications */ - if (V_altqs_inactive_open) - pf_rollback_altq(V_ticket_altqs_inactive); - - /* Start new altq ruleset */ - if (pf_begin_altq(&ticket)) - return; - - /* Copy the current active set */ - TAILQ_FOREACH(a1, V_pf_altqs_active, entries) { - a2 = malloc(sizeof(*a2), M_PFALTQ, M_NOWAIT); - if (a2 == NULL) { - error = ENOMEM; - break; - } - bcopy(a1, a2, sizeof(struct pf_altq)); - - if (a2->qname[0] != 0) { - if ((a2->qid = pf_qname2qid(a2->qname)) == 0) { - error = EBUSY; - free(a2, M_PFALTQ); - break; - } - a2->altq_disc = NULL; - TAILQ_FOREACH(a3, V_pf_altqs_inactive, entries) { - if (strncmp(a3->ifname, a2->ifname, - IFNAMSIZ) == 0 && a3->qname[0] == 0) { - a2->altq_disc = a3->altq_disc; - break; - } - } - } - /* Deactivate the interface in question */ - a2->local_flags &= ~PFALTQ_FLAG_IF_REMOVED; - if ((ifp1 = ifunit(a2->ifname)) == NULL || - (remove && ifp1 == ifp)) { - a2->local_flags |= PFALTQ_FLAG_IF_REMOVED; - } else { - error = altq_add(a2); - - if (ticket != V_ticket_altqs_inactive) - error = EBUSY; - - if (error) { - free(a2, M_PFALTQ); - break; - } - } - - TAILQ_INSERT_TAIL(V_pf_altqs_inactive, a2, entries); - } - - if (error != 0) - pf_rollback_altq(ticket); - else - pf_commit_altq(ticket); -} -#endif /* ALTQ */ - -static int -pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor) -{ - struct pf_ruleset *rs; - struct pf_rule *rule; - - PF_RULES_WASSERT(); - - if (rs_num < 0 || rs_num >= PF_RULESET_MAX) - return (EINVAL); - rs = pf_find_or_create_ruleset(anchor); - if (rs == NULL) - return (EINVAL); - while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) { - pf_unlink_rule(rs->rules[rs_num].inactive.ptr, rule); - rs->rules[rs_num].inactive.rcount--; - } - *ticket = ++rs->rules[rs_num].inactive.ticket; - rs->rules[rs_num].inactive.open = 1; - return (0); -} - -static int -pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor) -{ - struct pf_ruleset *rs; - struct pf_rule *rule; - - PF_RULES_WASSERT(); - - if (rs_num < 0 || rs_num >= PF_RULESET_MAX) - return (EINVAL); - rs = pf_find_ruleset(anchor); - if (rs == NULL || !rs->rules[rs_num].inactive.open || - rs->rules[rs_num].inactive.ticket != ticket) - return (0); - while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) { - pf_unlink_rule(rs->rules[rs_num].inactive.ptr, rule); - rs->rules[rs_num].inactive.rcount--; - } - rs->rules[rs_num].inactive.open = 0; - return (0); -} - -#define PF_MD5_UPD(st, elm) \ - MD5Update(ctx, (u_int8_t *) &(st)->elm, sizeof((st)->elm)) - -#define PF_MD5_UPD_STR(st, elm) \ - MD5Update(ctx, (u_int8_t *) (st)->elm, strlen((st)->elm)) - -#define PF_MD5_UPD_HTONL(st, elm, stor) do { \ - (stor) = htonl((st)->elm); \ - MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int32_t));\ -} while (0) - -#define PF_MD5_UPD_HTONS(st, elm, stor) do { \ - (stor) = htons((st)->elm); \ - MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int16_t));\ -} while (0) - -static void -pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) -{ - PF_MD5_UPD(pfr, addr.type); - switch (pfr->addr.type) { - case PF_ADDR_DYNIFTL: - PF_MD5_UPD(pfr, addr.v.ifname); - PF_MD5_UPD(pfr, addr.iflags); - break; - case PF_ADDR_TABLE: - PF_MD5_UPD(pfr, addr.v.tblname); - break; - case PF_ADDR_ADDRMASK: - /* XXX ignore af? */ - PF_MD5_UPD(pfr, addr.v.a.addr.addr32); - PF_MD5_UPD(pfr, addr.v.a.mask.addr32); - break; - } - - PF_MD5_UPD(pfr, port[0]); - PF_MD5_UPD(pfr, port[1]); - PF_MD5_UPD(pfr, neg); - PF_MD5_UPD(pfr, port_op); -} - -static void -pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule) -{ - u_int16_t x; - u_int32_t y; - - pf_hash_rule_addr(ctx, &rule->src); - pf_hash_rule_addr(ctx, &rule->dst); - PF_MD5_UPD_STR(rule, label); - PF_MD5_UPD_STR(rule, ifname); - PF_MD5_UPD_STR(rule, match_tagname); - PF_MD5_UPD_HTONS(rule, match_tag, x); /* dup? */ - PF_MD5_UPD_HTONL(rule, os_fingerprint, y); - PF_MD5_UPD_HTONL(rule, prob, y); - PF_MD5_UPD_HTONL(rule, uid.uid[0], y); - PF_MD5_UPD_HTONL(rule, uid.uid[1], y); - PF_MD5_UPD(rule, uid.op); - PF_MD5_UPD_HTONL(rule, gid.gid[0], y); - PF_MD5_UPD_HTONL(rule, gid.gid[1], y); - PF_MD5_UPD(rule, gid.op); - PF_MD5_UPD_HTONL(rule, rule_flag, y); - PF_MD5_UPD(rule, action); - PF_MD5_UPD(rule, direction); - PF_MD5_UPD(rule, af); - PF_MD5_UPD(rule, quick); - PF_MD5_UPD(rule, ifnot); - PF_MD5_UPD(rule, match_tag_not); - PF_MD5_UPD(rule, natpass); - PF_MD5_UPD(rule, keep_state); - PF_MD5_UPD(rule, proto); - PF_MD5_UPD(rule, type); - PF_MD5_UPD(rule, code); - PF_MD5_UPD(rule, flags); - PF_MD5_UPD(rule, flagset); - PF_MD5_UPD(rule, allow_opts); - PF_MD5_UPD(rule, rt); - PF_MD5_UPD(rule, tos); -} - -static int -pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) -{ - struct pf_ruleset *rs; - struct pf_rule *rule, **old_array; - struct pf_rulequeue *old_rules; - int error; - u_int32_t old_rcount; - - PF_RULES_WASSERT(); - - if (rs_num < 0 || rs_num >= PF_RULESET_MAX) - return (EINVAL); - rs = pf_find_ruleset(anchor); - if (rs == NULL || !rs->rules[rs_num].inactive.open || - ticket != rs->rules[rs_num].inactive.ticket) - return (EBUSY); - - /* Calculate checksum for the main ruleset */ - if (rs == &pf_main_ruleset) { - error = pf_setup_pfsync_matching(rs); - if (error != 0) - return (error); - } - - /* Swap rules, keep the old. */ - old_rules = rs->rules[rs_num].active.ptr; - old_rcount = rs->rules[rs_num].active.rcount; - old_array = rs->rules[rs_num].active.ptr_array; - - rs->rules[rs_num].active.ptr = - rs->rules[rs_num].inactive.ptr; - rs->rules[rs_num].active.ptr_array = - rs->rules[rs_num].inactive.ptr_array; - rs->rules[rs_num].active.rcount = - rs->rules[rs_num].inactive.rcount; - rs->rules[rs_num].inactive.ptr = old_rules; - rs->rules[rs_num].inactive.ptr_array = old_array; - rs->rules[rs_num].inactive.rcount = old_rcount; - - rs->rules[rs_num].active.ticket = - rs->rules[rs_num].inactive.ticket; - pf_calc_skip_steps(rs->rules[rs_num].active.ptr); - - - /* Purge the old rule list. */ - while ((rule = TAILQ_FIRST(old_rules)) != NULL) - pf_unlink_rule(old_rules, rule); - if (rs->rules[rs_num].inactive.ptr_array) - free(rs->rules[rs_num].inactive.ptr_array, M_TEMP); - rs->rules[rs_num].inactive.ptr_array = NULL; - rs->rules[rs_num].inactive.rcount = 0; - rs->rules[rs_num].inactive.open = 0; - pf_remove_if_empty_ruleset(rs); - - return (0); -} - -static int -pf_setup_pfsync_matching(struct pf_ruleset *rs) -{ - MD5_CTX ctx; - struct pf_rule *rule; - int rs_cnt; - u_int8_t digest[PF_MD5_DIGEST_LENGTH]; - - MD5Init(&ctx); - for (rs_cnt = 0; rs_cnt < PF_RULESET_MAX; rs_cnt++) { - /* XXX PF_RULESET_SCRUB as well? */ - if (rs_cnt == PF_RULESET_SCRUB) - continue; - - if (rs->rules[rs_cnt].inactive.ptr_array) - free(rs->rules[rs_cnt].inactive.ptr_array, M_TEMP); - rs->rules[rs_cnt].inactive.ptr_array = NULL; - - if (rs->rules[rs_cnt].inactive.rcount) { - rs->rules[rs_cnt].inactive.ptr_array = - malloc(sizeof(caddr_t) * - rs->rules[rs_cnt].inactive.rcount, - M_TEMP, M_NOWAIT); - - if (!rs->rules[rs_cnt].inactive.ptr_array) - return (ENOMEM); - } - - TAILQ_FOREACH(rule, rs->rules[rs_cnt].inactive.ptr, - entries) { - pf_hash_rule(&ctx, rule); - (rs->rules[rs_cnt].inactive.ptr_array)[rule->nr] = rule; - } - } - - MD5Final(digest, &ctx); - memcpy(V_pf_status.pf_chksum, digest, sizeof(V_pf_status.pf_chksum)); - return (0); -} - -static int -pf_addr_setup(struct pf_ruleset *ruleset, struct pf_addr_wrap *addr, - sa_family_t af) -{ - int error = 0; - - switch (addr->type) { - case PF_ADDR_TABLE: - addr->p.tbl = pfr_attach_table(ruleset, addr->v.tblname); - if (addr->p.tbl == NULL) - error = ENOMEM; - break; - case PF_ADDR_DYNIFTL: - error = pfi_dynaddr_setup(addr, af); - break; - } - - return (error); -} - -static void -pf_addr_copyout(struct pf_addr_wrap *addr) -{ - - switch (addr->type) { - case PF_ADDR_DYNIFTL: - pfi_dynaddr_copyout(addr); - break; - case PF_ADDR_TABLE: - pf_tbladdr_copyout(addr); - break; - } -} - -static int -pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) -{ - int error = 0; - - CURVNET_SET(TD_TO_VNET(td)); - - /* XXX keep in sync with switch() below */ - if (securelevel_gt(td->td_ucred, 2)) - switch (cmd) { - case DIOCGETRULES: - case DIOCGETRULE: - case DIOCGETADDRS: - case DIOCGETADDR: - case DIOCGETSTATE: - case DIOCSETSTATUSIF: - case DIOCGETSTATUS: - case DIOCCLRSTATUS: - case DIOCNATLOOK: - case DIOCSETDEBUG: - case DIOCGETSTATES: - case DIOCGETTIMEOUT: - case DIOCCLRRULECTRS: - case DIOCGETLIMIT: - case DIOCGETALTQS: - case DIOCGETALTQ: - case DIOCGETQSTATS: - case DIOCGETRULESETS: - case DIOCGETRULESET: - case DIOCRGETTABLES: - case DIOCRGETTSTATS: - case DIOCRCLRTSTATS: - case DIOCRCLRADDRS: - case DIOCRADDADDRS: - case DIOCRDELADDRS: - case DIOCRSETADDRS: - case DIOCRGETADDRS: - case DIOCRGETASTATS: - case DIOCRCLRASTATS: - case DIOCRTSTADDRS: - case DIOCOSFPGET: - case DIOCGETSRCNODES: - case DIOCCLRSRCNODES: - case DIOCIGETIFACES: - case DIOCGIFSPEED: - case DIOCSETIFFLAG: - case DIOCCLRIFFLAG: - break; - case DIOCRCLRTABLES: - case DIOCRADDTABLES: - case DIOCRDELTABLES: - case DIOCRSETTFLAGS: - if (((struct pfioc_table *)addr)->pfrio_flags & - PFR_FLAG_DUMMY) - break; /* dummy operation ok */ - return (EPERM); - default: - return (EPERM); - } - - if (!(flags & FWRITE)) - switch (cmd) { - case DIOCGETRULES: - case DIOCGETADDRS: - case DIOCGETADDR: - case DIOCGETSTATE: - case DIOCGETSTATUS: - case DIOCGETSTATES: - case DIOCGETTIMEOUT: - case DIOCGETLIMIT: - case DIOCGETALTQS: - case DIOCGETALTQ: - case DIOCGETQSTATS: - case DIOCGETRULESETS: - case DIOCGETRULESET: - case DIOCNATLOOK: - case DIOCRGETTABLES: - case DIOCRGETTSTATS: - case DIOCRGETADDRS: - case DIOCRGETASTATS: - case DIOCRTSTADDRS: - case DIOCOSFPGET: - case DIOCGETSRCNODES: - case DIOCIGETIFACES: - case DIOCGIFSPEED: - break; - case DIOCRCLRTABLES: - case DIOCRADDTABLES: - case DIOCRDELTABLES: - case DIOCRCLRTSTATS: - case DIOCRCLRADDRS: - case DIOCRADDADDRS: - case DIOCRDELADDRS: - case DIOCRSETADDRS: - case DIOCRSETTFLAGS: - if (((struct pfioc_table *)addr)->pfrio_flags & - PFR_FLAG_DUMMY) { - flags |= FWRITE; /* need write lock for dummy */ - break; /* dummy operation ok */ - } - return (EACCES); - case DIOCGETRULE: - if (((struct pfioc_rule *)addr)->action == - PF_GET_CLR_CNTR) - return (EACCES); - break; - default: - return (EACCES); - } - - switch (cmd) { - case DIOCSTART: - PF_RULES_WLOCK(); - if (V_pf_status.running) - error = EEXIST; - else { - int cpu; - - PF_RULES_WUNLOCK(); - error = hook_pf(); - if (error) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: pfil registration failed\n")); - break; - } - PF_RULES_WLOCK(); - V_pf_status.running = 1; - V_pf_status.since = time_second; - - CPU_FOREACH(cpu) - V_pf_stateid[cpu] = time_second; - - DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); - } - PF_RULES_WUNLOCK(); - break; - - case DIOCSTOP: - PF_RULES_WLOCK(); - if (!V_pf_status.running) - error = ENOENT; - else { - V_pf_status.running = 0; - PF_RULES_WUNLOCK(); - error = dehook_pf(); - if (error) { - V_pf_status.running = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: pfil unregistration failed\n")); - } - PF_RULES_WLOCK(); - V_pf_status.since = time_second; - DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); - } - PF_RULES_WUNLOCK(); - break; - - case DIOCADDRULE: { - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *rule, *tail; - struct pf_pooladdr *pa; - struct pfi_kif *kif = NULL; - int rs_num; - - if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { - error = EINVAL; - break; - } -#ifndef INET - if (pr->rule.af == AF_INET) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET */ -#ifndef INET6 - if (pr->rule.af == AF_INET6) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET6 */ - - rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK); - bcopy(&pr->rule, rule, sizeof(struct pf_rule)); - if (rule->ifname[0]) - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - rule->cuid = td->td_ucred->cr_ruid; - rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; - TAILQ_INIT(&rule->rpool.list); - -#define ERROUT(x) { error = (x); goto DIOCADDRULE_error; } - - PF_RULES_WLOCK(); - pr->anchor[sizeof(pr->anchor) - 1] = 0; - ruleset = pf_find_ruleset(pr->anchor); - if (ruleset == NULL) - ERROUT(EINVAL); - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) - ERROUT(EINVAL); - if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { - DPFPRINTF(PF_DEBUG_MISC, - ("ticket: %d != [%d]%d\n", pr->ticket, rs_num, - ruleset->rules[rs_num].inactive.ticket)); - ERROUT(EBUSY); - } - if (pr->pool_ticket != V_ticket_pabuf) { - DPFPRINTF(PF_DEBUG_MISC, - ("pool_ticket: %d != %d\n", pr->pool_ticket, - V_ticket_pabuf)); - ERROUT(EBUSY); - } - - tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, - pf_rulequeue); - if (tail) - rule->nr = tail->nr + 1; - else - rule->nr = 0; - if (rule->ifname[0]) { - rule->kif = pfi_kif_attach(kif, rule->ifname); - pfi_kif_ref(rule->kif); - } else - rule->kif = NULL; - - if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) - error = EBUSY; - -#ifdef ALTQ - /* set queue IDs */ - if (rule->qname[0] != 0) { - if ((rule->qid = pf_qname2qid(rule->qname)) == 0) - error = EBUSY; - else if (rule->pqname[0] != 0) { - if ((rule->pqid = - pf_qname2qid(rule->pqname)) == 0) - error = EBUSY; - } else - rule->pqid = rule->qid; - } -#endif - if (rule->tagname[0]) - if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) - error = EBUSY; - if (rule->match_tagname[0]) - if ((rule->match_tag = - pf_tagname2tag(rule->match_tagname)) == 0) - error = EBUSY; - if (rule->rt && !rule->direction) - error = EINVAL; - if (!rule->log) - rule->logif = 0; - if (rule->logif >= PFLOGIFS_MAX) - error = EINVAL; - if (pf_addr_setup(ruleset, &rule->src.addr, rule->af)) - error = ENOMEM; - if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af)) - error = ENOMEM; - if (pf_anchor_setup(rule, ruleset, pr->anchor_call)) - error = EINVAL; - TAILQ_FOREACH(pa, &V_pf_pabuf, entries) - if (pa->addr.type == PF_ADDR_TABLE) { - pa->addr.p.tbl = pfr_attach_table(ruleset, - pa->addr.v.tblname); - if (pa->addr.p.tbl == NULL) - error = ENOMEM; - } - - if (rule->overload_tblname[0]) { - if ((rule->overload_tbl = pfr_attach_table(ruleset, - rule->overload_tblname)) == NULL) - error = EINVAL; - else - rule->overload_tbl->pfrkt_flags |= - PFR_TFLAG_ACTIVE; - } - - pf_mv_pool(&V_pf_pabuf, &rule->rpool.list); - if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || - (rule->action == PF_BINAT)) && rule->anchor == NULL) || - (rule->rt > PF_FASTROUTE)) && - (TAILQ_FIRST(&rule->rpool.list) == NULL)) - error = EINVAL; - - if (error) { - pf_free_rule(rule); - PF_RULES_WUNLOCK(); - break; - } - - rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); - rule->evaluations = rule->packets[0] = rule->packets[1] = - rule->bytes[0] = rule->bytes[1] = 0; - TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, - rule, entries); - ruleset->rules[rs_num].inactive.rcount++; - PF_RULES_WUNLOCK(); - break; - -#undef ERROUT -DIOCADDRULE_error: - PF_RULES_WUNLOCK(); - free(rule, M_PFRULE); - if (kif) - free(kif, PFI_MTYPE); - break; - } - - case DIOCGETRULES: { - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *tail; - int rs_num; - - PF_RULES_WLOCK(); - pr->anchor[sizeof(pr->anchor) - 1] = 0; - ruleset = pf_find_ruleset(pr->anchor); - if (ruleset == NULL) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - tail = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, - pf_rulequeue); - if (tail) - pr->nr = tail->nr + 1; - else - pr->nr = 0; - pr->ticket = ruleset->rules[rs_num].active.ticket; - PF_RULES_WUNLOCK(); - break; - } - - case DIOCGETRULE: { - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *rule; - int rs_num, i; - - PF_RULES_WLOCK(); - pr->anchor[sizeof(pr->anchor) - 1] = 0; - ruleset = pf_find_ruleset(pr->anchor); - if (ruleset == NULL) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - if (pr->ticket != ruleset->rules[rs_num].active.ticket) { - PF_RULES_WUNLOCK(); - error = EBUSY; - break; - } - rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); - while ((rule != NULL) && (rule->nr != pr->nr)) - rule = TAILQ_NEXT(rule, entries); - if (rule == NULL) { - PF_RULES_WUNLOCK(); - error = EBUSY; - break; - } - bcopy(rule, &pr->rule, sizeof(struct pf_rule)); - if (pf_anchor_copyout(ruleset, rule, pr)) { - PF_RULES_WUNLOCK(); - error = EBUSY; - break; - } - pf_addr_copyout(&pr->rule.src.addr); - pf_addr_copyout(&pr->rule.dst.addr); - for (i = 0; i < PF_SKIP_COUNT; ++i) - if (rule->skip[i].ptr == NULL) - pr->rule.skip[i].nr = -1; - else - pr->rule.skip[i].nr = - rule->skip[i].ptr->nr; - - if (pr->action == PF_GET_CLR_CNTR) { - rule->evaluations = 0; - rule->packets[0] = rule->packets[1] = 0; - rule->bytes[0] = rule->bytes[1] = 0; - rule->states_tot = 0; - } - PF_RULES_WUNLOCK(); - break; - } - - case DIOCCHANGERULE: { - struct pfioc_rule *pcr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *oldrule = NULL, *newrule = NULL; - struct pfi_kif *kif = NULL; - struct pf_pooladdr *pa; - u_int32_t nr = 0; - int rs_num; - - if (pcr->action < PF_CHANGE_ADD_HEAD || - pcr->action > PF_CHANGE_GET_TICKET) { - error = EINVAL; - break; - } - if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { - error = EINVAL; - break; - } - - if (pcr->action != PF_CHANGE_REMOVE) { -#ifndef INET - if (pcr->rule.af == AF_INET) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET */ -#ifndef INET6 - if (pcr->rule.af == AF_INET6) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET6 */ - newrule = malloc(sizeof(*newrule), M_PFRULE, M_WAITOK); - bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); - newrule->cuid = td->td_ucred->cr_ruid; - newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0; - TAILQ_INIT(&newrule->rpool.list); - /* Initialize refcounting. */ - newrule->states_cur = 0; - newrule->entries.tqe_prev = NULL; - - if (newrule->ifname[0]) - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - } - -#define ERROUT(x) { error = (x); goto DIOCCHANGERULE_error; } - - PF_RULES_WLOCK(); - if (!(pcr->action == PF_CHANGE_REMOVE || - pcr->action == PF_CHANGE_GET_TICKET) && - pcr->pool_ticket != V_ticket_pabuf) - ERROUT(EBUSY); - - ruleset = pf_find_ruleset(pcr->anchor); - if (ruleset == NULL) - ERROUT(EINVAL); - - rs_num = pf_get_ruleset_number(pcr->rule.action); - if (rs_num >= PF_RULESET_MAX) - ERROUT(EINVAL); - - if (pcr->action == PF_CHANGE_GET_TICKET) { - pcr->ticket = ++ruleset->rules[rs_num].active.ticket; - ERROUT(0); - } else if (pcr->ticket != - ruleset->rules[rs_num].active.ticket) - ERROUT(EINVAL); - - if (pcr->action != PF_CHANGE_REMOVE) { - if (newrule->ifname[0]) { - newrule->kif = pfi_kif_attach(kif, - newrule->ifname); - pfi_kif_ref(newrule->kif); - } else - newrule->kif = NULL; - - if (newrule->rtableid > 0 && - newrule->rtableid >= rt_numfibs) - error = EBUSY; - -#ifdef ALTQ - /* set queue IDs */ - if (newrule->qname[0] != 0) { - if ((newrule->qid = - pf_qname2qid(newrule->qname)) == 0) - error = EBUSY; - else if (newrule->pqname[0] != 0) { - if ((newrule->pqid = - pf_qname2qid(newrule->pqname)) == 0) - error = EBUSY; - } else - newrule->pqid = newrule->qid; - } -#endif /* ALTQ */ - if (newrule->tagname[0]) - if ((newrule->tag = - pf_tagname2tag(newrule->tagname)) == 0) - error = EBUSY; - if (newrule->match_tagname[0]) - if ((newrule->match_tag = pf_tagname2tag( - newrule->match_tagname)) == 0) - error = EBUSY; - if (newrule->rt && !newrule->direction) - error = EINVAL; - if (!newrule->log) - newrule->logif = 0; - if (newrule->logif >= PFLOGIFS_MAX) - error = EINVAL; - if (pf_addr_setup(ruleset, &newrule->src.addr, newrule->af)) - error = ENOMEM; - if (pf_addr_setup(ruleset, &newrule->dst.addr, newrule->af)) - error = ENOMEM; - if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call)) - error = EINVAL; - TAILQ_FOREACH(pa, &V_pf_pabuf, entries) - if (pa->addr.type == PF_ADDR_TABLE) { - pa->addr.p.tbl = - pfr_attach_table(ruleset, - pa->addr.v.tblname); - if (pa->addr.p.tbl == NULL) - error = ENOMEM; - } - - if (newrule->overload_tblname[0]) { - if ((newrule->overload_tbl = pfr_attach_table( - ruleset, newrule->overload_tblname)) == - NULL) - error = EINVAL; - else - newrule->overload_tbl->pfrkt_flags |= - PFR_TFLAG_ACTIVE; - } - - pf_mv_pool(&V_pf_pabuf, &newrule->rpool.list); - if (((((newrule->action == PF_NAT) || - (newrule->action == PF_RDR) || - (newrule->action == PF_BINAT) || - (newrule->rt > PF_FASTROUTE)) && - !newrule->anchor)) && - (TAILQ_FIRST(&newrule->rpool.list) == NULL)) - error = EINVAL; - - if (error) { - pf_free_rule(newrule); - PF_RULES_WUNLOCK(); - break; - } - - newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list); - newrule->evaluations = 0; - newrule->packets[0] = newrule->packets[1] = 0; - newrule->bytes[0] = newrule->bytes[1] = 0; - } - pf_empty_pool(&V_pf_pabuf); - - if (pcr->action == PF_CHANGE_ADD_HEAD) - oldrule = TAILQ_FIRST( - ruleset->rules[rs_num].active.ptr); - else if (pcr->action == PF_CHANGE_ADD_TAIL) - oldrule = TAILQ_LAST( - ruleset->rules[rs_num].active.ptr, pf_rulequeue); - else { - oldrule = TAILQ_FIRST( - ruleset->rules[rs_num].active.ptr); - while ((oldrule != NULL) && (oldrule->nr != pcr->nr)) - oldrule = TAILQ_NEXT(oldrule, entries); - if (oldrule == NULL) { - if (newrule != NULL) - pf_free_rule(newrule); - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - } - - if (pcr->action == PF_CHANGE_REMOVE) { - pf_unlink_rule(ruleset->rules[rs_num].active.ptr, - oldrule); - ruleset->rules[rs_num].active.rcount--; - } else { - if (oldrule == NULL) - TAILQ_INSERT_TAIL( - ruleset->rules[rs_num].active.ptr, - newrule, entries); - else if (pcr->action == PF_CHANGE_ADD_HEAD || - pcr->action == PF_CHANGE_ADD_BEFORE) - TAILQ_INSERT_BEFORE(oldrule, newrule, entries); - else - TAILQ_INSERT_AFTER( - ruleset->rules[rs_num].active.ptr, - oldrule, newrule, entries); - ruleset->rules[rs_num].active.rcount++; - } - - nr = 0; - TAILQ_FOREACH(oldrule, - ruleset->rules[rs_num].active.ptr, entries) - oldrule->nr = nr++; - - ruleset->rules[rs_num].active.ticket++; - - pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); - pf_remove_if_empty_ruleset(ruleset); - - PF_RULES_WUNLOCK(); - break; - -#undef ERROUT -DIOCCHANGERULE_error: - PF_RULES_WUNLOCK(); - if (newrule != NULL) - free(newrule, M_PFRULE); - if (kif != NULL) - free(kif, PFI_MTYPE); - break; - } - - case DIOCCLRSTATES: { - struct pf_state *s; - struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; - u_int i, killed = 0; - - for (i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - -relock_DIOCCLRSTATES: - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) - if (!psk->psk_ifname[0] || - !strcmp(psk->psk_ifname, - s->kif->pfik_name)) { - /* - * Don't send out individual - * delete messages. - */ - s->state_flags |= PFSTATE_NOSYNC; - pf_unlink_state(s, PF_ENTER_LOCKED); - killed++; - goto relock_DIOCCLRSTATES; - } - PF_HASHROW_UNLOCK(ih); - } - psk->psk_killed = killed; - if (pfsync_clear_states_ptr != NULL) - pfsync_clear_states_ptr(V_pf_status.hostid, psk->psk_ifname); - break; - } - - case DIOCKILLSTATES: { - struct pf_state *s; - struct pf_state_key *sk; - struct pf_addr *srcaddr, *dstaddr; - u_int16_t srcport, dstport; - struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; - u_int i, killed = 0; - - if (psk->psk_pfcmp.id) { - if (psk->psk_pfcmp.creatorid == 0) - psk->psk_pfcmp.creatorid = V_pf_status.hostid; - if ((s = pf_find_state_byid(psk->psk_pfcmp.id, - psk->psk_pfcmp.creatorid))) { - pf_unlink_state(s, PF_ENTER_LOCKED); - psk->psk_killed = 1; - } - break; - } - - for (i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - -relock_DIOCKILLSTATES: - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - sk = s->key[PF_SK_WIRE]; - if (s->direction == PF_OUT) { - srcaddr = &sk->addr[1]; - dstaddr = &sk->addr[0]; - srcport = sk->port[0]; - dstport = sk->port[0]; - } else { - srcaddr = &sk->addr[0]; - dstaddr = &sk->addr[1]; - srcport = sk->port[0]; - dstport = sk->port[0]; - } - - if ((!psk->psk_af || sk->af == psk->psk_af) - && (!psk->psk_proto || psk->psk_proto == - sk->proto) && - PF_MATCHA(psk->psk_src.neg, - &psk->psk_src.addr.v.a.addr, - &psk->psk_src.addr.v.a.mask, - srcaddr, sk->af) && - PF_MATCHA(psk->psk_dst.neg, - &psk->psk_dst.addr.v.a.addr, - &psk->psk_dst.addr.v.a.mask, - dstaddr, sk->af) && - (psk->psk_src.port_op == 0 || - pf_match_port(psk->psk_src.port_op, - psk->psk_src.port[0], psk->psk_src.port[1], - srcport)) && - (psk->psk_dst.port_op == 0 || - pf_match_port(psk->psk_dst.port_op, - psk->psk_dst.port[0], psk->psk_dst.port[1], - dstport)) && - (!psk->psk_label[0] || - (s->rule.ptr->label[0] && - !strcmp(psk->psk_label, - s->rule.ptr->label))) && - (!psk->psk_ifname[0] || - !strcmp(psk->psk_ifname, - s->kif->pfik_name))) { - pf_unlink_state(s, PF_ENTER_LOCKED); - killed++; - goto relock_DIOCKILLSTATES; - } - } - PF_HASHROW_UNLOCK(ih); - } - psk->psk_killed = killed; - break; - } - - case DIOCADDSTATE: { - struct pfioc_state *ps = (struct pfioc_state *)addr; - struct pfsync_state *sp = &ps->state; - - if (sp->timeout >= PFTM_MAX && - sp->timeout != PFTM_UNTIL_PACKET) { - error = EINVAL; - break; - } - if (pfsync_state_import_ptr != NULL) { - PF_RULES_RLOCK(); - error = pfsync_state_import_ptr(sp, PFSYNC_SI_IOCTL); - PF_RULES_RUNLOCK(); - } - error = EOPNOTSUPP; - break; - } - - case DIOCGETSTATE: { - struct pfioc_state *ps = (struct pfioc_state *)addr; - struct pf_state *s; - - s = pf_find_state_byid(ps->state.id, ps->state.creatorid); - if (s == NULL) { - error = ENOENT; - break; - } - - pfsync_state_export(&ps->state, s); - PF_STATE_UNLOCK(s); - break; - } - - case DIOCGETSTATES: { - struct pfioc_states *ps = (struct pfioc_states *)addr; - struct pf_state *s; - struct pfsync_state *pstore, *p; - int i, nr; - - if (ps->ps_len == 0) { - nr = uma_zone_get_cur(V_pf_state_z); - ps->ps_len = sizeof(struct pfsync_state) * nr; - break; - } - - p = pstore = malloc(ps->ps_len, M_TEMP, M_WAITOK); - nr = 0; - - for (i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - - if (s->timeout == PFTM_UNLINKED) - continue; - - if ((nr+1) * sizeof(*p) > ps->ps_len) { - PF_HASHROW_UNLOCK(ih); - goto DIOCGETSTATES_full; - } - pfsync_state_export(p, s); - p++; - nr++; - } - PF_HASHROW_UNLOCK(ih); - } -DIOCGETSTATES_full: - error = copyout(pstore, ps->ps_states, - sizeof(struct pfsync_state) * nr); - if (error) { - free(pstore, M_TEMP); - break; - } - ps->ps_len = sizeof(struct pfsync_state) * nr; - free(pstore, M_TEMP); - - break; - } - - case DIOCGETSTATUS: { - struct pf_status *s = (struct pf_status *)addr; - PF_RULES_RLOCK(); - bcopy(&V_pf_status, s, sizeof(struct pf_status)); - pfi_update_status(s->ifname, s); - PF_RULES_RUNLOCK(); - break; - } - - case DIOCSETSTATUSIF: { - struct pfioc_if *pi = (struct pfioc_if *)addr; - - if (pi->ifname[0] == 0) { - bzero(V_pf_status.ifname, IFNAMSIZ); - break; - } - PF_RULES_WLOCK(); - strlcpy(V_pf_status.ifname, pi->ifname, IFNAMSIZ); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCCLRSTATUS: { - PF_RULES_WLOCK(); - bzero(V_pf_status.counters, sizeof(V_pf_status.counters)); - bzero(V_pf_status.fcounters, sizeof(V_pf_status.fcounters)); - bzero(V_pf_status.scounters, sizeof(V_pf_status.scounters)); - V_pf_status.since = time_second; - if (*V_pf_status.ifname) - pfi_update_status(V_pf_status.ifname, NULL); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCNATLOOK: { - struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; - struct pf_state_key *sk; - struct pf_state *state; - struct pf_state_key_cmp key; - int m = 0, direction = pnl->direction; - int sidx, didx; - - /* NATLOOK src and dst are reversed, so reverse sidx/didx */ - sidx = (direction == PF_IN) ? 1 : 0; - didx = (direction == PF_IN) ? 0 : 1; - - if (!pnl->proto || - PF_AZERO(&pnl->saddr, pnl->af) || - PF_AZERO(&pnl->daddr, pnl->af) || - ((pnl->proto == IPPROTO_TCP || - pnl->proto == IPPROTO_UDP) && - (!pnl->dport || !pnl->sport))) - error = EINVAL; - else { - key.af = pnl->af; - key.proto = pnl->proto; - PF_ACPY(&key.addr[sidx], &pnl->saddr, pnl->af); - key.port[sidx] = pnl->sport; - PF_ACPY(&key.addr[didx], &pnl->daddr, pnl->af); - key.port[didx] = pnl->dport; - - state = pf_find_state_all(&key, direction, &m); - - if (m > 1) - error = E2BIG; /* more than one state */ - else if (state != NULL) { - /* XXXGL: not locked read */ - sk = state->key[sidx]; - PF_ACPY(&pnl->rsaddr, &sk->addr[sidx], sk->af); - pnl->rsport = sk->port[sidx]; - PF_ACPY(&pnl->rdaddr, &sk->addr[didx], sk->af); - pnl->rdport = sk->port[didx]; - } else - error = ENOENT; - } - break; - } - - case DIOCSETTIMEOUT: { - struct pfioc_tm *pt = (struct pfioc_tm *)addr; - int old; - - if (pt->timeout < 0 || pt->timeout >= PFTM_MAX || - pt->seconds < 0) { - error = EINVAL; - break; - } - PF_RULES_WLOCK(); - old = V_pf_default_rule.timeout[pt->timeout]; - if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0) - pt->seconds = 1; - V_pf_default_rule.timeout[pt->timeout] = pt->seconds; - if (pt->timeout == PFTM_INTERVAL && pt->seconds < old) - wakeup(pf_purge_thread); - pt->seconds = old; - PF_RULES_WUNLOCK(); - break; - } - - case DIOCGETTIMEOUT: { - struct pfioc_tm *pt = (struct pfioc_tm *)addr; - - if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) { - error = EINVAL; - break; - } - PF_RULES_RLOCK(); - pt->seconds = V_pf_default_rule.timeout[pt->timeout]; - PF_RULES_RUNLOCK(); - break; - } - - case DIOCGETLIMIT: { - struct pfioc_limit *pl = (struct pfioc_limit *)addr; - - if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { - error = EINVAL; - break; - } - PF_RULES_RLOCK(); - pl->limit = V_pf_limits[pl->index].limit; - PF_RULES_RUNLOCK(); - break; - } - - case DIOCSETLIMIT: { - struct pfioc_limit *pl = (struct pfioc_limit *)addr; - int old_limit; - - PF_RULES_WLOCK(); - if (pl->index < 0 || pl->index >= PF_LIMIT_MAX || - V_pf_limits[pl->index].zone == NULL) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - uma_zone_set_max(V_pf_limits[pl->index].zone, pl->limit); - old_limit = V_pf_limits[pl->index].limit; - V_pf_limits[pl->index].limit = pl->limit; - pl->limit = old_limit; - PF_RULES_WUNLOCK(); - break; - } - - case DIOCSETDEBUG: { - u_int32_t *level = (u_int32_t *)addr; - - PF_RULES_WLOCK(); - V_pf_status.debug = *level; - PF_RULES_WUNLOCK(); - break; - } - - case DIOCCLRRULECTRS: { - /* obsoleted by DIOCGETRULE with action=PF_GET_CLR_CNTR */ - struct pf_ruleset *ruleset = &pf_main_ruleset; - struct pf_rule *rule; - - PF_RULES_WLOCK(); - TAILQ_FOREACH(rule, - ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) { - rule->evaluations = 0; - rule->packets[0] = rule->packets[1] = 0; - rule->bytes[0] = rule->bytes[1] = 0; - } - PF_RULES_WUNLOCK(); - break; - } - - case DIOCGIFSPEED: { - struct pf_ifspeed *psp = (struct pf_ifspeed *)addr; - struct pf_ifspeed ps; - struct ifnet *ifp; - - if (psp->ifname[0] != 0) { - /* Can we completely trust user-land? */ - strlcpy(ps.ifname, psp->ifname, IFNAMSIZ); - ifp = ifunit(ps.ifname); - if (ifp != NULL) - psp->baudrate = ifp->if_baudrate; - else - error = EINVAL; - } else - error = EINVAL; - break; - } - -#ifdef ALTQ - case DIOCSTARTALTQ: { - struct pf_altq *altq; - - PF_RULES_WLOCK(); - /* enable all altq interfaces on active list */ - TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { - if (altq->qname[0] == 0 && (altq->local_flags & - PFALTQ_FLAG_IF_REMOVED) == 0) { - error = pf_enable_altq(altq); - if (error != 0) - break; - } - } - if (error == 0) - V_pf_altq_running = 1; - PF_RULES_WUNLOCK(); - DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n")); - break; - } - - case DIOCSTOPALTQ: { - struct pf_altq *altq; - - PF_RULES_WLOCK(); - /* disable all altq interfaces on active list */ - TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { - if (altq->qname[0] == 0 && (altq->local_flags & - PFALTQ_FLAG_IF_REMOVED) == 0) { - error = pf_disable_altq(altq); - if (error != 0) - break; - } - } - if (error == 0) - V_pf_altq_running = 0; - PF_RULES_WUNLOCK(); - DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); - break; - } - - case DIOCADDALTQ: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; - struct pf_altq *altq, *a; - struct ifnet *ifp; - - altq = malloc(sizeof(*altq), M_PFALTQ, M_WAITOK); - bcopy(&pa->altq, altq, sizeof(struct pf_altq)); - altq->local_flags = 0; - - PF_RULES_WLOCK(); - if (pa->ticket != V_ticket_altqs_inactive) { - PF_RULES_WUNLOCK(); - free(altq, M_PFALTQ); - error = EBUSY; - break; - } - - /* - * if this is for a queue, find the discipline and - * copy the necessary fields - */ - if (altq->qname[0] != 0) { - if ((altq->qid = pf_qname2qid(altq->qname)) == 0) { - PF_RULES_WUNLOCK(); - error = EBUSY; - free(altq, M_PFALTQ); - break; - } - altq->altq_disc = NULL; - TAILQ_FOREACH(a, V_pf_altqs_inactive, entries) { - if (strncmp(a->ifname, altq->ifname, - IFNAMSIZ) == 0 && a->qname[0] == 0) { - altq->altq_disc = a->altq_disc; - break; - } - } - } - - if ((ifp = ifunit(altq->ifname)) == NULL) - altq->local_flags |= PFALTQ_FLAG_IF_REMOVED; - else - error = altq_add(altq); - - if (error) { - PF_RULES_WUNLOCK(); - free(altq, M_PFALTQ); - break; - } - - TAILQ_INSERT_TAIL(V_pf_altqs_inactive, altq, entries); - bcopy(altq, &pa->altq, sizeof(struct pf_altq)); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCGETALTQS: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; - struct pf_altq *altq; - - PF_RULES_RLOCK(); - pa->nr = 0; - TAILQ_FOREACH(altq, V_pf_altqs_active, entries) - pa->nr++; - pa->ticket = V_ticket_altqs_active; - PF_RULES_RUNLOCK(); - break; - } - - case DIOCGETALTQ: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; - struct pf_altq *altq; - u_int32_t nr; - - PF_RULES_RLOCK(); - if (pa->ticket != V_ticket_altqs_active) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - nr = 0; - altq = TAILQ_FIRST(V_pf_altqs_active); - while ((altq != NULL) && (nr < pa->nr)) { - altq = TAILQ_NEXT(altq, entries); - nr++; - } - if (altq == NULL) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - bcopy(altq, &pa->altq, sizeof(struct pf_altq)); - PF_RULES_RUNLOCK(); - break; - } - - case DIOCCHANGEALTQ: - /* CHANGEALTQ not supported yet! */ - error = ENODEV; - break; - - case DIOCGETQSTATS: { - struct pfioc_qstats *pq = (struct pfioc_qstats *)addr; - struct pf_altq *altq; - u_int32_t nr; - int nbytes; - - PF_RULES_RLOCK(); - if (pq->ticket != V_ticket_altqs_active) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - nbytes = pq->nbytes; - nr = 0; - altq = TAILQ_FIRST(V_pf_altqs_active); - while ((altq != NULL) && (nr < pq->nr)) { - altq = TAILQ_NEXT(altq, entries); - nr++; - } - if (altq == NULL) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - - if ((altq->local_flags & PFALTQ_FLAG_IF_REMOVED) != 0) { - PF_RULES_RUNLOCK(); - error = ENXIO; - break; - } - PF_RULES_RUNLOCK(); - error = altq_getqstats(altq, pq->buf, &nbytes); - if (error == 0) { - pq->scheduler = altq->scheduler; - pq->nbytes = nbytes; - } - break; - } -#endif /* ALTQ */ - - case DIOCBEGINADDRS: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; - - PF_RULES_WLOCK(); - pf_empty_pool(&V_pf_pabuf); - pp->ticket = ++V_ticket_pabuf; - PF_RULES_WUNLOCK(); - break; - } - - case DIOCADDADDR: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; - struct pf_pooladdr *pa; - struct pfi_kif *kif = NULL; - -#ifndef INET - if (pp->af == AF_INET) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET */ -#ifndef INET6 - if (pp->af == AF_INET6) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET6 */ - if (pp->addr.addr.type != PF_ADDR_ADDRMASK && - pp->addr.addr.type != PF_ADDR_DYNIFTL && - pp->addr.addr.type != PF_ADDR_TABLE) { - error = EINVAL; - break; - } - pa = malloc(sizeof(*pa), M_PFRULE, M_WAITOK); - bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); - if (pa->ifname[0]) - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - PF_RULES_WLOCK(); - if (pp->ticket != V_ticket_pabuf) { - PF_RULES_WUNLOCK(); - if (pa->ifname[0]) - free(kif, PFI_MTYPE); - free(pa, M_PFRULE); - error = EBUSY; - break; - } - if (pa->ifname[0]) { - pa->kif = pfi_kif_attach(kif, pa->ifname); - pfi_kif_ref(pa->kif); - } else - pa->kif = NULL; - if (pa->addr.type == PF_ADDR_DYNIFTL && ((error = - pfi_dynaddr_setup(&pa->addr, pp->af)) != 0)) { - if (pa->ifname[0]) - pfi_kif_unref(pa->kif); - PF_RULES_WUNLOCK(); - free(pa, M_PFRULE); - break; - } - TAILQ_INSERT_TAIL(&V_pf_pabuf, pa, entries); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCGETADDRS: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; - struct pf_pool *pool; - struct pf_pooladdr *pa; - - PF_RULES_RLOCK(); - pp->nr = 0; - pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, - pp->r_num, 0, 1, 0); - if (pool == NULL) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - TAILQ_FOREACH(pa, &pool->list, entries) - pp->nr++; - PF_RULES_RUNLOCK(); - break; - } - - case DIOCGETADDR: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; - struct pf_pool *pool; - struct pf_pooladdr *pa; - u_int32_t nr = 0; - - PF_RULES_RLOCK(); - pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, - pp->r_num, 0, 1, 1); - if (pool == NULL) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - pa = TAILQ_FIRST(&pool->list); - while ((pa != NULL) && (nr < pp->nr)) { - pa = TAILQ_NEXT(pa, entries); - nr++; - } - if (pa == NULL) { - PF_RULES_RUNLOCK(); - error = EBUSY; - break; - } - bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); - pf_addr_copyout(&pp->addr.addr); - PF_RULES_RUNLOCK(); - break; - } - - case DIOCCHANGEADDR: { - struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; - struct pf_pool *pool; - struct pf_pooladdr *oldpa = NULL, *newpa = NULL; - struct pf_ruleset *ruleset; - struct pfi_kif *kif = NULL; - - if (pca->action < PF_CHANGE_ADD_HEAD || - pca->action > PF_CHANGE_REMOVE) { - error = EINVAL; - break; - } - if (pca->addr.addr.type != PF_ADDR_ADDRMASK && - pca->addr.addr.type != PF_ADDR_DYNIFTL && - pca->addr.addr.type != PF_ADDR_TABLE) { - error = EINVAL; - break; - } - - if (pca->action != PF_CHANGE_REMOVE) { -#ifndef INET - if (pca->af == AF_INET) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET */ -#ifndef INET6 - if (pca->af == AF_INET6) { - error = EAFNOSUPPORT; - break; - } -#endif /* INET6 */ - newpa = malloc(sizeof(*newpa), M_PFRULE, M_WAITOK); - bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); - if (newpa->ifname[0]) - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); - } - -#define ERROUT(x) { error = (x); goto DIOCCHANGEADDR_error; } - PF_RULES_WLOCK(); - ruleset = pf_find_ruleset(pca->anchor); - if (ruleset == NULL) - ERROUT(EBUSY); - - pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action, - pca->r_num, pca->r_last, 1, 1); - if (pool == NULL) - ERROUT(EBUSY); - - if (pca->action != PF_CHANGE_REMOVE) { - if (newpa->ifname[0]) { - newpa->kif = pfi_kif_attach(kif, newpa->ifname); - pfi_kif_ref(newpa->kif); - } else - newpa->kif = NULL; - - switch (newpa->addr.type) { - case PF_ADDR_DYNIFTL: - error = pfi_dynaddr_setup(&newpa->addr, - pca->af); - break; - case PF_ADDR_TABLE: - newpa->addr.p.tbl = pfr_attach_table(ruleset, - newpa->addr.v.tblname); - if (newpa->addr.p.tbl == NULL) - error = ENOMEM; - break; - } - if (error) { - if (newpa->kif) - pfi_kif_unref(newpa->kif); - PF_RULES_WUNLOCK(); - free(newpa, M_PFRULE); - break; - } - } - - if (pca->action == PF_CHANGE_ADD_HEAD) - oldpa = TAILQ_FIRST(&pool->list); - else if (pca->action == PF_CHANGE_ADD_TAIL) - oldpa = TAILQ_LAST(&pool->list, pf_palist); - else { - int i = 0; - - oldpa = TAILQ_FIRST(&pool->list); - while ((oldpa != NULL) && (i < pca->nr)) { - oldpa = TAILQ_NEXT(oldpa, entries); - i++; - } - if (oldpa == NULL) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } - } - - if (pca->action == PF_CHANGE_REMOVE) { - TAILQ_REMOVE(&pool->list, oldpa, entries); - switch (oldpa->addr.type) { - case PF_ADDR_DYNIFTL: - pfi_dynaddr_remove(oldpa->addr.p.dyn); - break; - case PF_ADDR_TABLE: - pfr_detach_table(oldpa->addr.p.tbl); - break; - } - if (oldpa->kif) - pfi_kif_unref(oldpa->kif); - free(oldpa, M_PFRULE); - } else { - if (oldpa == NULL) - TAILQ_INSERT_TAIL(&pool->list, newpa, entries); - else if (pca->action == PF_CHANGE_ADD_HEAD || - pca->action == PF_CHANGE_ADD_BEFORE) - TAILQ_INSERT_BEFORE(oldpa, newpa, entries); - else - TAILQ_INSERT_AFTER(&pool->list, oldpa, - newpa, entries); - } - - pool->cur = TAILQ_FIRST(&pool->list); - PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, - pca->af); - PF_RULES_WUNLOCK(); - break; - -#undef ERROUT -DIOCCHANGEADDR_error: - PF_RULES_WUNLOCK(); - if (newpa != NULL) - free(newpa, M_PFRULE); - if (kif != NULL) - free(kif, PFI_MTYPE); - break; - } - - case DIOCGETRULESETS: { - struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; - struct pf_ruleset *ruleset; - struct pf_anchor *anchor; - - PF_RULES_RLOCK(); - pr->path[sizeof(pr->path) - 1] = 0; - if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { - PF_RULES_RUNLOCK(); - error = ENOENT; - break; - } - pr->nr = 0; - if (ruleset->anchor == NULL) { - /* XXX kludge for pf_main_ruleset */ - RB_FOREACH(anchor, pf_anchor_global, &V_pf_anchors) - if (anchor->parent == NULL) - pr->nr++; - } else { - RB_FOREACH(anchor, pf_anchor_node, - &ruleset->anchor->children) - pr->nr++; - } - PF_RULES_RUNLOCK(); - break; - } - - case DIOCGETRULESET: { - struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; - struct pf_ruleset *ruleset; - struct pf_anchor *anchor; - u_int32_t nr = 0; - - PF_RULES_RLOCK(); - pr->path[sizeof(pr->path) - 1] = 0; - if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { - PF_RULES_RUNLOCK(); - error = ENOENT; - break; - } - pr->name[0] = 0; - if (ruleset->anchor == NULL) { - /* XXX kludge for pf_main_ruleset */ - RB_FOREACH(anchor, pf_anchor_global, &V_pf_anchors) - if (anchor->parent == NULL && nr++ == pr->nr) { - strlcpy(pr->name, anchor->name, - sizeof(pr->name)); - break; - } - } else { - RB_FOREACH(anchor, pf_anchor_node, - &ruleset->anchor->children) - if (nr++ == pr->nr) { - strlcpy(pr->name, anchor->name, - sizeof(pr->name)); - break; - } - } - if (!pr->name[0]) - error = EBUSY; - PF_RULES_RUNLOCK(); - break; - } - - case DIOCRCLRTABLES: { - struct pfioc_table *io = (struct pfioc_table *)addr; - - if (io->pfrio_esize != 0) { - error = ENODEV; - break; - } - PF_RULES_WLOCK(); - error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, - io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCRADDTABLES: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_table *pfrts; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_table)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfrts, totlen); - if (error) { - free(pfrts, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_add_tables(pfrts, io->pfrio_size, - &io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - free(pfrts, M_TEMP); - break; - } - - case DIOCRDELTABLES: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_table *pfrts; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_table)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfrts, totlen); - if (error) { - free(pfrts, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_del_tables(pfrts, io->pfrio_size, - &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - free(pfrts, M_TEMP); - break; - } - - case DIOCRGETTABLES: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_table *pfrts; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_table)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = malloc(totlen, M_TEMP, M_WAITOK); - PF_RULES_RLOCK(); - error = pfr_get_tables(&io->pfrio_table, pfrts, - &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_RUNLOCK(); - if (error == 0) - error = copyout(pfrts, io->pfrio_buffer, totlen); - free(pfrts, M_TEMP); - break; - } - - case DIOCRGETTSTATS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_tstats *pfrtstats; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_tstats)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_tstats); - pfrtstats = malloc(totlen, M_TEMP, M_WAITOK); - PF_RULES_WLOCK(); - error = pfr_get_tstats(&io->pfrio_table, pfrtstats, - &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - if (error == 0) - error = copyout(pfrtstats, io->pfrio_buffer, totlen); - free(pfrtstats, M_TEMP); - break; - } - - case DIOCRCLRTSTATS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_table *pfrts; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_table)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfrts, totlen); - if (error) { - free(pfrts, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_clr_tstats(pfrts, io->pfrio_size, - &io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - free(pfrts, M_TEMP); - break; - } - - case DIOCRSETTFLAGS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_table *pfrts; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_table)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfrts, totlen); - if (error) { - free(pfrts, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_set_tflags(pfrts, io->pfrio_size, - io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, - &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - free(pfrts, M_TEMP); - break; - } - - case DIOCRCLRADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - - if (io->pfrio_esize != 0) { - error = ENODEV; - break; - } - PF_RULES_WLOCK(); - error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel, - io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCRADDADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfras, totlen); - if (error) { - free(pfras, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_add_addrs(&io->pfrio_table, pfras, - io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags | - PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) - error = copyout(pfras, io->pfrio_buffer, totlen); - free(pfras, M_TEMP); - break; - } - - case DIOCRDELADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfras, totlen); - if (error) { - free(pfras, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_del_addrs(&io->pfrio_table, pfras, - io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags | - PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) - error = copyout(pfras, io->pfrio_buffer, totlen); - free(pfras, M_TEMP); - break; - } - - case DIOCRSETADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = (io->pfrio_size + io->pfrio_size2) * - sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfras, totlen); - if (error) { - free(pfras, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_set_addrs(&io->pfrio_table, pfras, - io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, - &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags | - PFR_FLAG_USERIOCTL, 0); - PF_RULES_WUNLOCK(); - if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) - error = copyout(pfras, io->pfrio_buffer, totlen); - free(pfras, M_TEMP); - break; - } - - case DIOCRGETADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - PF_RULES_RLOCK(); - error = pfr_get_addrs(&io->pfrio_table, pfras, - &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_RUNLOCK(); - if (error == 0) - error = copyout(pfras, io->pfrio_buffer, totlen); - free(pfras, M_TEMP); - break; - } - - case DIOCRGETASTATS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_astats *pfrastats; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_astats)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_astats); - pfrastats = malloc(totlen, M_TEMP, M_WAITOK); - PF_RULES_RLOCK(); - error = pfr_get_astats(&io->pfrio_table, pfrastats, - &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_RUNLOCK(); - if (error == 0) - error = copyout(pfrastats, io->pfrio_buffer, totlen); - free(pfrastats, M_TEMP); - break; - } - - case DIOCRCLRASTATS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfras, totlen); - if (error) { - free(pfras, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_clr_astats(&io->pfrio_table, pfras, - io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags | - PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) - error = copyout(pfras, io->pfrio_buffer, totlen); - free(pfras, M_TEMP); - break; - } - - case DIOCRTSTADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfras, totlen); - if (error) { - free(pfras, M_TEMP); - break; - } - PF_RULES_RLOCK(); - error = pfr_tst_addrs(&io->pfrio_table, pfras, - io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags | - PFR_FLAG_USERIOCTL); - PF_RULES_RUNLOCK(); - if (error == 0) - error = copyout(pfras, io->pfrio_buffer, totlen); - free(pfras, M_TEMP); - break; - } - - case DIOCRINADEFINE: { - struct pfioc_table *io = (struct pfioc_table *)addr; - struct pfr_addr *pfras; - size_t totlen; - - if (io->pfrio_esize != sizeof(struct pfr_addr)) { - error = ENODEV; - break; - } - totlen = io->pfrio_size * sizeof(struct pfr_addr); - pfras = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->pfrio_buffer, pfras, totlen); - if (error) { - free(pfras, M_TEMP); - break; - } - PF_RULES_WLOCK(); - error = pfr_ina_define(&io->pfrio_table, pfras, - io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr, - io->pfrio_ticket, io->pfrio_flags | PFR_FLAG_USERIOCTL); - PF_RULES_WUNLOCK(); - free(pfras, M_TEMP); - break; - } - - case DIOCOSFPADD: { - struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; - PF_RULES_WLOCK(); - error = pf_osfp_add(io); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCOSFPGET: { - struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; - PF_RULES_RLOCK(); - error = pf_osfp_get(io); - PF_RULES_RUNLOCK(); - break; - } - - case DIOCXBEGIN: { - struct pfioc_trans *io = (struct pfioc_trans *)addr; - struct pfioc_trans_e *ioes, *ioe; - size_t totlen; - int i; - - if (io->esize != sizeof(*ioe)) { - error = ENODEV; - break; - } - totlen = sizeof(struct pfioc_trans_e) * io->size; - ioes = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->array, ioes, totlen); - if (error) { - free(ioes, M_TEMP); - break; - } - PF_RULES_WLOCK(); - for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { - switch (ioe->rs_num) { -#ifdef ALTQ - case PF_RULESET_ALTQ: - if (ioe->anchor[0]) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EINVAL; - goto fail; - } - if ((error = pf_begin_altq(&ioe->ticket))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; - } - break; -#endif /* ALTQ */ - case PF_RULESET_TABLE: - { - struct pfr_table table; - - bzero(&table, sizeof(table)); - strlcpy(table.pfrt_anchor, ioe->anchor, - sizeof(table.pfrt_anchor)); - if ((error = pfr_ina_begin(&table, - &ioe->ticket, NULL, 0))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; - } - break; - } - default: - if ((error = pf_begin_rules(&ioe->ticket, - ioe->rs_num, ioe->anchor))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; - } - break; - } - } - PF_RULES_WUNLOCK(); - error = copyout(ioes, io->array, totlen); - free(ioes, M_TEMP); - break; - } - - case DIOCXROLLBACK: { - struct pfioc_trans *io = (struct pfioc_trans *)addr; - struct pfioc_trans_e *ioe, *ioes; - size_t totlen; - int i; - - if (io->esize != sizeof(*ioe)) { - error = ENODEV; - break; - } - totlen = sizeof(struct pfioc_trans_e) * io->size; - ioes = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->array, ioes, totlen); - if (error) { - free(ioes, M_TEMP); - break; - } - PF_RULES_WLOCK(); - for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { - switch (ioe->rs_num) { -#ifdef ALTQ - case PF_RULESET_ALTQ: - if (ioe->anchor[0]) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EINVAL; - goto fail; - } - if ((error = pf_rollback_altq(ioe->ticket))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; /* really bad */ - } - break; -#endif /* ALTQ */ - case PF_RULESET_TABLE: - { - struct pfr_table table; - - bzero(&table, sizeof(table)); - strlcpy(table.pfrt_anchor, ioe->anchor, - sizeof(table.pfrt_anchor)); - if ((error = pfr_ina_rollback(&table, - ioe->ticket, NULL, 0))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; /* really bad */ - } - break; - } - default: - if ((error = pf_rollback_rules(ioe->ticket, - ioe->rs_num, ioe->anchor))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; /* really bad */ - } - break; - } - } - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - break; - } - - case DIOCXCOMMIT: { - struct pfioc_trans *io = (struct pfioc_trans *)addr; - struct pfioc_trans_e *ioe, *ioes; - struct pf_ruleset *rs; - size_t totlen; - int i; - - if (io->esize != sizeof(*ioe)) { - error = ENODEV; - break; - } - totlen = sizeof(struct pfioc_trans_e) * io->size; - ioes = malloc(totlen, M_TEMP, M_WAITOK); - error = copyin(io->array, ioes, totlen); - if (error) { - free(ioes, M_TEMP); - break; - } - PF_RULES_WLOCK(); - /* First makes sure everything will succeed. */ - for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { - switch (ioe->rs_num) { -#ifdef ALTQ - case PF_RULESET_ALTQ: - if (ioe->anchor[0]) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EINVAL; - goto fail; - } - if (!V_altqs_inactive_open || ioe->ticket != - V_ticket_altqs_inactive) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EBUSY; - goto fail; - } - break; -#endif /* ALTQ */ - case PF_RULESET_TABLE: - rs = pf_find_ruleset(ioe->anchor); - if (rs == NULL || !rs->topen || ioe->ticket != - rs->tticket) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EBUSY; - goto fail; - } - break; - default: - if (ioe->rs_num < 0 || ioe->rs_num >= - PF_RULESET_MAX) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EINVAL; - goto fail; - } - rs = pf_find_ruleset(ioe->anchor); - if (rs == NULL || - !rs->rules[ioe->rs_num].inactive.open || - rs->rules[ioe->rs_num].inactive.ticket != - ioe->ticket) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - error = EBUSY; - goto fail; - } - break; - } - } - /* Now do the commit - no errors should happen here. */ - for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { - switch (ioe->rs_num) { -#ifdef ALTQ - case PF_RULESET_ALTQ: - if ((error = pf_commit_altq(ioe->ticket))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; /* really bad */ - } - break; -#endif /* ALTQ */ - case PF_RULESET_TABLE: - { - struct pfr_table table; - - bzero(&table, sizeof(table)); - strlcpy(table.pfrt_anchor, ioe->anchor, - sizeof(table.pfrt_anchor)); - if ((error = pfr_ina_commit(&table, - ioe->ticket, NULL, NULL, 0))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; /* really bad */ - } - break; - } - default: - if ((error = pf_commit_rules(ioe->ticket, - ioe->rs_num, ioe->anchor))) { - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - goto fail; /* really bad */ - } - break; - } - } - PF_RULES_WUNLOCK(); - free(ioes, M_TEMP); - break; - } - - case DIOCGETSRCNODES: { - struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr; - struct pf_srchash *sh; - struct pf_src_node *n, *p, *pstore; - uint32_t i, nr = 0; - - if (psn->psn_len == 0) { - for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; - i++, sh++) { - PF_HASHROW_LOCK(sh); - LIST_FOREACH(n, &sh->nodes, entry) - nr++; - PF_HASHROW_UNLOCK(sh); - } - psn->psn_len = sizeof(struct pf_src_node) * nr; - break; - } - - p = pstore = malloc(psn->psn_len, M_TEMP, M_WAITOK); - for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; - i++, sh++) { - PF_HASHROW_LOCK(sh); - LIST_FOREACH(n, &sh->nodes, entry) { - int secs = time_uptime, diff; - - if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len) - break; - - bcopy(n, p, sizeof(struct pf_src_node)); - if (n->rule.ptr != NULL) - p->rule.nr = n->rule.ptr->nr; - p->creation = secs - p->creation; - if (p->expire > secs) - p->expire -= secs; - else - p->expire = 0; - - /* Adjust the connection rate estimate. */ - diff = secs - n->conn_rate.last; - if (diff >= n->conn_rate.seconds) - p->conn_rate.count = 0; - else - p->conn_rate.count -= - n->conn_rate.count * diff / - n->conn_rate.seconds; - p++; - nr++; - } - PF_HASHROW_UNLOCK(sh); - } - error = copyout(pstore, psn->psn_src_nodes, - sizeof(struct pf_src_node) * nr); - if (error) { - free(pstore, M_TEMP); - break; - } - psn->psn_len = sizeof(struct pf_src_node) * nr; - free(pstore, M_TEMP); - break; - } - - case DIOCCLRSRCNODES: { - - pf_clear_srcnodes(NULL); - pf_purge_expired_src_nodes(); - V_pf_status.src_nodes = 0; - break; - } - - case DIOCKILLSRCNODES: { - struct pfioc_src_node_kill *psnk = - (struct pfioc_src_node_kill *)addr; - struct pf_srchash *sh; - struct pf_src_node *sn; - u_int i, killed = 0; - - for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; - i++, sh++) { - /* - * XXXGL: we don't ever acquire sources hash lock - * but if we ever do, the below call to pf_clear_srcnodes() - * would lead to a LOR. - */ - PF_HASHROW_LOCK(sh); - LIST_FOREACH(sn, &sh->nodes, entry) - if (PF_MATCHA(psnk->psnk_src.neg, - &psnk->psnk_src.addr.v.a.addr, - &psnk->psnk_src.addr.v.a.mask, - &sn->addr, sn->af) && - PF_MATCHA(psnk->psnk_dst.neg, - &psnk->psnk_dst.addr.v.a.addr, - &psnk->psnk_dst.addr.v.a.mask, - &sn->raddr, sn->af)) { - /* Handle state to src_node linkage */ - if (sn->states != 0) - pf_clear_srcnodes(sn); - sn->expire = 1; - killed++; - } - PF_HASHROW_UNLOCK(sh); - } - - if (killed > 0) - pf_purge_expired_src_nodes(); - - psnk->psnk_killed = killed; - break; - } - - case DIOCSETHOSTID: { - u_int32_t *hostid = (u_int32_t *)addr; - - PF_RULES_WLOCK(); - if (*hostid == 0) - V_pf_status.hostid = arc4random(); - else - V_pf_status.hostid = *hostid; - PF_RULES_WUNLOCK(); - break; - } - - case DIOCOSFPFLUSH: - PF_RULES_WLOCK(); - pf_osfp_flush(); - PF_RULES_WUNLOCK(); - break; - - case DIOCIGETIFACES: { - struct pfioc_iface *io = (struct pfioc_iface *)addr; - struct pfi_kif *ifstore; - size_t bufsiz; - - if (io->pfiio_esize != sizeof(struct pfi_kif)) { - error = ENODEV; - break; - } - - bufsiz = io->pfiio_size * sizeof(struct pfi_kif); - ifstore = malloc(bufsiz, M_TEMP, M_WAITOK); - PF_RULES_RLOCK(); - pfi_get_ifaces(io->pfiio_name, ifstore, &io->pfiio_size); - PF_RULES_RUNLOCK(); - error = copyout(ifstore, io->pfiio_buffer, bufsiz); - free(ifstore, M_TEMP); - break; - } - - case DIOCSETIFFLAG: { - struct pfioc_iface *io = (struct pfioc_iface *)addr; - - PF_RULES_WLOCK(); - error = pfi_set_flags(io->pfiio_name, io->pfiio_flags); - PF_RULES_WUNLOCK(); - break; - } - - case DIOCCLRIFFLAG: { - struct pfioc_iface *io = (struct pfioc_iface *)addr; - - PF_RULES_WLOCK(); - error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags); - PF_RULES_WUNLOCK(); - break; - } - - default: - error = ENODEV; - break; - } -fail: - CURVNET_RESTORE(); - - return (error); -} - -void -pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) -{ - bzero(sp, sizeof(struct pfsync_state)); - - /* copy from state key */ - sp->key[PF_SK_WIRE].addr[0] = st->key[PF_SK_WIRE]->addr[0]; - sp->key[PF_SK_WIRE].addr[1] = st->key[PF_SK_WIRE]->addr[1]; - sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0]; - sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1]; - sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0]; - sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1]; - sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0]; - sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1]; - sp->proto = st->key[PF_SK_WIRE]->proto; - sp->af = st->key[PF_SK_WIRE]->af; - - /* copy from state */ - strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname)); - bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); - sp->creation = htonl(time_uptime - st->creation); - sp->expire = pf_state_expires(st); - if (sp->expire <= time_uptime) - sp->expire = htonl(0); - else - sp->expire = htonl(sp->expire - time_uptime); - - sp->direction = st->direction; - sp->log = st->log; - sp->timeout = st->timeout; - sp->state_flags = st->state_flags; - if (st->src_node) - sp->sync_flags |= PFSYNC_FLAG_SRCNODE; - if (st->nat_src_node) - sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE; - - sp->id = st->id; - sp->creatorid = st->creatorid; - pf_state_peer_hton(&st->src, &sp->src); - pf_state_peer_hton(&st->dst, &sp->dst); - - if (st->rule.ptr == NULL) - sp->rule = htonl(-1); - else - sp->rule = htonl(st->rule.ptr->nr); - if (st->anchor.ptr == NULL) - sp->anchor = htonl(-1); - else - sp->anchor = htonl(st->anchor.ptr->nr); - if (st->nat_rule.ptr == NULL) - sp->nat_rule = htonl(-1); - else - sp->nat_rule = htonl(st->nat_rule.ptr->nr); - - pf_state_counter_hton(st->packets[0], sp->packets[0]); - pf_state_counter_hton(st->packets[1], sp->packets[1]); - pf_state_counter_hton(st->bytes[0], sp->bytes[0]); - pf_state_counter_hton(st->bytes[1], sp->bytes[1]); - -} - -static void -pf_tbladdr_copyout(struct pf_addr_wrap *aw) -{ - struct pfr_ktable *kt; - - KASSERT(aw->type == PF_ADDR_TABLE, ("%s: type %u", __func__, aw->type)); - - kt = aw->p.tbl; - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) - kt = kt->pfrkt_root; - aw->p.tbl = NULL; - aw->p.tblcnt = (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) ? - kt->pfrkt_cnt : -1; -} - -/* - * XXX - Check for version missmatch!!! - */ -static void -pf_clear_states(void) -{ - struct pf_state *s; - u_int i; - - for (i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; -relock: - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - s->timeout = PFTM_PURGE; - /* Don't send out individual delete messages. */ - s->sync_state = PFSTATE_NOSYNC; - pf_unlink_state(s, PF_ENTER_LOCKED); - goto relock; - } - PF_HASHROW_UNLOCK(ih); - } -} - -static int -pf_clear_tables(void) -{ - struct pfioc_table io; - int error; - - bzero(&io, sizeof(io)); - - error = pfr_clr_tables(&io.pfrio_table, &io.pfrio_ndel, - io.pfrio_flags); - - return (error); -} - -static void -pf_clear_srcnodes(struct pf_src_node *n) -{ - struct pf_state *s; - int i; - - for (i = 0; i <= V_pf_hashmask; i++) { - struct pf_idhash *ih = &V_pf_idhash[i]; - - PF_HASHROW_LOCK(ih); - LIST_FOREACH(s, &ih->states, entry) { - if (n == NULL || n == s->src_node) - s->src_node = NULL; - if (n == NULL || n == s->nat_src_node) - s->nat_src_node = NULL; - } - PF_HASHROW_UNLOCK(ih); - } - - if (n == NULL) { - struct pf_srchash *sh; - - for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; - i++, sh++) { - PF_HASHROW_LOCK(sh); - LIST_FOREACH(n, &sh->nodes, entry) { - n->expire = 1; - n->states = 0; - } - PF_HASHROW_UNLOCK(sh); - } - } else { - /* XXX: hash slot should already be locked here. */ - n->expire = 1; - n->states = 0; - } -} -/* - * XXX - Check for version missmatch!!! - */ - -/* - * Duplicate pfctl -Fa operation to get rid of as much as we can. - */ -static int -shutdown_pf(void) -{ - int error = 0; - u_int32_t t[5]; - char nn = '\0'; - - V_pf_status.running = 0; - do { - if ((error = pf_begin_rules(&t[0], PF_RULESET_SCRUB, &nn)) - != 0) { - DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: SCRUB\n")); - break; - } - if ((error = pf_begin_rules(&t[1], PF_RULESET_FILTER, &nn)) - != 0) { - DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: FILTER\n")); - break; /* XXX: rollback? */ - } - if ((error = pf_begin_rules(&t[2], PF_RULESET_NAT, &nn)) - != 0) { - DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: NAT\n")); - break; /* XXX: rollback? */ - } - if ((error = pf_begin_rules(&t[3], PF_RULESET_BINAT, &nn)) - != 0) { - DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: BINAT\n")); - break; /* XXX: rollback? */ - } - if ((error = pf_begin_rules(&t[4], PF_RULESET_RDR, &nn)) - != 0) { - DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: RDR\n")); - break; /* XXX: rollback? */ - } - - /* XXX: these should always succeed here */ - pf_commit_rules(t[0], PF_RULESET_SCRUB, &nn); - pf_commit_rules(t[1], PF_RULESET_FILTER, &nn); - pf_commit_rules(t[2], PF_RULESET_NAT, &nn); - pf_commit_rules(t[3], PF_RULESET_BINAT, &nn); - pf_commit_rules(t[4], PF_RULESET_RDR, &nn); - - if ((error = pf_clear_tables()) != 0) - break; - -#ifdef ALTQ - if ((error = pf_begin_altq(&t[0])) != 0) { - DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: ALTQ\n")); - break; - } - pf_commit_altq(t[0]); -#endif - - pf_clear_states(); - - pf_clear_srcnodes(NULL); - - /* status does not use malloced mem so no need to cleanup */ - /* fingerprints and interfaces have thier own cleanup code */ - } while(0); - - return (error); -} - -#ifdef INET -static int -pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, - struct inpcb *inp) -{ - /* - * XXX Wed Jul 9 22:03:16 2003 UTC - * OpenBSD has changed its byte ordering convention on ip_len/ip_off - * in network stack. OpenBSD's network stack have converted - * ip_len/ip_off to host byte order frist as FreeBSD. - * Now this is not true anymore , so we should convert back to network - * byte order. - */ - struct ip *h = NULL; - int chk; - - if ((*m)->m_pkthdr.len >= (int)sizeof(struct ip)) { - /* if m_pkthdr.len is less than ip header, pf will handle. */ - h = mtod(*m, struct ip *); - HTONS(h->ip_len); - HTONS(h->ip_off); - } - CURVNET_SET(ifp->if_vnet); - chk = pf_test(PF_IN, ifp, m, inp); - CURVNET_RESTORE(); - if (chk && *m) { - m_freem(*m); - *m = NULL; - } - if (*m != NULL) { - /* pf_test can change ip header location */ - h = mtod(*m, struct ip *); - NTOHS(h->ip_len); - NTOHS(h->ip_off); - } - return chk; -} - -static int -pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, - struct inpcb *inp) -{ - /* - * XXX Wed Jul 9 22:03:16 2003 UTC - * OpenBSD has changed its byte ordering convention on ip_len/ip_off - * in network stack. OpenBSD's network stack have converted - * ip_len/ip_off to host byte order frist as FreeBSD. - * Now this is not true anymore , so we should convert back to network - * byte order. - */ - struct ip *h = NULL; - int chk; - - /* We need a proper CSUM befor we start (s. OpenBSD ip_output) */ - if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { - in_delayed_cksum(*m); - (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - if ((*m)->m_pkthdr.len >= (int)sizeof(*h)) { - /* if m_pkthdr.len is less than ip header, pf will handle. */ - h = mtod(*m, struct ip *); - HTONS(h->ip_len); - HTONS(h->ip_off); - } - CURVNET_SET(ifp->if_vnet); - chk = pf_test(PF_OUT, ifp, m, inp); - CURVNET_RESTORE(); - if (chk && *m) { - m_freem(*m); - *m = NULL; - } - if (*m != NULL) { - /* pf_test can change ip header location */ - h = mtod(*m, struct ip *); - NTOHS(h->ip_len); - NTOHS(h->ip_off); - } - return chk; -} -#endif - -#ifdef INET6 -static int -pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, - struct inpcb *inp) -{ - - /* - * IPv6 is not affected by ip_len/ip_off byte order changes. - */ - int chk; - - /* - * In case of loopback traffic IPv6 uses the real interface in - * order to support scoped addresses. In order to support stateful - * filtering we have change this to lo0 as it is the case in IPv4. - */ - CURVNET_SET(ifp->if_vnet); - chk = pf_test6(PF_IN, (*m)->m_flags & M_LOOP ? V_loif : ifp, m, inp); - CURVNET_RESTORE(); - if (chk && *m) { - m_freem(*m); - *m = NULL; - } - return chk; -} - -static int -pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, - struct inpcb *inp) -{ - /* - * IPv6 does not affected ip_len/ip_off byte order changes. - */ - int chk; - - /* We need a proper CSUM before we start (s. OpenBSD ip_output) */ - if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { -#ifdef INET - /* XXX-BZ copy&paste error from r126261? */ - in_delayed_cksum(*m); -#endif - (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - CURVNET_SET(ifp->if_vnet); - chk = pf_test6(PF_OUT, ifp, m, inp); - CURVNET_RESTORE(); - if (chk && *m) { - m_freem(*m); - *m = NULL; - } - return chk; -} -#endif /* INET6 */ - -static int -hook_pf(void) -{ -#ifdef INET - struct pfil_head *pfh_inet; -#endif -#ifdef INET6 - struct pfil_head *pfh_inet6; -#endif - - if (V_pf_pfil_hooked) - return (0); - -#ifdef INET - pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); - if (pfh_inet == NULL) - return (ESRCH); /* XXX */ - pfil_add_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); - pfil_add_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); -#endif -#ifdef INET6 - pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); - if (pfh_inet6 == NULL) { -#ifdef INET - pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, - pfh_inet); - pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, - pfh_inet); -#endif - return (ESRCH); /* XXX */ - } - pfil_add_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); - pfil_add_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); -#endif - - V_pf_pfil_hooked = 1; - return (0); -} - -static int -dehook_pf(void) -{ -#ifdef INET - struct pfil_head *pfh_inet; -#endif -#ifdef INET6 - struct pfil_head *pfh_inet6; -#endif - - if (V_pf_pfil_hooked == 0) - return (0); - -#ifdef INET - pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); - if (pfh_inet == NULL) - return (ESRCH); /* XXX */ - pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, - pfh_inet); - pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, - pfh_inet); -#endif -#ifdef INET6 - pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); - if (pfh_inet6 == NULL) - return (ESRCH); /* XXX */ - pfil_remove_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, - pfh_inet6); - pfil_remove_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, - pfh_inet6); -#endif - - V_pf_pfil_hooked = 0; - return (0); -} - -static int -pf_load(void) -{ - int error; - - VNET_ITERATOR_DECL(vnet_iter); - - VNET_LIST_RLOCK(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - V_pf_pfil_hooked = 0; - V_pf_end_threads = 0; - TAILQ_INIT(&V_pf_tags); - TAILQ_INIT(&V_pf_qids); - CURVNET_RESTORE(); - } - VNET_LIST_RUNLOCK(); - - rw_init(&pf_rules_lock, "pf rulesets"); - - pf_dev = make_dev(&pf_cdevsw, 0, 0, 0, 0600, PF_NAME); - if ((error = pfattach()) != 0) - return (error); - - return (0); -} - -static int -pf_unload(void) -{ - int error = 0; - - PF_RULES_WLOCK(); - V_pf_status.running = 0; - PF_RULES_WUNLOCK(); - swi_remove(V_pf_swi_cookie); - error = dehook_pf(); - if (error) { - /* - * Should not happen! - * XXX Due to error code ESRCH, kldunload will show - * a message like 'No such process'. - */ - printf("%s : pfil unregisteration fail\n", __FUNCTION__); - return error; - } - PF_RULES_WLOCK(); - shutdown_pf(); - V_pf_end_threads = 1; - while (V_pf_end_threads < 2) { - wakeup_one(pf_purge_thread); - rw_sleep(pf_purge_thread, &pf_rules_lock, 0, "pftmo", 0); - } - pf_normalize_cleanup(); - pfi_cleanup(); - pfr_cleanup(); - pf_osfp_flush(); - pf_cleanup(); - PF_RULES_WUNLOCK(); - destroy_dev(pf_dev); - rw_destroy(&pf_rules_lock); - - return (error); -} - -static int -pf_modevent(module_t mod, int type, void *data) -{ - int error = 0; - - switch(type) { - case MOD_LOAD: - error = pf_load(); - break; - case MOD_QUIESCE: - /* - * Module should not be unloaded due to race conditions. - */ - error = EPERM; - break; - case MOD_UNLOAD: - error = pf_unload(); - break; - default: - error = EINVAL; - break; - } - - return (error); -} - -static moduledata_t pf_mod = { - "pf", - pf_modevent, - 0 -}; - -DECLARE_MODULE(pf, pf_mod, SI_SUB_PSEUDO, SI_ORDER_FIRST); -MODULE_VERSION(pf, PF_MODVER); diff --git a/sys/contrib/pf/net/pf_lb.c b/sys/contrib/pf/net/pf_lb.c deleted file mode 100644 index 5b47852a5d1b4..0000000000000 --- a/sys/contrib/pf/net/pf_lb.c +++ /dev/null @@ -1,663 +0,0 @@ -/* $OpenBSD: pf_lb.c,v 1.2 2009/02/12 02:13:15 sthen Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002 - 2008 Henning Brauer - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Effort sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F30602-01-2-0537. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_pf.h" -#include "opt_inet.h" -#include "opt_inet6.h" - -#include -#include -#include - -#include -#include -#include -#include - -#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x - -static void pf_hash(struct pf_addr *, struct pf_addr *, - struct pf_poolhashkey *, sa_family_t); -static struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, - int, int, struct pfi_kif *, - struct pf_addr *, u_int16_t, struct pf_addr *, - u_int16_t, int); -static int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, - struct pf_addr *, struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, - struct pf_src_node **); - -#define mix(a,b,c) \ - do { \ - a -= b; a -= c; a ^= (c >> 13); \ - b -= c; b -= a; b ^= (a << 8); \ - c -= a; c -= b; c ^= (b >> 13); \ - a -= b; a -= c; a ^= (c >> 12); \ - b -= c; b -= a; b ^= (a << 16); \ - c -= a; c -= b; c ^= (b >> 5); \ - a -= b; a -= c; a ^= (c >> 3); \ - b -= c; b -= a; b ^= (a << 10); \ - c -= a; c -= b; c ^= (b >> 15); \ - } while (0) - -/* - * hash function based on bridge_hash in if_bridge.c - */ -static void -pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, - struct pf_poolhashkey *key, sa_family_t af) -{ - u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0]; - - switch (af) { -#ifdef INET - case AF_INET: - a += inaddr->addr32[0]; - b += key->key32[1]; - mix(a, b, c); - hash->addr32[0] = c + key->key32[2]; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - a += inaddr->addr32[0]; - b += inaddr->addr32[2]; - mix(a, b, c); - hash->addr32[0] = c; - a += inaddr->addr32[1]; - b += inaddr->addr32[3]; - c += key->key32[1]; - mix(a, b, c); - hash->addr32[1] = c; - a += inaddr->addr32[2]; - b += inaddr->addr32[1]; - c += key->key32[2]; - mix(a, b, c); - hash->addr32[2] = c; - a += inaddr->addr32[3]; - b += inaddr->addr32[0]; - c += key->key32[3]; - mix(a, b, c); - hash->addr32[3] = c; - break; -#endif /* INET6 */ - } -} - -static struct pf_rule * -pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, - int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, - struct pf_addr *daddr, u_int16_t dport, int rs_num) -{ - struct pf_rule *r, *rm = NULL; - struct pf_ruleset *ruleset = NULL; - int tag = -1; - int rtableid = -1; - int asd = 0; - - r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr); - while (r && rm == NULL) { - struct pf_rule_addr *src = NULL, *dst = NULL; - struct pf_addr_wrap *xdst = NULL; - - if (r->action == PF_BINAT && direction == PF_IN) { - src = &r->dst; - if (r->rpool.cur != NULL) - xdst = &r->rpool.cur->addr; - } else { - src = &r->src; - dst = &r->dst; - } - - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != pd->af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&src->addr, saddr, pd->af, - src->neg, kif, M_GETFIB(m))) - r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR : - PF_SKIP_DST_ADDR].ptr; - else if (src->port_op && !pf_match_port(src->port_op, - src->port[0], src->port[1], sport)) - r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT : - PF_SKIP_DST_PORT].ptr; - else if (dst != NULL && - PF_MISMATCHAW(&dst->addr, daddr, pd->af, dst->neg, NULL, - M_GETFIB(m))) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, pd->af, - 0, NULL, M_GETFIB(m))) - r = TAILQ_NEXT(r, entries); - else if (dst != NULL && dst->port_op && - !pf_match_port(dst->port_op, dst->port[0], - dst->port[1], dport)) - r = r->skip[PF_SKIP_DST_PORT].ptr; - else if (r->match_tag && !pf_match_tag(m, r, &tag, - pd->pf_mtag ? pd->pf_mtag->tag : 0)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != - IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m, - off, pd->hdr.tcp), r->os_fingerprint))) - r = TAILQ_NEXT(r, entries); - else { - if (r->tag) - tag = r->tag; - if (r->rtableid >= 0) - rtableid = r->rtableid; - if (r->anchor == NULL) { - rm = r; - } else - pf_step_into_anchor(&asd, &ruleset, rs_num, - &r, NULL, NULL); - } - if (r == NULL) - pf_step_out_of_anchor(&asd, &ruleset, rs_num, &r, - NULL, NULL); - } - - if (tag > 0 && pf_tag_packet(m, pd, tag)) - return (NULL); - if (rtableid >= 0) - M_SETFIB(m, rtableid); - - if (rm != NULL && (rm->action == PF_NONAT || - rm->action == PF_NORDR || rm->action == PF_NOBINAT)) - return (NULL); - return (rm); -} - -static int -pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, - struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, - struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, - struct pf_src_node **sn) -{ - struct pf_state_key_cmp key; - struct pf_addr init_addr; - u_int16_t cut; - - bzero(&init_addr, sizeof(init_addr)); - if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) - return (1); - - if (proto == IPPROTO_ICMP) { - low = 1; - high = 65535; - } - - do { - key.af = af; - key.proto = proto; - PF_ACPY(&key.addr[1], daddr, key.af); - PF_ACPY(&key.addr[0], naddr, key.af); - key.port[1] = dport; - - /* - * port search; start random, step; - * similar 2 portloop in in_pcbbind - */ - if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP || - proto == IPPROTO_ICMP)) { - key.port[0] = dport; - if (pf_find_state_all(&key, PF_IN, NULL) == NULL) - return (0); - } else if (low == 0 && high == 0) { - key.port[0] = *nport; - if (pf_find_state_all(&key, PF_IN, NULL) == NULL) - return (0); - } else if (low == high) { - key.port[0] = htons(low); - if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { - *nport = htons(low); - return (0); - } - } else { - u_int16_t tmp; - - if (low > high) { - tmp = low; - low = high; - high = tmp; - } - /* low < high */ - cut = htonl(arc4random()) % (1 + high - low) + low; - /* low <= cut <= high */ - for (tmp = cut; tmp <= high; ++(tmp)) { - key.port[0] = htons(tmp); - if (pf_find_state_all(&key, PF_IN, NULL) == - NULL) { - *nport = htons(tmp); - return (0); - } - } - for (tmp = cut - 1; tmp >= low; --(tmp)) { - key.port[0] = htons(tmp); - if (pf_find_state_all(&key, PF_IN, NULL) == - NULL) { - *nport = htons(tmp); - return (0); - } - } - } - - switch (r->rpool.opts & PF_POOL_TYPEMASK) { - case PF_POOL_RANDOM: - case PF_POOL_ROUNDROBIN: - if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) - return (1); - break; - case PF_POOL_NONE: - case PF_POOL_SRCHASH: - case PF_POOL_BITMASK: - default: - return (1); - } - } while (! PF_AEQ(&init_addr, naddr, af) ); - return (1); /* none available */ -} - -int -pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, - struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) -{ - struct pf_pool *rpool = &r->rpool; - struct pf_addr *raddr = NULL, *rmask = NULL; - - if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && - (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { - *sn = pf_find_src_node(saddr, r, af, 0); - if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { - PF_ACPY(naddr, &(*sn)->raddr, af); - if (V_pf_status.debug >= PF_DEBUG_MISC) { - printf("pf_map_addr: src tracking maps "); - pf_print_host(saddr, 0, af); - printf(" to "); - pf_print_host(naddr, 0, af); - printf("\n"); - } - return (0); - } - } - - if (rpool->cur->addr.type == PF_ADDR_NOROUTE) - return (1); - if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - switch (af) { -#ifdef INET - case AF_INET: - if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && - (rpool->opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) - return (1); - raddr = &rpool->cur->addr.p.dyn->pfid_addr4; - rmask = &rpool->cur->addr.p.dyn->pfid_mask4; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 && - (rpool->opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) - return (1); - raddr = &rpool->cur->addr.p.dyn->pfid_addr6; - rmask = &rpool->cur->addr.p.dyn->pfid_mask6; - break; -#endif /* INET6 */ - } - } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { - if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) - return (1); /* unsupported */ - } else { - raddr = &rpool->cur->addr.v.a.addr; - rmask = &rpool->cur->addr.v.a.mask; - } - - switch (rpool->opts & PF_POOL_TYPEMASK) { - case PF_POOL_NONE: - PF_ACPY(naddr, raddr, af); - break; - case PF_POOL_BITMASK: - PF_POOLMASK(naddr, raddr, rmask, saddr, af); - break; - case PF_POOL_RANDOM: - if (init_addr != NULL && PF_AZERO(init_addr, af)) { - switch (af) { -#ifdef INET - case AF_INET: - rpool->counter.addr32[0] = htonl(arc4random()); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (rmask->addr32[3] != 0xffffffff) - rpool->counter.addr32[3] = - htonl(arc4random()); - else - break; - if (rmask->addr32[2] != 0xffffffff) - rpool->counter.addr32[2] = - htonl(arc4random()); - else - break; - if (rmask->addr32[1] != 0xffffffff) - rpool->counter.addr32[1] = - htonl(arc4random()); - else - break; - if (rmask->addr32[0] != 0xffffffff) - rpool->counter.addr32[0] = - htonl(arc4random()); - break; -#endif /* INET6 */ - } - PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); - PF_ACPY(init_addr, naddr, af); - - } else { - PF_AINC(&rpool->counter, af); - PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); - } - break; - case PF_POOL_SRCHASH: - { - unsigned char hash[16]; - - pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); - PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); - break; - } - case PF_POOL_ROUNDROBIN: - { - struct pf_pooladdr *acur = rpool->cur; - - /* - * XXXGL: in the round-robin case we need to store - * the round-robin machine state in the rule, thus - * forwarding thread needs to modify rule. - * - * This is done w/o locking, because performance is assumed - * more important than round-robin precision. - * - * In the simpliest case we just update the "rpool->cur" - * pointer. However, if pool contains tables or dynamic - * addresses, then "tblidx" is also used to store machine - * state. Since "tblidx" is int, concurrent access to it can't - * lead to inconsistence, only to lost of precision. - * - * Things get worse, if table contains not hosts, but - * prefixes. In this case counter also stores machine state, - * and for IPv6 address, counter can't be updated atomically. - * Probably, using round-robin on a table containing IPv6 - * prefixes (or even IPv4) would cause a panic. - */ - - if (rpool->cur->addr.type == PF_ADDR_TABLE) { - if (!pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, af)) - goto get_addr; - } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, af)) - goto get_addr; - } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) - goto get_addr; - - try_next: - if (TAILQ_NEXT(rpool->cur, entries) == NULL) - rpool->cur = TAILQ_FIRST(&rpool->list); - else - rpool->cur = TAILQ_NEXT(rpool->cur, entries); - if (rpool->cur->addr.type == PF_ADDR_TABLE) { - rpool->tblidx = -1; - if (pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, af)) { - /* table contains no address of type 'af' */ - if (rpool->cur != acur) - goto try_next; - return (1); - } - } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - rpool->tblidx = -1; - if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, af)) { - /* table contains no address of type 'af' */ - if (rpool->cur != acur) - goto try_next; - return (1); - } - } else { - raddr = &rpool->cur->addr.v.a.addr; - rmask = &rpool->cur->addr.v.a.mask; - PF_ACPY(&rpool->counter, raddr, af); - } - - get_addr: - PF_ACPY(naddr, &rpool->counter, af); - if (init_addr != NULL && PF_AZERO(init_addr, af)) - PF_ACPY(init_addr, naddr, af); - PF_AINC(&rpool->counter, af); - break; - } - } - if (*sn != NULL) - PF_ACPY(&(*sn)->raddr, naddr, af); - - if (V_pf_status.debug >= PF_DEBUG_MISC && - (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { - printf("pf_map_addr: selected address "); - pf_print_host(naddr, 0, af); - printf("\n"); - } - - return (0); -} - -struct pf_rule * -pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, - struct pfi_kif *kif, struct pf_src_node **sn, - struct pf_state_key **skp, struct pf_state_key **nkp, - struct pf_addr *saddr, struct pf_addr *daddr, - u_int16_t sport, u_int16_t dport) -{ - struct pf_rule *r = NULL; - struct pf_addr *naddr; - uint16_t *nport; - - PF_RULES_RASSERT(); - KASSERT(*skp == NULL, ("*skp not NULL")); - KASSERT(*nkp == NULL, ("*nkp not NULL")); - - if (direction == PF_OUT) { - r = pf_match_translation(pd, m, off, direction, kif, saddr, - sport, daddr, dport, PF_RULESET_BINAT); - if (r == NULL) - r = pf_match_translation(pd, m, off, direction, kif, - saddr, sport, daddr, dport, PF_RULESET_NAT); - } else { - r = pf_match_translation(pd, m, off, direction, kif, saddr, - sport, daddr, dport, PF_RULESET_RDR); - if (r == NULL) - r = pf_match_translation(pd, m, off, direction, kif, - saddr, sport, daddr, dport, PF_RULESET_BINAT); - } - - if (r == NULL) - return (NULL); - - switch (r->action) { - case PF_NONAT: - case PF_NOBINAT: - case PF_NORDR: - return (NULL); - } - - *skp = pf_state_key_setup(pd, saddr, daddr, sport, dport); - if (*skp == NULL) - return (NULL); - *nkp = pf_state_key_clone(*skp); - if (*nkp == NULL) { - uma_zfree(V_pf_state_key_z, skp); - *skp = NULL; - return (NULL); - } - - /* XXX We only modify one side for now. */ - naddr = &(*nkp)->addr[1]; - nport = &(*nkp)->port[1]; - - switch (r->action) { - case PF_NAT: - if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, - naddr, nport, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], sn)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: NAT proxy port allocation (%u-%u) failed\n", - r->rpool.proxy_port[0], r->rpool.proxy_port[1])); - goto notrans; - } - break; - case PF_BINAT: - switch (direction) { - case PF_OUT: - if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ - switch (pd->af) { -#ifdef INET - case AF_INET: - if (r->rpool.cur->addr.p.dyn-> - pfid_acnt4 < 1) - goto notrans; - PF_POOLMASK(naddr, - &r->rpool.cur->addr.p.dyn-> - pfid_addr4, - &r->rpool.cur->addr.p.dyn-> - pfid_mask4, saddr, AF_INET); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (r->rpool.cur->addr.p.dyn-> - pfid_acnt6 < 1) - goto notrans; - PF_POOLMASK(naddr, - &r->rpool.cur->addr.p.dyn-> - pfid_addr6, - &r->rpool.cur->addr.p.dyn-> - pfid_mask6, saddr, AF_INET6); - break; -#endif /* INET6 */ - } - } else - PF_POOLMASK(naddr, - &r->rpool.cur->addr.v.a.addr, - &r->rpool.cur->addr.v.a.mask, saddr, - pd->af); - break; - case PF_IN: - if (r->src.addr.type == PF_ADDR_DYNIFTL) { - switch (pd->af) { -#ifdef INET - case AF_INET: - if (r->src.addr.p.dyn-> pfid_acnt4 < 1) - goto notrans; - PF_POOLMASK(naddr, - &r->src.addr.p.dyn->pfid_addr4, - &r->src.addr.p.dyn->pfid_mask4, - daddr, AF_INET); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (r->src.addr.p.dyn->pfid_acnt6 < 1) - goto notrans; - PF_POOLMASK(naddr, - &r->src.addr.p.dyn->pfid_addr6, - &r->src.addr.p.dyn->pfid_mask6, - daddr, AF_INET6); - break; -#endif /* INET6 */ - } - } else - PF_POOLMASK(naddr, &r->src.addr.v.a.addr, - &r->src.addr.v.a.mask, daddr, pd->af); - break; - } - break; - case PF_RDR: { - if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) - goto notrans; - if ((r->rpool.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) - PF_POOLMASK(naddr, naddr, &r->rpool.cur->addr.v.a.mask, - daddr, pd->af); - - if (r->rpool.proxy_port[1]) { - uint32_t tmp_nport; - - tmp_nport = ((ntohs(dport) - ntohs(r->dst.port[0])) % - (r->rpool.proxy_port[1] - r->rpool.proxy_port[0] + - 1)) + r->rpool.proxy_port[0]; - - /* Wrap around if necessary. */ - if (tmp_nport > 65535) - tmp_nport -= 65535; - *nport = htons((uint16_t)tmp_nport); - } else if (r->rpool.proxy_port[0]) - *nport = htons(r->rpool.proxy_port[0]); - break; - } - default: - panic("%s: unknown action %u", __func__, r->action); - } - - /* Return success only if translation really happened. */ - if (bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) - return (r); - -notrans: - uma_zfree(V_pf_state_key_z, *nkp); - uma_zfree(V_pf_state_key_z, *skp); - *skp = *nkp = NULL; - - return (NULL); -} diff --git a/sys/contrib/pf/net/pf_mtag.h b/sys/contrib/pf/net/pf_mtag.h deleted file mode 100644 index baff00a699b9b..0000000000000 --- a/sys/contrib/pf/net/pf_mtag.h +++ /dev/null @@ -1,62 +0,0 @@ -/* $FreeBSD$ */ -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _NET_PF_MTAG_H_ -#define _NET_PF_MTAG_H_ - -#ifdef _KERNEL - -#define PF_TAG_GENERATED 0x01 -#define PF_TAG_FRAGCACHE 0x02 -#define PF_TAG_TRANSLATE_LOCALHOST 0x04 -#define PF_PACKET_LOOPED 0x08 -#define PF_FASTFWD_OURS_PRESENT 0x10 - -struct pf_mtag { - void *hdr; /* saved hdr pos in mbuf, for ECN */ - u_int32_t qid; /* queue id */ - u_int16_t tag; /* tag id */ - u_int8_t flags; - u_int8_t routed; -}; - -static __inline struct pf_mtag * -pf_find_mtag(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) == NULL) - return (NULL); - - return ((struct pf_mtag *)(mtag + 1)); -} -#endif /* _KERNEL */ -#endif /* _NET_PF_MTAG_H_ */ diff --git a/sys/contrib/pf/net/pf_norm.c b/sys/contrib/pf/net/pf_norm.c deleted file mode 100644 index 9063fe8917c03..0000000000000 --- a/sys/contrib/pf/net/pf_norm.c +++ /dev/null @@ -1,1999 +0,0 @@ -/* $OpenBSD: pf_norm.c,v 1.114 2009/01/29 14:11:45 henning Exp $ */ - -/* - * Copyright 2001 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" -#include "opt_pf.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifdef INET6 -#include -#endif /* INET6 */ - -struct pf_frent { - LIST_ENTRY(pf_frent) fr_next; - union { - struct { - struct ip *_fr_ip; - struct mbuf *_fr_m; - } _frag; - struct { - uint16_t _fr_off; - uint16_t _fr_end; - } _cache; - } _u; -}; -#define fr_ip _u._frag._fr_ip -#define fr_m _u._frag._fr_m -#define fr_off _u._cache._fr_off -#define fr_end _u._cache._fr_end - -struct pf_fragment { - RB_ENTRY(pf_fragment) fr_entry; - TAILQ_ENTRY(pf_fragment) frag_next; - struct in_addr fr_src; - struct in_addr fr_dst; - u_int8_t fr_p; /* protocol of this fragment */ - u_int8_t fr_flags; /* status flags */ -#define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ -#define PFFRAG_NOBUFFER 0x0002 /* Non-buffering fragment cache */ -#define PFFRAG_DROP 0x0004 /* Drop all fragments */ -#define BUFFER_FRAGMENTS(fr) (!((fr)->fr_flags & PFFRAG_NOBUFFER)) - u_int16_t fr_id; /* fragment id for reassemble */ - u_int16_t fr_max; /* fragment data max */ - u_int32_t fr_timeout; - LIST_HEAD(, pf_frent) fr_queue; -}; - -static struct mtx pf_frag_mtx; -#define PF_FRAG_LOCK() mtx_lock(&pf_frag_mtx) -#define PF_FRAG_UNLOCK() mtx_unlock(&pf_frag_mtx) -#define PF_FRAG_ASSERT() mtx_assert(&pf_frag_mtx, MA_OWNED) - -VNET_DEFINE(uma_zone_t, pf_state_scrub_z); /* XXX: shared with pfsync */ - -static VNET_DEFINE(uma_zone_t, pf_frent_z); -#define V_pf_frent_z VNET(pf_frent_z) -static VNET_DEFINE(uma_zone_t, pf_frag_z); -#define V_pf_frag_z VNET(pf_frag_z) - -TAILQ_HEAD(pf_fragqueue, pf_fragment); -TAILQ_HEAD(pf_cachequeue, pf_fragment); -static VNET_DEFINE(struct pf_fragqueue, pf_fragqueue); -#define V_pf_fragqueue VNET(pf_fragqueue) -static VNET_DEFINE(struct pf_cachequeue, pf_cachequeue); -#define V_pf_cachequeue VNET(pf_cachequeue) -RB_HEAD(pf_frag_tree, pf_fragment); -static VNET_DEFINE(struct pf_frag_tree, pf_frag_tree); -#define V_pf_frag_tree VNET(pf_frag_tree) -static VNET_DEFINE(struct pf_frag_tree, pf_cache_tree); -#define V_pf_cache_tree VNET(pf_cache_tree) -static int pf_frag_compare(struct pf_fragment *, - struct pf_fragment *); -static RB_PROTOTYPE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); -static RB_GENERATE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); - -/* Private prototypes */ -static void pf_free_fragment(struct pf_fragment *); -static void pf_remove_fragment(struct pf_fragment *); -static int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *, - struct tcphdr *, int, sa_family_t); -#ifdef INET -static void pf_ip2key(struct pf_fragment *, struct ip *); -static void pf_scrub_ip(struct mbuf **, u_int32_t, u_int8_t, - u_int8_t); -static void pf_flush_fragments(void); -static struct pf_fragment *pf_find_fragment(struct ip *, struct pf_frag_tree *); -static struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment **, - struct pf_frent *, int); -static struct mbuf *pf_fragcache(struct mbuf **, struct ip*, - struct pf_fragment **, int, int, int *); -#endif /* INET */ -#ifdef INET6 -static void pf_scrub_ip6(struct mbuf **, u_int8_t); -#endif -#define DPFPRINTF(x) do { \ - if (V_pf_status.debug >= PF_DEBUG_MISC) { \ - printf("%s: ", __func__); \ - printf x ; \ - } \ -} while(0) - -void -pf_normalize_init(void) -{ - - V_pf_frag_z = uma_zcreate("pf frags", sizeof(struct pf_fragment), - NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); - V_pf_frent_z = uma_zcreate("pf frag entries", sizeof(struct pf_frent), - NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); - V_pf_state_scrub_z = uma_zcreate("pf state scrubs", - sizeof(struct pf_state_scrub), NULL, NULL, NULL, NULL, - UMA_ALIGN_PTR, 0); - - V_pf_limits[PF_LIMIT_FRAGS].zone = V_pf_frent_z; - V_pf_limits[PF_LIMIT_FRAGS].limit = PFFRAG_FRENT_HIWAT; - uma_zone_set_max(V_pf_frent_z, PFFRAG_FRENT_HIWAT); - - mtx_init(&pf_frag_mtx, "pf fragments", NULL, MTX_DEF); - - TAILQ_INIT(&V_pf_fragqueue); - TAILQ_INIT(&V_pf_cachequeue); -} - -void -pf_normalize_cleanup(void) -{ - - uma_zdestroy(V_pf_state_scrub_z); - uma_zdestroy(V_pf_frent_z); - uma_zdestroy(V_pf_frag_z); - - mtx_destroy(&pf_frag_mtx); -} - -static int -pf_frag_compare(struct pf_fragment *a, struct pf_fragment *b) -{ - int diff; - - if ((diff = a->fr_id - b->fr_id)) - return (diff); - else if ((diff = a->fr_p - b->fr_p)) - return (diff); - else if (a->fr_src.s_addr < b->fr_src.s_addr) - return (-1); - else if (a->fr_src.s_addr > b->fr_src.s_addr) - return (1); - else if (a->fr_dst.s_addr < b->fr_dst.s_addr) - return (-1); - else if (a->fr_dst.s_addr > b->fr_dst.s_addr) - return (1); - return (0); -} - -void -pf_purge_expired_fragments(void) -{ - struct pf_fragment *frag; - u_int32_t expire = time_uptime - - V_pf_default_rule.timeout[PFTM_FRAG]; - - PF_FRAG_LOCK(); - while ((frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue)) != NULL) { - KASSERT((BUFFER_FRAGMENTS(frag)), - ("BUFFER_FRAGMENTS(frag) == 0: %s", __FUNCTION__)); - if (frag->fr_timeout > expire) - break; - - DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); - pf_free_fragment(frag); - } - - while ((frag = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue)) != NULL) { - KASSERT((!BUFFER_FRAGMENTS(frag)), - ("BUFFER_FRAGMENTS(frag) != 0: %s", __FUNCTION__)); - if (frag->fr_timeout > expire) - break; - - DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); - pf_free_fragment(frag); - KASSERT((TAILQ_EMPTY(&V_pf_cachequeue) || - TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue) != frag), - ("!(TAILQ_EMPTY() || TAILQ_LAST() == farg): %s", - __FUNCTION__)); - } - PF_FRAG_UNLOCK(); -} - -#ifdef INET -/* - * Try to flush old fragments to make space for new ones - */ -static void -pf_flush_fragments(void) -{ - struct pf_fragment *frag, *cache; - int goal; - - PF_FRAG_ASSERT(); - - goal = uma_zone_get_cur(V_pf_frent_z) * 9 / 10; - DPFPRINTF(("trying to free %d frag entriess\n", goal)); - while (goal < uma_zone_get_cur(V_pf_frent_z)) { - frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue); - if (frag) - pf_free_fragment(frag); - cache = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue); - if (cache) - pf_free_fragment(cache); - if (frag == NULL && cache == NULL) - break; - } -} -#endif /* INET */ - -/* Frees the fragments and all associated entries */ -static void -pf_free_fragment(struct pf_fragment *frag) -{ - struct pf_frent *frent; - - PF_FRAG_ASSERT(); - - /* Free all fragments */ - if (BUFFER_FRAGMENTS(frag)) { - for (frent = LIST_FIRST(&frag->fr_queue); frent; - frent = LIST_FIRST(&frag->fr_queue)) { - LIST_REMOVE(frent, fr_next); - - m_freem(frent->fr_m); - uma_zfree(V_pf_frent_z, frent); - } - } else { - for (frent = LIST_FIRST(&frag->fr_queue); frent; - frent = LIST_FIRST(&frag->fr_queue)) { - LIST_REMOVE(frent, fr_next); - - KASSERT((LIST_EMPTY(&frag->fr_queue) || - LIST_FIRST(&frag->fr_queue)->fr_off > - frent->fr_end), - ("! (LIST_EMPTY() || LIST_FIRST()->fr_off >" - " frent->fr_end): %s", __func__)); - - uma_zfree(V_pf_frent_z, frent); - } - } - - pf_remove_fragment(frag); -} - -#ifdef INET -static void -pf_ip2key(struct pf_fragment *key, struct ip *ip) -{ - key->fr_p = ip->ip_p; - key->fr_id = ip->ip_id; - key->fr_src.s_addr = ip->ip_src.s_addr; - key->fr_dst.s_addr = ip->ip_dst.s_addr; -} - -static struct pf_fragment * -pf_find_fragment(struct ip *ip, struct pf_frag_tree *tree) -{ - struct pf_fragment key; - struct pf_fragment *frag; - - PF_FRAG_ASSERT(); - - pf_ip2key(&key, ip); - - frag = RB_FIND(pf_frag_tree, tree, &key); - if (frag != NULL) { - /* XXX Are we sure we want to update the timeout? */ - frag->fr_timeout = time_uptime; - if (BUFFER_FRAGMENTS(frag)) { - TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next); - TAILQ_INSERT_HEAD(&V_pf_fragqueue, frag, frag_next); - } else { - TAILQ_REMOVE(&V_pf_cachequeue, frag, frag_next); - TAILQ_INSERT_HEAD(&V_pf_cachequeue, frag, frag_next); - } - } - - return (frag); -} -#endif /* INET */ - -/* Removes a fragment from the fragment queue and frees the fragment */ - -static void -pf_remove_fragment(struct pf_fragment *frag) -{ - - PF_FRAG_ASSERT(); - - if (BUFFER_FRAGMENTS(frag)) { - RB_REMOVE(pf_frag_tree, &V_pf_frag_tree, frag); - TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next); - uma_zfree(V_pf_frag_z, frag); - } else { - RB_REMOVE(pf_frag_tree, &V_pf_cache_tree, frag); - TAILQ_REMOVE(&V_pf_cachequeue, frag, frag_next); - uma_zfree(V_pf_frag_z, frag); - } -} - -#ifdef INET -#define FR_IP_OFF(fr) ((ntohs((fr)->fr_ip->ip_off) & IP_OFFMASK) << 3) -static struct mbuf * -pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, - struct pf_frent *frent, int mff) -{ - struct mbuf *m = *m0, *m2; - struct pf_frent *frea, *next; - struct pf_frent *frep = NULL; - struct ip *ip = frent->fr_ip; - int hlen = ip->ip_hl << 2; - u_int16_t off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3; - u_int16_t ip_len = ntohs(ip->ip_len) - ip->ip_hl * 4; - u_int16_t max = ip_len + off; - - PF_FRAG_ASSERT(); - KASSERT((*frag == NULL || BUFFER_FRAGMENTS(*frag)), - ("! (*frag == NULL || BUFFER_FRAGMENTS(*frag)): %s", __FUNCTION__)); - - /* Strip off ip header */ - m->m_data += hlen; - m->m_len -= hlen; - - /* Create a new reassembly queue for this packet */ - if (*frag == NULL) { - *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); - if (*frag == NULL) { - pf_flush_fragments(); - *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); - if (*frag == NULL) - goto drop_fragment; - } - - (*frag)->fr_flags = 0; - (*frag)->fr_max = 0; - (*frag)->fr_src = frent->fr_ip->ip_src; - (*frag)->fr_dst = frent->fr_ip->ip_dst; - (*frag)->fr_p = frent->fr_ip->ip_p; - (*frag)->fr_id = frent->fr_ip->ip_id; - (*frag)->fr_timeout = time_uptime; - LIST_INIT(&(*frag)->fr_queue); - - RB_INSERT(pf_frag_tree, &V_pf_frag_tree, *frag); - TAILQ_INSERT_HEAD(&V_pf_fragqueue, *frag, frag_next); - - /* We do not have a previous fragment */ - frep = NULL; - goto insert; - } - - /* - * Find a fragment after the current one: - * - off contains the real shifted offset. - */ - LIST_FOREACH(frea, &(*frag)->fr_queue, fr_next) { - if (FR_IP_OFF(frea) > off) - break; - frep = frea; - } - - KASSERT((frep != NULL || frea != NULL), - ("!(frep != NULL || frea != NULL): %s", __FUNCTION__));; - - if (frep != NULL && - FR_IP_OFF(frep) + ntohs(frep->fr_ip->ip_len) - frep->fr_ip->ip_hl * - 4 > off) - { - u_int16_t precut; - - precut = FR_IP_OFF(frep) + ntohs(frep->fr_ip->ip_len) - - frep->fr_ip->ip_hl * 4 - off; - if (precut >= ip_len) - goto drop_fragment; - m_adj(frent->fr_m, precut); - DPFPRINTF(("overlap -%d\n", precut)); - /* Enforce 8 byte boundaries */ - ip->ip_off = htons(ntohs(ip->ip_off) + (precut >> 3)); - off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3; - ip_len -= precut; - ip->ip_len = htons(ip_len); - } - - for (; frea != NULL && ip_len + off > FR_IP_OFF(frea); - frea = next) - { - u_int16_t aftercut; - - aftercut = ip_len + off - FR_IP_OFF(frea); - DPFPRINTF(("adjust overlap %d\n", aftercut)); - if (aftercut < ntohs(frea->fr_ip->ip_len) - frea->fr_ip->ip_hl - * 4) - { - frea->fr_ip->ip_len = - htons(ntohs(frea->fr_ip->ip_len) - aftercut); - frea->fr_ip->ip_off = htons(ntohs(frea->fr_ip->ip_off) + - (aftercut >> 3)); - m_adj(frea->fr_m, aftercut); - break; - } - - /* This fragment is completely overlapped, lose it */ - next = LIST_NEXT(frea, fr_next); - m_freem(frea->fr_m); - LIST_REMOVE(frea, fr_next); - uma_zfree(V_pf_frent_z, frea); - } - - insert: - /* Update maximum data size */ - if ((*frag)->fr_max < max) - (*frag)->fr_max = max; - /* This is the last segment */ - if (!mff) - (*frag)->fr_flags |= PFFRAG_SEENLAST; - - if (frep == NULL) - LIST_INSERT_HEAD(&(*frag)->fr_queue, frent, fr_next); - else - LIST_INSERT_AFTER(frep, frent, fr_next); - - /* Check if we are completely reassembled */ - if (!((*frag)->fr_flags & PFFRAG_SEENLAST)) - return (NULL); - - /* Check if we have all the data */ - off = 0; - for (frep = LIST_FIRST(&(*frag)->fr_queue); frep; frep = next) { - next = LIST_NEXT(frep, fr_next); - - off += ntohs(frep->fr_ip->ip_len) - frep->fr_ip->ip_hl * 4; - if (off < (*frag)->fr_max && - (next == NULL || FR_IP_OFF(next) != off)) - { - DPFPRINTF(("missing fragment at %d, next %d, max %d\n", - off, next == NULL ? -1 : FR_IP_OFF(next), - (*frag)->fr_max)); - return (NULL); - } - } - DPFPRINTF(("%d < %d?\n", off, (*frag)->fr_max)); - if (off < (*frag)->fr_max) - return (NULL); - - /* We have all the data */ - frent = LIST_FIRST(&(*frag)->fr_queue); - KASSERT((frent != NULL), ("frent == NULL: %s", __FUNCTION__)); - if ((frent->fr_ip->ip_hl << 2) + off > IP_MAXPACKET) { - DPFPRINTF(("drop: too big: %d\n", off)); - pf_free_fragment(*frag); - *frag = NULL; - return (NULL); - } - next = LIST_NEXT(frent, fr_next); - - /* Magic from ip_input */ - ip = frent->fr_ip; - m = frent->fr_m; - m2 = m->m_next; - m->m_next = NULL; - m_cat(m, m2); - uma_zfree(V_pf_frent_z, frent); - for (frent = next; frent != NULL; frent = next) { - next = LIST_NEXT(frent, fr_next); - - m2 = frent->fr_m; - uma_zfree(V_pf_frent_z, frent); - m->m_pkthdr.csum_flags &= m2->m_pkthdr.csum_flags; - m->m_pkthdr.csum_data += m2->m_pkthdr.csum_data; - m_cat(m, m2); - } - - while (m->m_pkthdr.csum_data & 0xffff0000) - m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data & 0xffff) + - (m->m_pkthdr.csum_data >> 16); - ip->ip_src = (*frag)->fr_src; - ip->ip_dst = (*frag)->fr_dst; - - /* Remove from fragment queue */ - pf_remove_fragment(*frag); - *frag = NULL; - - hlen = ip->ip_hl << 2; - ip->ip_len = htons(off + hlen); - m->m_len += hlen; - m->m_data -= hlen; - - /* some debugging cruft by sklower, below, will go away soon */ - /* XXX this should be done elsewhere */ - if (m->m_flags & M_PKTHDR) { - int plen = 0; - for (m2 = m; m2; m2 = m2->m_next) - plen += m2->m_len; - m->m_pkthdr.len = plen; - } - - DPFPRINTF(("complete: %p(%d)\n", m, ntohs(ip->ip_len))); - return (m); - - drop_fragment: - /* Oops - fail safe - drop packet */ - uma_zfree(V_pf_frent_z, frent); - m_freem(m); - return (NULL); -} - -static struct mbuf * -pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, - int drop, int *nomem) -{ - struct mbuf *m = *m0; - struct pf_frent *frp, *fra, *cur = NULL; - int ip_len = ntohs(h->ip_len) - (h->ip_hl << 2); - u_int16_t off = ntohs(h->ip_off) << 3; - u_int16_t max = ip_len + off; - int hosed = 0; - - PF_FRAG_ASSERT(); - KASSERT((*frag == NULL || !BUFFER_FRAGMENTS(*frag)), - ("!(*frag == NULL || !BUFFER_FRAGMENTS(*frag)): %s", __FUNCTION__)); - - /* Create a new range queue for this packet */ - if (*frag == NULL) { - *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); - if (*frag == NULL) { - pf_flush_fragments(); - *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); - if (*frag == NULL) - goto no_mem; - } - - /* Get an entry for the queue */ - cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); - if (cur == NULL) { - uma_zfree(V_pf_frag_z, *frag); - *frag = NULL; - goto no_mem; - } - - (*frag)->fr_flags = PFFRAG_NOBUFFER; - (*frag)->fr_max = 0; - (*frag)->fr_src = h->ip_src; - (*frag)->fr_dst = h->ip_dst; - (*frag)->fr_p = h->ip_p; - (*frag)->fr_id = h->ip_id; - (*frag)->fr_timeout = time_uptime; - - cur->fr_off = off; - cur->fr_end = max; - LIST_INIT(&(*frag)->fr_queue); - LIST_INSERT_HEAD(&(*frag)->fr_queue, cur, fr_next); - - RB_INSERT(pf_frag_tree, &V_pf_cache_tree, *frag); - TAILQ_INSERT_HEAD(&V_pf_cachequeue, *frag, frag_next); - - DPFPRINTF(("fragcache[%d]: new %d-%d\n", h->ip_id, off, max)); - - goto pass; - } - - /* - * Find a fragment after the current one: - * - off contains the real shifted offset. - */ - frp = NULL; - LIST_FOREACH(fra, &(*frag)->fr_queue, fr_next) { - if (fra->fr_off > off) - break; - frp = fra; - } - - KASSERT((frp != NULL || fra != NULL), - ("!(frp != NULL || fra != NULL): %s", __FUNCTION__)); - - if (frp != NULL) { - int precut; - - precut = frp->fr_end - off; - if (precut >= ip_len) { - /* Fragment is entirely a duplicate */ - DPFPRINTF(("fragcache[%d]: dead (%d-%d) %d-%d\n", - h->ip_id, frp->fr_off, frp->fr_end, off, max)); - goto drop_fragment; - } - if (precut == 0) { - /* They are adjacent. Fixup cache entry */ - DPFPRINTF(("fragcache[%d]: adjacent (%d-%d) %d-%d\n", - h->ip_id, frp->fr_off, frp->fr_end, off, max)); - frp->fr_end = max; - } else if (precut > 0) { - /* The first part of this payload overlaps with a - * fragment that has already been passed. - * Need to trim off the first part of the payload. - * But to do so easily, we need to create another - * mbuf to throw the original header into. - */ - - DPFPRINTF(("fragcache[%d]: chop %d (%d-%d) %d-%d\n", - h->ip_id, precut, frp->fr_off, frp->fr_end, off, - max)); - - off += precut; - max -= precut; - /* Update the previous frag to encompass this one */ - frp->fr_end = max; - - if (!drop) { - /* XXX Optimization opportunity - * This is a very heavy way to trim the payload. - * we could do it much faster by diddling mbuf - * internals but that would be even less legible - * than this mbuf magic. For my next trick, - * I'll pull a rabbit out of my laptop. - */ - *m0 = m_dup(m, M_NOWAIT); - if (*m0 == NULL) - goto no_mem; - /* From KAME Project : We have missed this! */ - m_adj(*m0, (h->ip_hl << 2) - - (*m0)->m_pkthdr.len); - - KASSERT(((*m0)->m_next == NULL), - ("(*m0)->m_next != NULL: %s", - __FUNCTION__)); - m_adj(m, precut + (h->ip_hl << 2)); - m_cat(*m0, m); - m = *m0; - if (m->m_flags & M_PKTHDR) { - int plen = 0; - struct mbuf *t; - for (t = m; t; t = t->m_next) - plen += t->m_len; - m->m_pkthdr.len = plen; - } - - - h = mtod(m, struct ip *); - - KASSERT(((int)m->m_len == - ntohs(h->ip_len) - precut), - ("m->m_len != ntohs(h->ip_len) - precut: %s", - __FUNCTION__)); - h->ip_off = htons(ntohs(h->ip_off) + - (precut >> 3)); - h->ip_len = htons(ntohs(h->ip_len) - precut); - } else { - hosed++; - } - } else { - /* There is a gap between fragments */ - - DPFPRINTF(("fragcache[%d]: gap %d (%d-%d) %d-%d\n", - h->ip_id, -precut, frp->fr_off, frp->fr_end, off, - max)); - - cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); - if (cur == NULL) - goto no_mem; - - cur->fr_off = off; - cur->fr_end = max; - LIST_INSERT_AFTER(frp, cur, fr_next); - } - } - - if (fra != NULL) { - int aftercut; - int merge = 0; - - aftercut = max - fra->fr_off; - if (aftercut == 0) { - /* Adjacent fragments */ - DPFPRINTF(("fragcache[%d]: adjacent %d-%d (%d-%d)\n", - h->ip_id, off, max, fra->fr_off, fra->fr_end)); - fra->fr_off = off; - merge = 1; - } else if (aftercut > 0) { - /* Need to chop off the tail of this fragment */ - DPFPRINTF(("fragcache[%d]: chop %d %d-%d (%d-%d)\n", - h->ip_id, aftercut, off, max, fra->fr_off, - fra->fr_end)); - fra->fr_off = off; - max -= aftercut; - - merge = 1; - - if (!drop) { - m_adj(m, -aftercut); - if (m->m_flags & M_PKTHDR) { - int plen = 0; - struct mbuf *t; - for (t = m; t; t = t->m_next) - plen += t->m_len; - m->m_pkthdr.len = plen; - } - h = mtod(m, struct ip *); - KASSERT(((int)m->m_len == ntohs(h->ip_len) - aftercut), - ("m->m_len != ntohs(h->ip_len) - aftercut: %s", - __FUNCTION__)); - h->ip_len = htons(ntohs(h->ip_len) - aftercut); - } else { - hosed++; - } - } else if (frp == NULL) { - /* There is a gap between fragments */ - DPFPRINTF(("fragcache[%d]: gap %d %d-%d (%d-%d)\n", - h->ip_id, -aftercut, off, max, fra->fr_off, - fra->fr_end)); - - cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); - if (cur == NULL) - goto no_mem; - - cur->fr_off = off; - cur->fr_end = max; - LIST_INSERT_BEFORE(fra, cur, fr_next); - } - - - /* Need to glue together two separate fragment descriptors */ - if (merge) { - if (cur && fra->fr_off <= cur->fr_end) { - /* Need to merge in a previous 'cur' */ - DPFPRINTF(("fragcache[%d]: adjacent(merge " - "%d-%d) %d-%d (%d-%d)\n", - h->ip_id, cur->fr_off, cur->fr_end, off, - max, fra->fr_off, fra->fr_end)); - fra->fr_off = cur->fr_off; - LIST_REMOVE(cur, fr_next); - uma_zfree(V_pf_frent_z, cur); - cur = NULL; - - } else if (frp && fra->fr_off <= frp->fr_end) { - /* Need to merge in a modified 'frp' */ - KASSERT((cur == NULL), ("cur != NULL: %s", - __FUNCTION__)); - DPFPRINTF(("fragcache[%d]: adjacent(merge " - "%d-%d) %d-%d (%d-%d)\n", - h->ip_id, frp->fr_off, frp->fr_end, off, - max, fra->fr_off, fra->fr_end)); - fra->fr_off = frp->fr_off; - LIST_REMOVE(frp, fr_next); - uma_zfree(V_pf_frent_z, frp); - frp = NULL; - - } - } - } - - if (hosed) { - /* - * We must keep tracking the overall fragment even when - * we're going to drop it anyway so that we know when to - * free the overall descriptor. Thus we drop the frag late. - */ - goto drop_fragment; - } - - - pass: - /* Update maximum data size */ - if ((*frag)->fr_max < max) - (*frag)->fr_max = max; - - /* This is the last segment */ - if (!mff) - (*frag)->fr_flags |= PFFRAG_SEENLAST; - - /* Check if we are completely reassembled */ - if (((*frag)->fr_flags & PFFRAG_SEENLAST) && - LIST_FIRST(&(*frag)->fr_queue)->fr_off == 0 && - LIST_FIRST(&(*frag)->fr_queue)->fr_end == (*frag)->fr_max) { - /* Remove from fragment queue */ - DPFPRINTF(("fragcache[%d]: done 0-%d\n", h->ip_id, - (*frag)->fr_max)); - pf_free_fragment(*frag); - *frag = NULL; - } - - return (m); - - no_mem: - *nomem = 1; - - /* Still need to pay attention to !IP_MF */ - if (!mff && *frag != NULL) - (*frag)->fr_flags |= PFFRAG_SEENLAST; - - m_freem(m); - return (NULL); - - drop_fragment: - - /* Still need to pay attention to !IP_MF */ - if (!mff && *frag != NULL) - (*frag)->fr_flags |= PFFRAG_SEENLAST; - - if (drop) { - /* This fragment has been deemed bad. Don't reass */ - if (((*frag)->fr_flags & PFFRAG_DROP) == 0) - DPFPRINTF(("fragcache[%d]: dropping overall fragment\n", - h->ip_id)); - (*frag)->fr_flags |= PFFRAG_DROP; - } - - m_freem(m); - return (NULL); -} - -int -pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, - struct pf_pdesc *pd) -{ - struct mbuf *m = *m0; - struct pf_rule *r; - struct pf_frent *frent; - struct pf_fragment *frag = NULL; - struct ip *h = mtod(m, struct ip *); - int mff = (ntohs(h->ip_off) & IP_MF); - int hlen = h->ip_hl << 2; - u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; - u_int16_t max; - int ip_len; - int ip_off; - int tag = -1; - - PF_RULES_RASSERT(); - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != dir) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != AF_INET) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != h->ip_p) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, - (struct pf_addr *)&h->ip_src.s_addr, AF_INET, - r->src.neg, kif, M_GETFIB(m))) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, - (struct pf_addr *)&h->ip_dst.s_addr, AF_INET, - r->dst.neg, NULL, M_GETFIB(m))) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (r->match_tag && !pf_match_tag(m, r, &tag, - pd->pf_mtag ? pd->pf_mtag->tag : 0)) - r = TAILQ_NEXT(r, entries); - else - break; - } - - if (r == NULL || r->action == PF_NOSCRUB) - return (PF_PASS); - else { - r->packets[dir == PF_OUT]++; - r->bytes[dir == PF_OUT] += pd->tot_len; - } - - /* Check for illegal packets */ - if (hlen < (int)sizeof(struct ip)) - goto drop; - - if (hlen > ntohs(h->ip_len)) - goto drop; - - /* Clear IP_DF if the rule uses the no-df option */ - if (r->rule_flag & PFRULE_NODF && h->ip_off & htons(IP_DF)) { - u_int16_t ip_off = h->ip_off; - - h->ip_off &= htons(~IP_DF); - h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); - } - - /* We will need other tests here */ - if (!fragoff && !mff) - goto no_fragment; - - /* We're dealing with a fragment now. Don't allow fragments - * with IP_DF to enter the cache. If the flag was cleared by - * no-df above, fine. Otherwise drop it. - */ - if (h->ip_off & htons(IP_DF)) { - DPFPRINTF(("IP_DF\n")); - goto bad; - } - - ip_len = ntohs(h->ip_len) - hlen; - ip_off = (ntohs(h->ip_off) & IP_OFFMASK) << 3; - - /* All fragments are 8 byte aligned */ - if (mff && (ip_len & 0x7)) { - DPFPRINTF(("mff and %d\n", ip_len)); - goto bad; - } - - /* Respect maximum length */ - if (fragoff + ip_len > IP_MAXPACKET) { - DPFPRINTF(("max packet %d\n", fragoff + ip_len)); - goto bad; - } - max = fragoff + ip_len; - - if ((r->rule_flag & (PFRULE_FRAGCROP|PFRULE_FRAGDROP)) == 0) { - - /* Fully buffer all of the fragments */ - PF_FRAG_LOCK(); - frag = pf_find_fragment(h, &V_pf_frag_tree); - - /* Check if we saw the last fragment already */ - if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && - max > frag->fr_max) - goto bad; - - /* Get an entry for the fragment queue */ - frent = uma_zalloc(V_pf_frent_z, M_NOWAIT); - if (frent == NULL) { - PF_FRAG_UNLOCK(); - REASON_SET(reason, PFRES_MEMORY); - return (PF_DROP); - } - frent->fr_ip = h; - frent->fr_m = m; - - /* Might return a completely reassembled mbuf, or NULL */ - DPFPRINTF(("reass frag %d @ %d-%d\n", h->ip_id, fragoff, max)); - *m0 = m = pf_reassemble(m0, &frag, frent, mff); - PF_FRAG_UNLOCK(); - - if (m == NULL) - return (PF_DROP); - - /* use mtag from concatenated mbuf chain */ - pd->pf_mtag = pf_find_mtag(m); -#ifdef DIAGNOSTIC - if (pd->pf_mtag == NULL) { - printf("%s: pf_find_mtag returned NULL(1)\n", __func__); - if ((pd->pf_mtag = pf_get_mtag(m)) == NULL) { - m_freem(m); - *m0 = NULL; - goto no_mem; - } - } -#endif - if (frag != NULL && (frag->fr_flags & PFFRAG_DROP)) - goto drop; - - h = mtod(m, struct ip *); - } else { - /* non-buffering fragment cache (drops or masks overlaps) */ - int nomem = 0; - - if (dir == PF_OUT && pd->pf_mtag->flags & PF_TAG_FRAGCACHE) { - /* - * Already passed the fragment cache in the - * input direction. If we continued, it would - * appear to be a dup and would be dropped. - */ - goto fragment_pass; - } - - PF_FRAG_LOCK(); - frag = pf_find_fragment(h, &V_pf_cache_tree); - - /* Check if we saw the last fragment already */ - if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && - max > frag->fr_max) { - if (r->rule_flag & PFRULE_FRAGDROP) - frag->fr_flags |= PFFRAG_DROP; - goto bad; - } - - *m0 = m = pf_fragcache(m0, h, &frag, mff, - (r->rule_flag & PFRULE_FRAGDROP) ? 1 : 0, &nomem); - PF_FRAG_UNLOCK(); - if (m == NULL) { - if (nomem) - goto no_mem; - goto drop; - } - - /* use mtag from copied and trimmed mbuf chain */ - pd->pf_mtag = pf_find_mtag(m); -#ifdef DIAGNOSTIC - if (pd->pf_mtag == NULL) { - printf("%s: pf_find_mtag returned NULL(2)\n", __func__); - if ((pd->pf_mtag = pf_get_mtag(m)) == NULL) { - m_freem(m); - *m0 = NULL; - goto no_mem; - } - } -#endif - if (dir == PF_IN) - pd->pf_mtag->flags |= PF_TAG_FRAGCACHE; - - if (frag != NULL && (frag->fr_flags & PFFRAG_DROP)) - goto drop; - goto fragment_pass; - } - - no_fragment: - /* At this point, only IP_DF is allowed in ip_off */ - if (h->ip_off & ~htons(IP_DF)) { - u_int16_t ip_off = h->ip_off; - - h->ip_off &= htons(IP_DF); - h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); - } - - /* not missing a return here */ - - fragment_pass: - pf_scrub_ip(&m, r->rule_flag, r->min_ttl, r->set_tos); - - if ((r->rule_flag & (PFRULE_FRAGCROP|PFRULE_FRAGDROP)) == 0) - pd->flags |= PFDESC_IP_REAS; - return (PF_PASS); - - no_mem: - REASON_SET(reason, PFRES_MEMORY); - if (r != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, - 1); - return (PF_DROP); - - drop: - REASON_SET(reason, PFRES_NORM); - if (r != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, - 1); - return (PF_DROP); - - bad: - DPFPRINTF(("dropping bad fragment\n")); - - /* Free associated fragments */ - if (frag != NULL) { - pf_free_fragment(frag); - PF_FRAG_UNLOCK(); - } - - REASON_SET(reason, PFRES_FRAG); - if (r != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, - 1); - - return (PF_DROP); -} -#endif - -#ifdef INET6 -int -pf_normalize_ip6(struct mbuf **m0, int dir, struct pfi_kif *kif, - u_short *reason, struct pf_pdesc *pd) -{ - struct mbuf *m = *m0; - struct pf_rule *r; - struct ip6_hdr *h = mtod(m, struct ip6_hdr *); - int off; - struct ip6_ext ext; - struct ip6_opt opt; - struct ip6_opt_jumbo jumbo; - struct ip6_frag frag; - u_int32_t jumbolen = 0, plen; - u_int16_t fragoff = 0; - int optend; - int ooff; - u_int8_t proto; - int terminal; - - PF_RULES_RASSERT(); - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != dir) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != AF_INET6) - r = r->skip[PF_SKIP_AF].ptr; -#if 0 /* header chain! */ - else if (r->proto && r->proto != h->ip6_nxt) - r = r->skip[PF_SKIP_PROTO].ptr; -#endif - else if (PF_MISMATCHAW(&r->src.addr, - (struct pf_addr *)&h->ip6_src, AF_INET6, - r->src.neg, kif, M_GETFIB(m))) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, - (struct pf_addr *)&h->ip6_dst, AF_INET6, - r->dst.neg, NULL, M_GETFIB(m))) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else - break; - } - - if (r == NULL || r->action == PF_NOSCRUB) - return (PF_PASS); - else { - r->packets[dir == PF_OUT]++; - r->bytes[dir == PF_OUT] += pd->tot_len; - } - - /* Check for illegal packets */ - if (sizeof(struct ip6_hdr) + IPV6_MAXPACKET < m->m_pkthdr.len) - goto drop; - - off = sizeof(struct ip6_hdr); - proto = h->ip6_nxt; - terminal = 0; - do { - switch (proto) { - case IPPROTO_FRAGMENT: - goto fragment; - break; - case IPPROTO_AH: - case IPPROTO_ROUTING: - case IPPROTO_DSTOPTS: - if (!pf_pull_hdr(m, off, &ext, sizeof(ext), NULL, - NULL, AF_INET6)) - goto shortpkt; - if (proto == IPPROTO_AH) - off += (ext.ip6e_len + 2) * 4; - else - off += (ext.ip6e_len + 1) * 8; - proto = ext.ip6e_nxt; - break; - case IPPROTO_HOPOPTS: - if (!pf_pull_hdr(m, off, &ext, sizeof(ext), NULL, - NULL, AF_INET6)) - goto shortpkt; - optend = off + (ext.ip6e_len + 1) * 8; - ooff = off + sizeof(ext); - do { - if (!pf_pull_hdr(m, ooff, &opt.ip6o_type, - sizeof(opt.ip6o_type), NULL, NULL, - AF_INET6)) - goto shortpkt; - if (opt.ip6o_type == IP6OPT_PAD1) { - ooff++; - continue; - } - if (!pf_pull_hdr(m, ooff, &opt, sizeof(opt), - NULL, NULL, AF_INET6)) - goto shortpkt; - if (ooff + sizeof(opt) + opt.ip6o_len > optend) - goto drop; - switch (opt.ip6o_type) { - case IP6OPT_JUMBO: - if (h->ip6_plen != 0) - goto drop; - if (!pf_pull_hdr(m, ooff, &jumbo, - sizeof(jumbo), NULL, NULL, - AF_INET6)) - goto shortpkt; - memcpy(&jumbolen, jumbo.ip6oj_jumbo_len, - sizeof(jumbolen)); - jumbolen = ntohl(jumbolen); - if (jumbolen <= IPV6_MAXPACKET) - goto drop; - if (sizeof(struct ip6_hdr) + jumbolen != - m->m_pkthdr.len) - goto drop; - break; - default: - break; - } - ooff += sizeof(opt) + opt.ip6o_len; - } while (ooff < optend); - - off = optend; - proto = ext.ip6e_nxt; - break; - default: - terminal = 1; - break; - } - } while (!terminal); - - /* jumbo payload option must be present, or plen > 0 */ - if (ntohs(h->ip6_plen) == 0) - plen = jumbolen; - else - plen = ntohs(h->ip6_plen); - if (plen == 0) - goto drop; - if (sizeof(struct ip6_hdr) + plen > m->m_pkthdr.len) - goto shortpkt; - - pf_scrub_ip6(&m, r->min_ttl); - - return (PF_PASS); - - fragment: - if (ntohs(h->ip6_plen) == 0 || jumbolen) - goto drop; - plen = ntohs(h->ip6_plen); - - if (!pf_pull_hdr(m, off, &frag, sizeof(frag), NULL, NULL, AF_INET6)) - goto shortpkt; - fragoff = ntohs(frag.ip6f_offlg & IP6F_OFF_MASK); - if (fragoff + (plen - off - sizeof(frag)) > IPV6_MAXPACKET) - goto badfrag; - - /* do something about it */ - /* remember to set pd->flags |= PFDESC_IP_REAS */ - return (PF_PASS); - - shortpkt: - REASON_SET(reason, PFRES_SHORT); - if (r != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, - 1); - return (PF_DROP); - - drop: - REASON_SET(reason, PFRES_NORM); - if (r != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, - 1); - return (PF_DROP); - - badfrag: - REASON_SET(reason, PFRES_FRAG); - if (r != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, - 1); - return (PF_DROP); -} -#endif /* INET6 */ - -int -pf_normalize_tcp(int dir, struct pfi_kif *kif, struct mbuf *m, int ipoff, - int off, void *h, struct pf_pdesc *pd) -{ - struct pf_rule *r, *rm = NULL; - struct tcphdr *th = pd->hdr.tcp; - int rewrite = 0; - u_short reason; - u_int8_t flags; - sa_family_t af = pd->af; - - PF_RULES_RASSERT(); - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != dir) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, - r->src.neg, kif, M_GETFIB(m))) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (r->src.port_op && !pf_match_port(r->src.port_op, - r->src.port[0], r->src.port[1], th->th_sport)) - r = r->skip[PF_SKIP_SRC_PORT].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, - r->dst.neg, NULL, M_GETFIB(m))) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (r->dst.port_op && !pf_match_port(r->dst.port_op, - r->dst.port[0], r->dst.port[1], th->th_dport)) - r = r->skip[PF_SKIP_DST_PORT].ptr; - else if (r->os_fingerprint != PF_OSFP_ANY && !pf_osfp_match( - pf_osfp_fingerprint(pd, m, off, th), - r->os_fingerprint)) - r = TAILQ_NEXT(r, entries); - else { - rm = r; - break; - } - } - - if (rm == NULL || rm->action == PF_NOSCRUB) - return (PF_PASS); - else { - r->packets[dir == PF_OUT]++; - r->bytes[dir == PF_OUT] += pd->tot_len; - } - - if (rm->rule_flag & PFRULE_REASSEMBLE_TCP) - pd->flags |= PFDESC_TCP_NORM; - - flags = th->th_flags; - if (flags & TH_SYN) { - /* Illegal packet */ - if (flags & TH_RST) - goto tcp_drop; - - if (flags & TH_FIN) - flags &= ~TH_FIN; - } else { - /* Illegal packet */ - if (!(flags & (TH_ACK|TH_RST))) - goto tcp_drop; - } - - if (!(flags & TH_ACK)) { - /* These flags are only valid if ACK is set */ - if ((flags & TH_FIN) || (flags & TH_PUSH) || (flags & TH_URG)) - goto tcp_drop; - } - - /* Check for illegal header length */ - if (th->th_off < (sizeof(struct tcphdr) >> 2)) - goto tcp_drop; - - /* If flags changed, or reserved data set, then adjust */ - if (flags != th->th_flags || th->th_x2 != 0) { - u_int16_t ov, nv; - - ov = *(u_int16_t *)(&th->th_ack + 1); - th->th_flags = flags; - th->th_x2 = 0; - nv = *(u_int16_t *)(&th->th_ack + 1); - - th->th_sum = pf_cksum_fixup(th->th_sum, ov, nv, 0); - rewrite = 1; - } - - /* Remove urgent pointer, if TH_URG is not set */ - if (!(flags & TH_URG) && th->th_urp) { - th->th_sum = pf_cksum_fixup(th->th_sum, th->th_urp, 0, 0); - th->th_urp = 0; - rewrite = 1; - } - - /* Process options */ - if (r->max_mss && pf_normalize_tcpopt(r, m, th, off, pd->af)) - rewrite = 1; - - /* copy back packet headers if we sanitized */ - if (rewrite) - m_copyback(m, off, sizeof(*th), (caddr_t)th); - - return (PF_PASS); - - tcp_drop: - REASON_SET(&reason, PFRES_NORM); - if (rm != NULL && r->log) - PFLOG_PACKET(kif, m, AF_INET, dir, reason, r, NULL, NULL, pd, - 1); - return (PF_DROP); -} - -int -pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, - struct tcphdr *th, struct pf_state_peer *src, struct pf_state_peer *dst) -{ - u_int32_t tsval, tsecr; - u_int8_t hdr[60]; - u_int8_t *opt; - - KASSERT((src->scrub == NULL), - ("pf_normalize_tcp_init: src->scrub != NULL")); - - src->scrub = uma_zalloc(V_pf_state_scrub_z, M_ZERO | M_NOWAIT); - if (src->scrub == NULL) - return (1); - - switch (pd->af) { -#ifdef INET - case AF_INET: { - struct ip *h = mtod(m, struct ip *); - src->scrub->pfss_ttl = h->ip_ttl; - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: { - struct ip6_hdr *h = mtod(m, struct ip6_hdr *); - src->scrub->pfss_ttl = h->ip6_hlim; - break; - } -#endif /* INET6 */ - } - - - /* - * All normalizations below are only begun if we see the start of - * the connections. They must all set an enabled bit in pfss_flags - */ - if ((th->th_flags & TH_SYN) == 0) - return (0); - - - if (th->th_off > (sizeof(struct tcphdr) >> 2) && src->scrub && - pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { - /* Diddle with TCP options */ - int hlen; - opt = hdr + sizeof(struct tcphdr); - hlen = (th->th_off << 2) - sizeof(struct tcphdr); - while (hlen >= TCPOLEN_TIMESTAMP) { - switch (*opt) { - case TCPOPT_EOL: /* FALLTHROUGH */ - case TCPOPT_NOP: - opt++; - hlen--; - break; - case TCPOPT_TIMESTAMP: - if (opt[1] >= TCPOLEN_TIMESTAMP) { - src->scrub->pfss_flags |= - PFSS_TIMESTAMP; - src->scrub->pfss_ts_mod = - htonl(arc4random()); - - /* note PFSS_PAWS not set yet */ - memcpy(&tsval, &opt[2], - sizeof(u_int32_t)); - memcpy(&tsecr, &opt[6], - sizeof(u_int32_t)); - src->scrub->pfss_tsval0 = ntohl(tsval); - src->scrub->pfss_tsval = ntohl(tsval); - src->scrub->pfss_tsecr = ntohl(tsecr); - getmicrouptime(&src->scrub->pfss_last); - } - /* FALLTHROUGH */ - default: - hlen -= MAX(opt[1], 2); - opt += MAX(opt[1], 2); - break; - } - } - } - - return (0); -} - -void -pf_normalize_tcp_cleanup(struct pf_state *state) -{ - if (state->src.scrub) - uma_zfree(V_pf_state_scrub_z, state->src.scrub); - if (state->dst.scrub) - uma_zfree(V_pf_state_scrub_z, state->dst.scrub); - - /* Someday... flush the TCP segment reassembly descriptors. */ -} - -int -pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, - u_short *reason, struct tcphdr *th, struct pf_state *state, - struct pf_state_peer *src, struct pf_state_peer *dst, int *writeback) -{ - struct timeval uptime; - u_int32_t tsval, tsecr; - u_int tsval_from_last; - u_int8_t hdr[60]; - u_int8_t *opt; - int copyback = 0; - int got_ts = 0; - - KASSERT((src->scrub || dst->scrub), - ("%s: src->scrub && dst->scrub!", __func__)); - - /* - * Enforce the minimum TTL seen for this connection. Negate a common - * technique to evade an intrusion detection system and confuse - * firewall state code. - */ - switch (pd->af) { -#ifdef INET - case AF_INET: { - if (src->scrub) { - struct ip *h = mtod(m, struct ip *); - if (h->ip_ttl > src->scrub->pfss_ttl) - src->scrub->pfss_ttl = h->ip_ttl; - h->ip_ttl = src->scrub->pfss_ttl; - } - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: { - if (src->scrub) { - struct ip6_hdr *h = mtod(m, struct ip6_hdr *); - if (h->ip6_hlim > src->scrub->pfss_ttl) - src->scrub->pfss_ttl = h->ip6_hlim; - h->ip6_hlim = src->scrub->pfss_ttl; - } - break; - } -#endif /* INET6 */ - } - - if (th->th_off > (sizeof(struct tcphdr) >> 2) && - ((src->scrub && (src->scrub->pfss_flags & PFSS_TIMESTAMP)) || - (dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP))) && - pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { - /* Diddle with TCP options */ - int hlen; - opt = hdr + sizeof(struct tcphdr); - hlen = (th->th_off << 2) - sizeof(struct tcphdr); - while (hlen >= TCPOLEN_TIMESTAMP) { - switch (*opt) { - case TCPOPT_EOL: /* FALLTHROUGH */ - case TCPOPT_NOP: - opt++; - hlen--; - break; - case TCPOPT_TIMESTAMP: - /* Modulate the timestamps. Can be used for - * NAT detection, OS uptime determination or - * reboot detection. - */ - - if (got_ts) { - /* Huh? Multiple timestamps!? */ - if (V_pf_status.debug >= PF_DEBUG_MISC) { - DPFPRINTF(("multiple TS??")); - pf_print_state(state); - printf("\n"); - } - REASON_SET(reason, PFRES_TS); - return (PF_DROP); - } - if (opt[1] >= TCPOLEN_TIMESTAMP) { - memcpy(&tsval, &opt[2], - sizeof(u_int32_t)); - if (tsval && src->scrub && - (src->scrub->pfss_flags & - PFSS_TIMESTAMP)) { - tsval = ntohl(tsval); - pf_change_a(&opt[2], - &th->th_sum, - htonl(tsval + - src->scrub->pfss_ts_mod), - 0); - copyback = 1; - } - - /* Modulate TS reply iff valid (!0) */ - memcpy(&tsecr, &opt[6], - sizeof(u_int32_t)); - if (tsecr && dst->scrub && - (dst->scrub->pfss_flags & - PFSS_TIMESTAMP)) { - tsecr = ntohl(tsecr) - - dst->scrub->pfss_ts_mod; - pf_change_a(&opt[6], - &th->th_sum, htonl(tsecr), - 0); - copyback = 1; - } - got_ts = 1; - } - /* FALLTHROUGH */ - default: - hlen -= MAX(opt[1], 2); - opt += MAX(opt[1], 2); - break; - } - } - if (copyback) { - /* Copyback the options, caller copys back header */ - *writeback = 1; - m_copyback(m, off + sizeof(struct tcphdr), - (th->th_off << 2) - sizeof(struct tcphdr), hdr + - sizeof(struct tcphdr)); - } - } - - - /* - * Must invalidate PAWS checks on connections idle for too long. - * The fastest allowed timestamp clock is 1ms. That turns out to - * be about 24 days before it wraps. XXX Right now our lowerbound - * TS echo check only works for the first 12 days of a connection - * when the TS has exhausted half its 32bit space - */ -#define TS_MAX_IDLE (24*24*60*60) -#define TS_MAX_CONN (12*24*60*60) /* XXX remove when better tsecr check */ - - getmicrouptime(&uptime); - if (src->scrub && (src->scrub->pfss_flags & PFSS_PAWS) && - (uptime.tv_sec - src->scrub->pfss_last.tv_sec > TS_MAX_IDLE || - time_uptime - state->creation > TS_MAX_CONN)) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - DPFPRINTF(("src idled out of PAWS\n")); - pf_print_state(state); - printf("\n"); - } - src->scrub->pfss_flags = (src->scrub->pfss_flags & ~PFSS_PAWS) - | PFSS_PAWS_IDLED; - } - if (dst->scrub && (dst->scrub->pfss_flags & PFSS_PAWS) && - uptime.tv_sec - dst->scrub->pfss_last.tv_sec > TS_MAX_IDLE) { - if (V_pf_status.debug >= PF_DEBUG_MISC) { - DPFPRINTF(("dst idled out of PAWS\n")); - pf_print_state(state); - printf("\n"); - } - dst->scrub->pfss_flags = (dst->scrub->pfss_flags & ~PFSS_PAWS) - | PFSS_PAWS_IDLED; - } - - if (got_ts && src->scrub && dst->scrub && - (src->scrub->pfss_flags & PFSS_PAWS) && - (dst->scrub->pfss_flags & PFSS_PAWS)) { - /* Validate that the timestamps are "in-window". - * RFC1323 describes TCP Timestamp options that allow - * measurement of RTT (round trip time) and PAWS - * (protection against wrapped sequence numbers). PAWS - * gives us a set of rules for rejecting packets on - * long fat pipes (packets that were somehow delayed - * in transit longer than the time it took to send the - * full TCP sequence space of 4Gb). We can use these - * rules and infer a few others that will let us treat - * the 32bit timestamp and the 32bit echoed timestamp - * as sequence numbers to prevent a blind attacker from - * inserting packets into a connection. - * - * RFC1323 tells us: - * - The timestamp on this packet must be greater than - * or equal to the last value echoed by the other - * endpoint. The RFC says those will be discarded - * since it is a dup that has already been acked. - * This gives us a lowerbound on the timestamp. - * timestamp >= other last echoed timestamp - * - The timestamp will be less than or equal to - * the last timestamp plus the time between the - * last packet and now. The RFC defines the max - * clock rate as 1ms. We will allow clocks to be - * up to 10% fast and will allow a total difference - * or 30 seconds due to a route change. And this - * gives us an upperbound on the timestamp. - * timestamp <= last timestamp + max ticks - * We have to be careful here. Windows will send an - * initial timestamp of zero and then initialize it - * to a random value after the 3whs; presumably to - * avoid a DoS by having to call an expensive RNG - * during a SYN flood. Proof MS has at least one - * good security geek. - * - * - The TCP timestamp option must also echo the other - * endpoints timestamp. The timestamp echoed is the - * one carried on the earliest unacknowledged segment - * on the left edge of the sequence window. The RFC - * states that the host will reject any echoed - * timestamps that were larger than any ever sent. - * This gives us an upperbound on the TS echo. - * tescr <= largest_tsval - * - The lowerbound on the TS echo is a little more - * tricky to determine. The other endpoint's echoed - * values will not decrease. But there may be - * network conditions that re-order packets and - * cause our view of them to decrease. For now the - * only lowerbound we can safely determine is that - * the TS echo will never be less than the original - * TS. XXX There is probably a better lowerbound. - * Remove TS_MAX_CONN with better lowerbound check. - * tescr >= other original TS - * - * It is also important to note that the fastest - * timestamp clock of 1ms will wrap its 32bit space in - * 24 days. So we just disable TS checking after 24 - * days of idle time. We actually must use a 12d - * connection limit until we can come up with a better - * lowerbound to the TS echo check. - */ - struct timeval delta_ts; - int ts_fudge; - - - /* - * PFTM_TS_DIFF is how many seconds of leeway to allow - * a host's timestamp. This can happen if the previous - * packet got delayed in transit for much longer than - * this packet. - */ - if ((ts_fudge = state->rule.ptr->timeout[PFTM_TS_DIFF]) == 0) - ts_fudge = V_pf_default_rule.timeout[PFTM_TS_DIFF]; - - /* Calculate max ticks since the last timestamp */ -#define TS_MAXFREQ 1100 /* RFC max TS freq of 1Khz + 10% skew */ -#define TS_MICROSECS 1000000 /* microseconds per second */ - delta_ts = uptime; - timevalsub(&delta_ts, &src->scrub->pfss_last); - tsval_from_last = (delta_ts.tv_sec + ts_fudge) * TS_MAXFREQ; - tsval_from_last += delta_ts.tv_usec / (TS_MICROSECS/TS_MAXFREQ); - - if ((src->state >= TCPS_ESTABLISHED && - dst->state >= TCPS_ESTABLISHED) && - (SEQ_LT(tsval, dst->scrub->pfss_tsecr) || - SEQ_GT(tsval, src->scrub->pfss_tsval + tsval_from_last) || - (tsecr && (SEQ_GT(tsecr, dst->scrub->pfss_tsval) || - SEQ_LT(tsecr, dst->scrub->pfss_tsval0))))) { - /* Bad RFC1323 implementation or an insertion attack. - * - * - Solaris 2.6 and 2.7 are known to send another ACK - * after the FIN,FIN|ACK,ACK closing that carries - * an old timestamp. - */ - - DPFPRINTF(("Timestamp failed %c%c%c%c\n", - SEQ_LT(tsval, dst->scrub->pfss_tsecr) ? '0' : ' ', - SEQ_GT(tsval, src->scrub->pfss_tsval + - tsval_from_last) ? '1' : ' ', - SEQ_GT(tsecr, dst->scrub->pfss_tsval) ? '2' : ' ', - SEQ_LT(tsecr, dst->scrub->pfss_tsval0)? '3' : ' ')); - DPFPRINTF((" tsval: %u tsecr: %u +ticks: %u " - "idle: %jus %lums\n", - tsval, tsecr, tsval_from_last, - (uintmax_t)delta_ts.tv_sec, - delta_ts.tv_usec / 1000)); - DPFPRINTF((" src->tsval: %u tsecr: %u\n", - src->scrub->pfss_tsval, src->scrub->pfss_tsecr)); - DPFPRINTF((" dst->tsval: %u tsecr: %u tsval0: %u" - "\n", dst->scrub->pfss_tsval, - dst->scrub->pfss_tsecr, dst->scrub->pfss_tsval0)); - if (V_pf_status.debug >= PF_DEBUG_MISC) { - pf_print_state(state); - pf_print_flags(th->th_flags); - printf("\n"); - } - REASON_SET(reason, PFRES_TS); - return (PF_DROP); - } - - /* XXX I'd really like to require tsecr but it's optional */ - - } else if (!got_ts && (th->th_flags & TH_RST) == 0 && - ((src->state == TCPS_ESTABLISHED && dst->state == TCPS_ESTABLISHED) - || pd->p_len > 0 || (th->th_flags & TH_SYN)) && - src->scrub && dst->scrub && - (src->scrub->pfss_flags & PFSS_PAWS) && - (dst->scrub->pfss_flags & PFSS_PAWS)) { - /* Didn't send a timestamp. Timestamps aren't really useful - * when: - * - connection opening or closing (often not even sent). - * but we must not let an attacker to put a FIN on a - * data packet to sneak it through our ESTABLISHED check. - * - on a TCP reset. RFC suggests not even looking at TS. - * - on an empty ACK. The TS will not be echoed so it will - * probably not help keep the RTT calculation in sync and - * there isn't as much danger when the sequence numbers - * got wrapped. So some stacks don't include TS on empty - * ACKs :-( - * - * To minimize the disruption to mostly RFC1323 conformant - * stacks, we will only require timestamps on data packets. - * - * And what do ya know, we cannot require timestamps on data - * packets. There appear to be devices that do legitimate - * TCP connection hijacking. There are HTTP devices that allow - * a 3whs (with timestamps) and then buffer the HTTP request. - * If the intermediate device has the HTTP response cache, it - * will spoof the response but not bother timestamping its - * packets. So we can look for the presence of a timestamp in - * the first data packet and if there, require it in all future - * packets. - */ - - if (pd->p_len > 0 && (src->scrub->pfss_flags & PFSS_DATA_TS)) { - /* - * Hey! Someone tried to sneak a packet in. Or the - * stack changed its RFC1323 behavior?!?! - */ - if (V_pf_status.debug >= PF_DEBUG_MISC) { - DPFPRINTF(("Did not receive expected RFC1323 " - "timestamp\n")); - pf_print_state(state); - pf_print_flags(th->th_flags); - printf("\n"); - } - REASON_SET(reason, PFRES_TS); - return (PF_DROP); - } - } - - - /* - * We will note if a host sends his data packets with or without - * timestamps. And require all data packets to contain a timestamp - * if the first does. PAWS implicitly requires that all data packets be - * timestamped. But I think there are middle-man devices that hijack - * TCP streams immediately after the 3whs and don't timestamp their - * packets (seen in a WWW accelerator or cache). - */ - if (pd->p_len > 0 && src->scrub && (src->scrub->pfss_flags & - (PFSS_TIMESTAMP|PFSS_DATA_TS|PFSS_DATA_NOTS)) == PFSS_TIMESTAMP) { - if (got_ts) - src->scrub->pfss_flags |= PFSS_DATA_TS; - else { - src->scrub->pfss_flags |= PFSS_DATA_NOTS; - if (V_pf_status.debug >= PF_DEBUG_MISC && dst->scrub && - (dst->scrub->pfss_flags & PFSS_TIMESTAMP)) { - /* Don't warn if other host rejected RFC1323 */ - DPFPRINTF(("Broken RFC1323 stack did not " - "timestamp data packet. Disabled PAWS " - "security.\n")); - pf_print_state(state); - pf_print_flags(th->th_flags); - printf("\n"); - } - } - } - - - /* - * Update PAWS values - */ - if (got_ts && src->scrub && PFSS_TIMESTAMP == (src->scrub->pfss_flags & - (PFSS_PAWS_IDLED|PFSS_TIMESTAMP))) { - getmicrouptime(&src->scrub->pfss_last); - if (SEQ_GEQ(tsval, src->scrub->pfss_tsval) || - (src->scrub->pfss_flags & PFSS_PAWS) == 0) - src->scrub->pfss_tsval = tsval; - - if (tsecr) { - if (SEQ_GEQ(tsecr, src->scrub->pfss_tsecr) || - (src->scrub->pfss_flags & PFSS_PAWS) == 0) - src->scrub->pfss_tsecr = tsecr; - - if ((src->scrub->pfss_flags & PFSS_PAWS) == 0 && - (SEQ_LT(tsval, src->scrub->pfss_tsval0) || - src->scrub->pfss_tsval0 == 0)) { - /* tsval0 MUST be the lowest timestamp */ - src->scrub->pfss_tsval0 = tsval; - } - - /* Only fully initialized after a TS gets echoed */ - if ((src->scrub->pfss_flags & PFSS_PAWS) == 0) - src->scrub->pfss_flags |= PFSS_PAWS; - } - } - - /* I have a dream.... TCP segment reassembly.... */ - return (0); -} - -static int -pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, - int off, sa_family_t af) -{ - u_int16_t *mss; - int thoff; - int opt, cnt, optlen = 0; - int rewrite = 0; - u_char opts[TCP_MAXOLEN]; - u_char *optp = opts; - - thoff = th->th_off << 2; - cnt = thoff - sizeof(struct tcphdr); - - if (cnt > 0 && !pf_pull_hdr(m, off + sizeof(*th), opts, cnt, - NULL, NULL, af)) - return (rewrite); - - for (; cnt > 0; cnt -= optlen, optp += optlen) { - opt = optp[0]; - if (opt == TCPOPT_EOL) - break; - if (opt == TCPOPT_NOP) - optlen = 1; - else { - if (cnt < 2) - break; - optlen = optp[1]; - if (optlen < 2 || optlen > cnt) - break; - } - switch (opt) { - case TCPOPT_MAXSEG: - mss = (u_int16_t *)(optp + 2); - if ((ntohs(*mss)) > r->max_mss) { - th->th_sum = pf_cksum_fixup(th->th_sum, - *mss, htons(r->max_mss), 0); - *mss = htons(r->max_mss); - rewrite = 1; - } - break; - default: - break; - } - } - - if (rewrite) - m_copyback(m, off + sizeof(*th), thoff - sizeof(*th), opts); - - return (rewrite); -} - -#ifdef INET -static void -pf_scrub_ip(struct mbuf **m0, u_int32_t flags, u_int8_t min_ttl, u_int8_t tos) -{ - struct mbuf *m = *m0; - struct ip *h = mtod(m, struct ip *); - - /* Clear IP_DF if no-df was requested */ - if (flags & PFRULE_NODF && h->ip_off & htons(IP_DF)) { - u_int16_t ip_off = h->ip_off; - - h->ip_off &= htons(~IP_DF); - h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); - } - - /* Enforce a minimum ttl, may cause endless packet loops */ - if (min_ttl && h->ip_ttl < min_ttl) { - u_int16_t ip_ttl = h->ip_ttl; - - h->ip_ttl = min_ttl; - h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_ttl, h->ip_ttl, 0); - } - - /* Enforce tos */ - if (flags & PFRULE_SET_TOS) { - u_int16_t ov, nv; - - ov = *(u_int16_t *)h; - h->ip_tos = tos; - nv = *(u_int16_t *)h; - - h->ip_sum = pf_cksum_fixup(h->ip_sum, ov, nv, 0); - } - - /* random-id, but not for fragments */ - if (flags & PFRULE_RANDOMID && !(h->ip_off & ~htons(IP_DF))) { - u_int16_t ip_id = h->ip_id; - - h->ip_id = ip_randomid(); - h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_id, h->ip_id, 0); - } -} -#endif /* INET */ - -#ifdef INET6 -static void -pf_scrub_ip6(struct mbuf **m0, u_int8_t min_ttl) -{ - struct mbuf *m = *m0; - struct ip6_hdr *h = mtod(m, struct ip6_hdr *); - - /* Enforce a minimum ttl, may cause endless packet loops */ - if (min_ttl && h->ip6_hlim < min_ttl) - h->ip6_hlim = min_ttl; -} -#endif diff --git a/sys/contrib/pf/net/pf_osfp.c b/sys/contrib/pf/net/pf_osfp.c deleted file mode 100644 index 29d4a4030b51b..0000000000000 --- a/sys/contrib/pf/net/pf_osfp.c +++ /dev/null @@ -1,526 +0,0 @@ -/* $OpenBSD: pf_osfp.c,v 1.14 2008/06/12 18:17:01 henning Exp $ */ - -/* - * Copyright (c) 2003 Mike Frantzen - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -static MALLOC_DEFINE(M_PFOSFP, "pf_osfp", "pf(4) operating system fingerprints"); -#define DPFPRINTF(format, x...) \ - if (V_pf_status.debug >= PF_DEBUG_NOISY) \ - printf(format , ##x) - -SLIST_HEAD(pf_osfp_list, pf_os_fingerprint); -static VNET_DEFINE(struct pf_osfp_list, pf_osfp_list) = - SLIST_HEAD_INITIALIZER(); -#define V_pf_osfp_list VNET(pf_osfp_list) - -static struct pf_osfp_enlist *pf_osfp_fingerprint_hdr(const struct ip *, - const struct ip6_hdr *, - const struct tcphdr *); -static struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *, - struct pf_os_fingerprint *, u_int8_t); -static struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *, - struct pf_os_fingerprint *); -static void pf_osfp_insert(struct pf_osfp_list *, - struct pf_os_fingerprint *); -#ifdef PFDEBUG -static struct pf_os_fingerprint *pf_osfp_validate(void); -#endif - -/* - * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only) - * Returns the list of possible OSes. - */ -struct pf_osfp_enlist * -pf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off, - const struct tcphdr *tcp) -{ - struct ip *ip; - struct ip6_hdr *ip6; - char hdr[60]; - - if ((pd->af != PF_INET && pd->af != PF_INET6) || - pd->proto != IPPROTO_TCP || (tcp->th_off << 2) < sizeof(*tcp)) - return (NULL); - - if (pd->af == PF_INET) { - ip = mtod(m, struct ip *); - ip6 = (struct ip6_hdr *)NULL; - } else { - ip = (struct ip *)NULL; - ip6 = mtod(m, struct ip6_hdr *); - } - if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL, - pd->af)) return (NULL); - - return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr)); -} - -static struct pf_osfp_enlist * -pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcp) -{ - struct pf_os_fingerprint fp, *fpresult; - int cnt, optlen = 0; - const u_int8_t *optp; - char srcname[128]; - - if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN) - return (NULL); - if (ip) { - if ((ip->ip_off & htons(IP_OFFMASK)) != 0) - return (NULL); - } - - memset(&fp, 0, sizeof(fp)); - - if (ip) { - fp.fp_psize = ntohs(ip->ip_len); - fp.fp_ttl = ip->ip_ttl; - if (ip->ip_off & htons(IP_DF)) - fp.fp_flags |= PF_OSFP_DF; - strlcpy(srcname, inet_ntoa(ip->ip_src), sizeof(srcname)); - } -#ifdef INET6 - else if (ip6) { - /* jumbo payload? */ - fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); - fp.fp_ttl = ip6->ip6_hlim; - fp.fp_flags |= PF_OSFP_DF; - fp.fp_flags |= PF_OSFP_INET6; - strlcpy(srcname, ip6_sprintf((struct in6_addr *)&ip6->ip6_src), - sizeof(srcname)); - } -#endif - else - return (NULL); - fp.fp_wsize = ntohs(tcp->th_win); - - - cnt = (tcp->th_off << 2) - sizeof(*tcp); - optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp)); - for (; cnt > 0; cnt -= optlen, optp += optlen) { - if (*optp == TCPOPT_EOL) - break; - - fp.fp_optcnt++; - if (*optp == TCPOPT_NOP) { - fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) | - PF_OSFP_TCPOPT_NOP; - optlen = 1; - } else { - if (cnt < 2) - return (NULL); - optlen = optp[1]; - if (optlen > cnt || optlen < 2) - return (NULL); - switch (*optp) { - case TCPOPT_MAXSEG: - if (optlen >= TCPOLEN_MAXSEG) - memcpy(&fp.fp_mss, &optp[2], - sizeof(fp.fp_mss)); - fp.fp_tcpopts = (fp.fp_tcpopts << - PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS; - NTOHS(fp.fp_mss); - break; - case TCPOPT_WINDOW: - if (optlen >= TCPOLEN_WINDOW) - memcpy(&fp.fp_wscale, &optp[2], - sizeof(fp.fp_wscale)); - NTOHS(fp.fp_wscale); - fp.fp_tcpopts = (fp.fp_tcpopts << - PF_OSFP_TCPOPT_BITS) | - PF_OSFP_TCPOPT_WSCALE; - break; - case TCPOPT_SACK_PERMITTED: - fp.fp_tcpopts = (fp.fp_tcpopts << - PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK; - break; - case TCPOPT_TIMESTAMP: - if (optlen >= TCPOLEN_TIMESTAMP) { - u_int32_t ts; - memcpy(&ts, &optp[2], sizeof(ts)); - if (ts == 0) - fp.fp_flags |= PF_OSFP_TS0; - - } - fp.fp_tcpopts = (fp.fp_tcpopts << - PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS; - break; - default: - return (NULL); - } - } - optlen = MAX(optlen, 1); /* paranoia */ - } - - DPFPRINTF("fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) " - "(TS=%s,M=%s%d,W=%s%d)\n", - srcname, ntohs(tcp->th_sport), - fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0, - fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt, - (fp.fp_flags & PF_OSFP_TS0) ? "0" : "", - (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" : - (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "", - fp.fp_mss, - (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" : - (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", - fp.fp_wscale); - - if ((fpresult = pf_osfp_find(&V_pf_osfp_list, &fp, - PF_OSFP_MAXTTL_OFFSET))) - return (&fpresult->fp_oses); - return (NULL); -} - -/* Match a fingerprint ID against a list of OSes */ -int -pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os) -{ - struct pf_osfp_entry *entry; - int os_class, os_version, os_subtype; - int en_class, en_version, en_subtype; - - if (os == PF_OSFP_ANY) - return (1); - if (list == NULL) { - DPFPRINTF("osfp no match against %x\n", os); - return (os == PF_OSFP_UNKNOWN); - } - PF_OSFP_UNPACK(os, os_class, os_version, os_subtype); - SLIST_FOREACH(entry, list, fp_entry) { - PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype); - if ((os_class == PF_OSFP_ANY || en_class == os_class) && - (os_version == PF_OSFP_ANY || en_version == os_version) && - (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) { - DPFPRINTF("osfp matched %s %s %s %x==%x\n", - entry->fp_class_nm, entry->fp_version_nm, - entry->fp_subtype_nm, os, entry->fp_os); - return (1); - } - } - DPFPRINTF("fingerprint 0x%x didn't match\n", os); - return (0); -} - -/* Flush the fingerprint list */ -void -pf_osfp_flush(void) -{ - struct pf_os_fingerprint *fp; - struct pf_osfp_entry *entry; - - while ((fp = SLIST_FIRST(&V_pf_osfp_list))) { - SLIST_REMOVE_HEAD(&V_pf_osfp_list, fp_next); - while ((entry = SLIST_FIRST(&fp->fp_oses))) { - SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry); - free(entry, M_PFOSFP); - } - free(fp, M_PFOSFP); - } -} - - -/* Add a fingerprint */ -int -pf_osfp_add(struct pf_osfp_ioctl *fpioc) -{ - struct pf_os_fingerprint *fp, fpadd; - struct pf_osfp_entry *entry; - - PF_RULES_WASSERT(); - - memset(&fpadd, 0, sizeof(fpadd)); - fpadd.fp_tcpopts = fpioc->fp_tcpopts; - fpadd.fp_wsize = fpioc->fp_wsize; - fpadd.fp_psize = fpioc->fp_psize; - fpadd.fp_mss = fpioc->fp_mss; - fpadd.fp_flags = fpioc->fp_flags; - fpadd.fp_optcnt = fpioc->fp_optcnt; - fpadd.fp_wscale = fpioc->fp_wscale; - fpadd.fp_ttl = fpioc->fp_ttl; - -#if 0 /* XXX RYAN wants to fix logging */ - DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d " - "(TS=%s,M=%s%d,W=%s%d) %x\n", - fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm, - fpioc->fp_os.fp_subtype_nm, - (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" : - (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" : - (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" : - (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "", - fpadd.fp_wsize, - fpadd.fp_ttl, - (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0, - (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" : - (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "", - fpadd.fp_psize, - (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt, - (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "", - (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" : - (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "", - fpadd.fp_mss, - (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" : - (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", - fpadd.fp_wscale, - fpioc->fp_os.fp_os); -#endif - - if ((fp = pf_osfp_find_exact(&V_pf_osfp_list, &fpadd))) { - SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { - if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os)) - return (EEXIST); - } - if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT)) - == NULL) - return (ENOMEM); - } else { - if ((fp = malloc(sizeof(*fp), M_PFOSFP, M_ZERO | M_NOWAIT)) - == NULL) - return (ENOMEM); - fp->fp_tcpopts = fpioc->fp_tcpopts; - fp->fp_wsize = fpioc->fp_wsize; - fp->fp_psize = fpioc->fp_psize; - fp->fp_mss = fpioc->fp_mss; - fp->fp_flags = fpioc->fp_flags; - fp->fp_optcnt = fpioc->fp_optcnt; - fp->fp_wscale = fpioc->fp_wscale; - fp->fp_ttl = fpioc->fp_ttl; - SLIST_INIT(&fp->fp_oses); - if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT)) - == NULL) { - free(fp, M_PFOSFP); - return (ENOMEM); - } - pf_osfp_insert(&V_pf_osfp_list, fp); - } - memcpy(entry, &fpioc->fp_os, sizeof(*entry)); - - /* Make sure the strings are NUL terminated */ - entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0'; - entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0'; - entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0'; - - SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry); - -#ifdef PFDEBUG - if ((fp = pf_osfp_validate())) - printf("Invalid fingerprint list\n"); -#endif /* PFDEBUG */ - return (0); -} - - -/* Find a fingerprint in the list */ -static struct pf_os_fingerprint * -pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find, - u_int8_t ttldiff) -{ - struct pf_os_fingerprint *f; - -#define MATCH_INT(_MOD, _DC, _field) \ - if ((f->fp_flags & _DC) == 0) { \ - if ((f->fp_flags & _MOD) == 0) { \ - if (f->_field != find->_field) \ - continue; \ - } else { \ - if (f->_field == 0 || find->_field % f->_field) \ - continue; \ - } \ - } - - SLIST_FOREACH(f, list, fp_next) { - if (f->fp_tcpopts != find->fp_tcpopts || - f->fp_optcnt != find->fp_optcnt || - f->fp_ttl < find->fp_ttl || - f->fp_ttl - find->fp_ttl > ttldiff || - (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) != - (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0))) - continue; - - MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize) - MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss) - MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale) - if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) { - if (f->fp_flags & PF_OSFP_WSIZE_MSS) { - if (find->fp_mss == 0) - continue; - -/* - * Some "smart" NAT devices and DSL routers will tweak the MSS size and - * will set it to whatever is suitable for the link type. - */ -#define SMART_MSS 1460 - if ((find->fp_wsize % find->fp_mss || - find->fp_wsize / find->fp_mss != - f->fp_wsize) && - (find->fp_wsize % SMART_MSS || - find->fp_wsize / SMART_MSS != - f->fp_wsize)) - continue; - } else if (f->fp_flags & PF_OSFP_WSIZE_MTU) { - if (find->fp_mss == 0) - continue; - -#define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr)) -#define SMART_MTU (SMART_MSS + MTUOFF) - if ((find->fp_wsize % (find->fp_mss + MTUOFF) || - find->fp_wsize / (find->fp_mss + MTUOFF) != - f->fp_wsize) && - (find->fp_wsize % SMART_MTU || - find->fp_wsize / SMART_MTU != - f->fp_wsize)) - continue; - } else if (f->fp_flags & PF_OSFP_WSIZE_MOD) { - if (f->fp_wsize == 0 || find->fp_wsize % - f->fp_wsize) - continue; - } else { - if (f->fp_wsize != find->fp_wsize) - continue; - } - } - return (f); - } - - return (NULL); -} - -/* Find an exact fingerprint in the list */ -static struct pf_os_fingerprint * -pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find) -{ - struct pf_os_fingerprint *f; - - SLIST_FOREACH(f, list, fp_next) { - if (f->fp_tcpopts == find->fp_tcpopts && - f->fp_wsize == find->fp_wsize && - f->fp_psize == find->fp_psize && - f->fp_mss == find->fp_mss && - f->fp_flags == find->fp_flags && - f->fp_optcnt == find->fp_optcnt && - f->fp_wscale == find->fp_wscale && - f->fp_ttl == find->fp_ttl) - return (f); - } - - return (NULL); -} - -/* Insert a fingerprint into the list */ -static void -pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins) -{ - struct pf_os_fingerprint *f, *prev = NULL; - - /* XXX need to go semi tree based. can key on tcp options */ - - SLIST_FOREACH(f, list, fp_next) - prev = f; - if (prev) - SLIST_INSERT_AFTER(prev, ins, fp_next); - else - SLIST_INSERT_HEAD(list, ins, fp_next); -} - -/* Fill a fingerprint by its number (from an ioctl) */ -int -pf_osfp_get(struct pf_osfp_ioctl *fpioc) -{ - struct pf_os_fingerprint *fp; - struct pf_osfp_entry *entry; - int num = fpioc->fp_getnum; - int i = 0; - - - memset(fpioc, 0, sizeof(*fpioc)); - SLIST_FOREACH(fp, &V_pf_osfp_list, fp_next) { - SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { - if (i++ == num) { - fpioc->fp_mss = fp->fp_mss; - fpioc->fp_wsize = fp->fp_wsize; - fpioc->fp_flags = fp->fp_flags; - fpioc->fp_psize = fp->fp_psize; - fpioc->fp_ttl = fp->fp_ttl; - fpioc->fp_wscale = fp->fp_wscale; - fpioc->fp_getnum = num; - memcpy(&fpioc->fp_os, entry, - sizeof(fpioc->fp_os)); - return (0); - } - } - } - - return (EBUSY); -} - - -#ifdef PFDEBUG -/* Validate that each signature is reachable */ -static struct pf_os_fingerprint * -pf_osfp_validate(void) -{ - struct pf_os_fingerprint *f, *f2, find; - - SLIST_FOREACH(f, &V_pf_osfp_list, fp_next) { - memcpy(&find, f, sizeof(find)); - - /* We do a few MSS/th_win percolations to make things unique */ - if (find.fp_mss == 0) - find.fp_mss = 128; - if (f->fp_flags & PF_OSFP_WSIZE_MSS) - find.fp_wsize *= find.fp_mss; - else if (f->fp_flags & PF_OSFP_WSIZE_MTU) - find.fp_wsize *= (find.fp_mss + 40); - else if (f->fp_flags & PF_OSFP_WSIZE_MOD) - find.fp_wsize *= 2; - if (f != (f2 = pf_osfp_find(&V_pf_osfp_list, &find, 0))) { - if (f2) - printf("Found \"%s %s %s\" instead of " - "\"%s %s %s\"\n", - SLIST_FIRST(&f2->fp_oses)->fp_class_nm, - SLIST_FIRST(&f2->fp_oses)->fp_version_nm, - SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm, - SLIST_FIRST(&f->fp_oses)->fp_class_nm, - SLIST_FIRST(&f->fp_oses)->fp_version_nm, - SLIST_FIRST(&f->fp_oses)->fp_subtype_nm); - else - printf("Couldn't find \"%s %s %s\"\n", - SLIST_FIRST(&f->fp_oses)->fp_class_nm, - SLIST_FIRST(&f->fp_oses)->fp_version_nm, - SLIST_FIRST(&f->fp_oses)->fp_subtype_nm); - return (f); - } - } - return (NULL); -} -#endif /* PFDEBUG */ diff --git a/sys/contrib/pf/net/pf_ruleset.c b/sys/contrib/pf/net/pf_ruleset.c deleted file mode 100644 index 77652a69b3c58..0000000000000 --- a/sys/contrib/pf/net/pf_ruleset.c +++ /dev/null @@ -1,424 +0,0 @@ -/* $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Effort sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F30602-01-2-0537. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#ifdef _KERNEL -# include -# include -#endif /* _KERNEL */ -#include - -#include -#include -#include -#include - -#include -#include - -#ifdef INET6 -#include -#endif /* INET6 */ - - -#ifdef _KERNEL -#define DPFPRINTF(format, x...) \ - if (V_pf_status.debug >= PF_DEBUG_NOISY) \ - printf(format , ##x) -#define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO) -#define rs_free(x) free(x, M_TEMP) - -#else -/* Userland equivalents so we can lend code to pfctl et al. */ - -#include -#include -#include -#include -#include -#define rs_malloc(x) calloc(1, x) -#define rs_free(x) free(x) - -#ifdef PFDEBUG -#include -#define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) -#else -#define DPFPRINTF(format, x...) ((void)0) -#endif /* PFDEBUG */ -#endif /* _KERNEL */ - -#ifdef _KERNEL -VNET_DEFINE(struct pf_anchor_global, pf_anchors); -VNET_DEFINE(struct pf_anchor, pf_main_anchor); -#else /* ! _KERNEL */ -struct pf_anchor_global pf_anchors; -struct pf_anchor pf_main_anchor; -#undef V_pf_anchors -#define V_pf_anchors pf_anchors -#undef pf_main_ruleset -#define pf_main_ruleset pf_main_anchor.ruleset -#endif /* _KERNEL */ - -static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); - -static struct pf_anchor *pf_find_anchor(const char *); - -RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); -RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); - -static __inline int -pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) -{ - int c = strcmp(a->path, b->path); - - return (c ? (c < 0 ? -1 : 1) : 0); -} - -int -pf_get_ruleset_number(u_int8_t action) -{ - switch (action) { - case PF_SCRUB: - case PF_NOSCRUB: - return (PF_RULESET_SCRUB); - break; - case PF_PASS: - case PF_DROP: - return (PF_RULESET_FILTER); - break; - case PF_NAT: - case PF_NONAT: - return (PF_RULESET_NAT); - break; - case PF_BINAT: - case PF_NOBINAT: - return (PF_RULESET_BINAT); - break; - case PF_RDR: - case PF_NORDR: - return (PF_RULESET_RDR); - break; - default: - return (PF_RULESET_MAX); - break; - } -} - -void -pf_init_ruleset(struct pf_ruleset *ruleset) -{ - int i; - - memset(ruleset, 0, sizeof(struct pf_ruleset)); - for (i = 0; i < PF_RULESET_MAX; i++) { - TAILQ_INIT(&ruleset->rules[i].queues[0]); - TAILQ_INIT(&ruleset->rules[i].queues[1]); - ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; - ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; - } -} - -static struct pf_anchor * -pf_find_anchor(const char *path) -{ - struct pf_anchor *key, *found; - - key = (struct pf_anchor *)rs_malloc(sizeof(*key)); - if (key == NULL) - return (NULL); - strlcpy(key->path, path, sizeof(key->path)); - found = RB_FIND(pf_anchor_global, &V_pf_anchors, key); - rs_free(key); - return (found); -} - -struct pf_ruleset * -pf_find_ruleset(const char *path) -{ - struct pf_anchor *anchor; - - while (*path == '/') - path++; - if (!*path) - return (&pf_main_ruleset); - anchor = pf_find_anchor(path); - if (anchor == NULL) - return (NULL); - else - return (&anchor->ruleset); -} - -struct pf_ruleset * -pf_find_or_create_ruleset(const char *path) -{ - char *p, *q, *r; - struct pf_ruleset *ruleset; - struct pf_anchor *anchor = NULL, *dup, *parent = NULL; - - if (path[0] == 0) - return (&pf_main_ruleset); - while (*path == '/') - path++; - ruleset = pf_find_ruleset(path); - if (ruleset != NULL) - return (ruleset); - p = (char *)rs_malloc(MAXPATHLEN); - if (p == NULL) - return (NULL); - strlcpy(p, path, MAXPATHLEN); - while (parent == NULL && (q = strrchr(p, '/')) != NULL) { - *q = 0; - if ((ruleset = pf_find_ruleset(p)) != NULL) { - parent = ruleset->anchor; - break; - } - } - if (q == NULL) - q = p; - else - q++; - strlcpy(p, path, MAXPATHLEN); - if (!*q) { - rs_free(p); - return (NULL); - } - while ((r = strchr(q, '/')) != NULL || *q) { - if (r != NULL) - *r = 0; - if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || - (parent != NULL && strlen(parent->path) >= - MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { - rs_free(p); - return (NULL); - } - anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); - if (anchor == NULL) { - rs_free(p); - return (NULL); - } - RB_INIT(&anchor->children); - strlcpy(anchor->name, q, sizeof(anchor->name)); - if (parent != NULL) { - strlcpy(anchor->path, parent->path, - sizeof(anchor->path)); - strlcat(anchor->path, "/", sizeof(anchor->path)); - } - strlcat(anchor->path, anchor->name, sizeof(anchor->path)); - if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) != - NULL) { - printf("pf_find_or_create_ruleset: RB_INSERT1 " - "'%s' '%s' collides with '%s' '%s'\n", - anchor->path, anchor->name, dup->path, dup->name); - rs_free(anchor); - rs_free(p); - return (NULL); - } - if (parent != NULL) { - anchor->parent = parent; - if ((dup = RB_INSERT(pf_anchor_node, &parent->children, - anchor)) != NULL) { - printf("pf_find_or_create_ruleset: " - "RB_INSERT2 '%s' '%s' collides with " - "'%s' '%s'\n", anchor->path, anchor->name, - dup->path, dup->name); - RB_REMOVE(pf_anchor_global, &V_pf_anchors, - anchor); - rs_free(anchor); - rs_free(p); - return (NULL); - } - } - pf_init_ruleset(&anchor->ruleset); - anchor->ruleset.anchor = anchor; - parent = anchor; - if (r != NULL) - q = r + 1; - else - *q = 0; - } - rs_free(p); - return (&anchor->ruleset); -} - -void -pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) -{ - struct pf_anchor *parent; - int i; - - while (ruleset != NULL) { - if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || - !RB_EMPTY(&ruleset->anchor->children) || - ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || - ruleset->topen) - return; - for (i = 0; i < PF_RULESET_MAX; ++i) - if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || - !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || - ruleset->rules[i].inactive.open) - return; - RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor); - if ((parent = ruleset->anchor->parent) != NULL) - RB_REMOVE(pf_anchor_node, &parent->children, - ruleset->anchor); - rs_free(ruleset->anchor); - if (parent == NULL) - return; - ruleset = &parent->ruleset; - } -} - -int -pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, - const char *name) -{ - char *p, *path; - struct pf_ruleset *ruleset; - - r->anchor = NULL; - r->anchor_relative = 0; - r->anchor_wildcard = 0; - if (!name[0]) - return (0); - path = (char *)rs_malloc(MAXPATHLEN); - if (path == NULL) - return (1); - if (name[0] == '/') - strlcpy(path, name + 1, MAXPATHLEN); - else { - /* relative path */ - r->anchor_relative = 1; - if (s->anchor == NULL || !s->anchor->path[0]) - path[0] = 0; - else - strlcpy(path, s->anchor->path, MAXPATHLEN); - while (name[0] == '.' && name[1] == '.' && name[2] == '/') { - if (!path[0]) { - printf("pf_anchor_setup: .. beyond root\n"); - rs_free(path); - return (1); - } - if ((p = strrchr(path, '/')) != NULL) - *p = 0; - else - path[0] = 0; - r->anchor_relative++; - name += 3; - } - if (path[0]) - strlcat(path, "/", MAXPATHLEN); - strlcat(path, name, MAXPATHLEN); - } - if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { - r->anchor_wildcard = 1; - *p = 0; - } - ruleset = pf_find_or_create_ruleset(path); - rs_free(path); - if (ruleset == NULL || ruleset->anchor == NULL) { - printf("pf_anchor_setup: ruleset\n"); - return (1); - } - r->anchor = ruleset->anchor; - r->anchor->refcnt++; - return (0); -} - -int -pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, - struct pfioc_rule *pr) -{ - pr->anchor_call[0] = 0; - if (r->anchor == NULL) - return (0); - if (!r->anchor_relative) { - strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); - strlcat(pr->anchor_call, r->anchor->path, - sizeof(pr->anchor_call)); - } else { - char *a, *p; - int i; - - a = (char *)rs_malloc(MAXPATHLEN); - if (a == NULL) - return (1); - if (rs->anchor == NULL) - a[0] = 0; - else - strlcpy(a, rs->anchor->path, MAXPATHLEN); - for (i = 1; i < r->anchor_relative; ++i) { - if ((p = strrchr(a, '/')) == NULL) - p = a; - *p = 0; - strlcat(pr->anchor_call, "../", - sizeof(pr->anchor_call)); - } - if (strncmp(a, r->anchor->path, strlen(a))) { - printf("pf_anchor_copyout: '%s' '%s'\n", a, - r->anchor->path); - rs_free(a); - return (1); - } - if (strlen(r->anchor->path) > strlen(a)) - strlcat(pr->anchor_call, r->anchor->path + (a[0] ? - strlen(a) + 1 : 0), sizeof(pr->anchor_call)); - rs_free(a); - } - if (r->anchor_wildcard) - strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", - sizeof(pr->anchor_call)); - return (0); -} - -void -pf_anchor_remove(struct pf_rule *r) -{ - if (r->anchor == NULL) - return; - if (r->anchor->refcnt <= 0) { - printf("pf_anchor_remove: broken refcount\n"); - r->anchor = NULL; - return; - } - if (!--r->anchor->refcnt) - pf_remove_if_empty_ruleset(&r->anchor->ruleset); - r->anchor = NULL; -} diff --git a/sys/contrib/pf/net/pf_table.c b/sys/contrib/pf/net/pf_table.c deleted file mode 100644 index fa88045dc5095..0000000000000 --- a/sys/contrib/pf/net/pf_table.c +++ /dev/null @@ -1,2191 +0,0 @@ -/* $OpenBSD: pf_table.c,v 1.79 2008/10/08 06:24:50 mcbride Exp $ */ - -/* - * Copyright (c) 2002 Cedric Berger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_inet.h" -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define ACCEPT_FLAGS(flags, oklist) \ - do { \ - if ((flags & ~(oklist)) & \ - PFR_FLAG_ALLMASK) \ - return (EINVAL); \ - } while (0) - -#define FILLIN_SIN(sin, addr) \ - do { \ - (sin).sin_len = sizeof(sin); \ - (sin).sin_family = AF_INET; \ - (sin).sin_addr = (addr); \ - } while (0) - -#define FILLIN_SIN6(sin6, addr) \ - do { \ - (sin6).sin6_len = sizeof(sin6); \ - (sin6).sin6_family = AF_INET6; \ - (sin6).sin6_addr = (addr); \ - } while (0) - -#define SWAP(type, a1, a2) \ - do { \ - type tmp = a1; \ - a1 = a2; \ - a2 = tmp; \ - } while (0) - -#define SUNION2PF(su, af) (((af)==AF_INET) ? \ - (struct pf_addr *)&(su)->sin.sin_addr : \ - (struct pf_addr *)&(su)->sin6.sin6_addr) - -#define AF_BITS(af) (((af)==AF_INET)?32:128) -#define ADDR_NETWORK(ad) ((ad)->pfra_net < AF_BITS((ad)->pfra_af)) -#define KENTRY_NETWORK(ke) ((ke)->pfrke_net < AF_BITS((ke)->pfrke_af)) -#define KENTRY_RNF_ROOT(ke) \ - ((((struct radix_node *)(ke))->rn_flags & RNF_ROOT) != 0) - -#define NO_ADDRESSES (-1) -#define ENQUEUE_UNMARKED_ONLY (1) -#define INVERT_NEG_FLAG (1) - -struct pfr_walktree { - enum pfrw_op { - PFRW_MARK, - PFRW_SWEEP, - PFRW_ENQUEUE, - PFRW_GET_ADDRS, - PFRW_GET_ASTATS, - PFRW_POOL_GET, - PFRW_DYNADDR_UPDATE - } pfrw_op; - union { - struct pfr_addr *pfrw1_addr; - struct pfr_astats *pfrw1_astats; - struct pfr_kentryworkq *pfrw1_workq; - struct pfr_kentry *pfrw1_kentry; - struct pfi_dynaddr *pfrw1_dyn; - } pfrw_1; - int pfrw_free; -}; -#define pfrw_addr pfrw_1.pfrw1_addr -#define pfrw_astats pfrw_1.pfrw1_astats -#define pfrw_workq pfrw_1.pfrw1_workq -#define pfrw_kentry pfrw_1.pfrw1_kentry -#define pfrw_dyn pfrw_1.pfrw1_dyn -#define pfrw_cnt pfrw_free - -#define senderr(e) do { rv = (e); goto _bad; } while (0) - -static MALLOC_DEFINE(M_PFTABLE, "pf_table", "pf(4) tables structures"); -static VNET_DEFINE(uma_zone_t, pfr_kentry_z); -#define V_pfr_kentry_z VNET(pfr_kentry_z) -static VNET_DEFINE(uma_zone_t, pfr_kcounters_z); -#define V_pfr_kcounters_z VNET(pfr_kcounters_z) - -static struct pf_addr pfr_ffaddr = { - .addr32 = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff } -}; - -static void pfr_copyout_addr(struct pfr_addr *, - struct pfr_kentry *ke); -static int pfr_validate_addr(struct pfr_addr *); -static void pfr_enqueue_addrs(struct pfr_ktable *, - struct pfr_kentryworkq *, int *, int); -static void pfr_mark_addrs(struct pfr_ktable *); -static struct pfr_kentry - *pfr_lookup_addr(struct pfr_ktable *, - struct pfr_addr *, int); -static struct pfr_kentry *pfr_create_kentry(struct pfr_addr *); -static void pfr_destroy_kentries(struct pfr_kentryworkq *); -static void pfr_destroy_kentry(struct pfr_kentry *); -static void pfr_insert_kentries(struct pfr_ktable *, - struct pfr_kentryworkq *, long); -static void pfr_remove_kentries(struct pfr_ktable *, - struct pfr_kentryworkq *); -static void pfr_clstats_kentries(struct pfr_kentryworkq *, long, - int); -static void pfr_reset_feedback(struct pfr_addr *, int); -static void pfr_prepare_network(union sockaddr_union *, int, int); -static int pfr_route_kentry(struct pfr_ktable *, - struct pfr_kentry *); -static int pfr_unroute_kentry(struct pfr_ktable *, - struct pfr_kentry *); -static int pfr_walktree(struct radix_node *, void *); -static int pfr_validate_table(struct pfr_table *, int, int); -static int pfr_fix_anchor(char *); -static void pfr_commit_ktable(struct pfr_ktable *, long); -static void pfr_insert_ktables(struct pfr_ktableworkq *); -static void pfr_insert_ktable(struct pfr_ktable *); -static void pfr_setflags_ktables(struct pfr_ktableworkq *); -static void pfr_setflags_ktable(struct pfr_ktable *, int); -static void pfr_clstats_ktables(struct pfr_ktableworkq *, long, - int); -static void pfr_clstats_ktable(struct pfr_ktable *, long, int); -static struct pfr_ktable - *pfr_create_ktable(struct pfr_table *, long, int); -static void pfr_destroy_ktables(struct pfr_ktableworkq *, int); -static void pfr_destroy_ktable(struct pfr_ktable *, int); -static int pfr_ktable_compare(struct pfr_ktable *, - struct pfr_ktable *); -static struct pfr_ktable - *pfr_lookup_table(struct pfr_table *); -static void pfr_clean_node_mask(struct pfr_ktable *, - struct pfr_kentryworkq *); -static int pfr_table_count(struct pfr_table *, int); -static int pfr_skip_table(struct pfr_table *, - struct pfr_ktable *, int); -static struct pfr_kentry - *pfr_kentry_byidx(struct pfr_ktable *, int, int); - -static RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); -static RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); - -struct pfr_ktablehead pfr_ktables; -struct pfr_table pfr_nulltable; -int pfr_ktable_cnt; - -void -pfr_initialize(void) -{ - - V_pfr_kentry_z = uma_zcreate("pf table entries", - sizeof(struct pfr_kentry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, - 0); - V_pfr_kcounters_z = uma_zcreate("pf table counters", - sizeof(struct pfr_kcounters), NULL, NULL, NULL, NULL, - UMA_ALIGN_PTR, 0); - V_pf_limits[PF_LIMIT_TABLE_ENTRIES].zone = V_pfr_kentry_z; - V_pf_limits[PF_LIMIT_TABLE_ENTRIES].limit = PFR_KENTRY_HIWAT; -} - -void -pfr_cleanup(void) -{ - - uma_zdestroy(V_pfr_kentry_z); - uma_zdestroy(V_pfr_kcounters_z); -} - -int -pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) -{ - struct pfr_ktable *kt; - struct pfr_kentryworkq workq; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - if (kt->pfrkt_flags & PFR_TFLAG_CONST) - return (EPERM); - pfr_enqueue_addrs(kt, &workq, ndel, 0); - - if (!(flags & PFR_FLAG_DUMMY)) { - pfr_remove_kentries(kt, &workq); - KASSERT(kt->pfrkt_cnt == 0, ("%s: non-null pfrkt_cnt", __func__)); - } - return (0); -} - -int -pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nadd, int flags) -{ - struct pfr_ktable *kt, *tmpkt; - struct pfr_kentryworkq workq; - struct pfr_kentry *p, *q; - struct pfr_addr *ad; - int i, rv, xadd = 0; - long tzero = time_second; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - if (kt->pfrkt_flags & PFR_TFLAG_CONST) - return (EPERM); - tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); - if (tmpkt == NULL) - return (ENOMEM); - SLIST_INIT(&workq); - for (i = 0, ad = addr; i < size; i++, ad++) { - if (pfr_validate_addr(ad)) - senderr(EINVAL); - p = pfr_lookup_addr(kt, ad, 1); - q = pfr_lookup_addr(tmpkt, ad, 1); - if (flags & PFR_FLAG_FEEDBACK) { - if (q != NULL) - ad->pfra_fback = PFR_FB_DUPLICATE; - else if (p == NULL) - ad->pfra_fback = PFR_FB_ADDED; - else if (p->pfrke_not != ad->pfra_not) - ad->pfra_fback = PFR_FB_CONFLICT; - else - ad->pfra_fback = PFR_FB_NONE; - } - if (p == NULL && q == NULL) { - p = pfr_create_kentry(ad); - if (p == NULL) - senderr(ENOMEM); - if (pfr_route_kentry(tmpkt, p)) { - pfr_destroy_kentry(p); - ad->pfra_fback = PFR_FB_NONE; - } else { - SLIST_INSERT_HEAD(&workq, p, pfrke_workq); - xadd++; - } - } - } - pfr_clean_node_mask(tmpkt, &workq); - if (!(flags & PFR_FLAG_DUMMY)) - pfr_insert_kentries(kt, &workq, tzero); - else - pfr_destroy_kentries(&workq); - if (nadd != NULL) - *nadd = xadd; - pfr_destroy_ktable(tmpkt, 0); - return (0); -_bad: - pfr_clean_node_mask(tmpkt, &workq); - pfr_destroy_kentries(&workq); - if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); - pfr_destroy_ktable(tmpkt, 0); - return (rv); -} - -int -pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *ndel, int flags) -{ - struct pfr_ktable *kt; - struct pfr_kentryworkq workq; - struct pfr_kentry *p; - struct pfr_addr *ad; - int i, rv, xdel = 0, log = 1; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - if (kt->pfrkt_flags & PFR_TFLAG_CONST) - return (EPERM); - /* - * there are two algorithms to choose from here. - * with: - * n: number of addresses to delete - * N: number of addresses in the table - * - * one is O(N) and is better for large 'n' - * one is O(n*LOG(N)) and is better for small 'n' - * - * following code try to decide which one is best. - */ - for (i = kt->pfrkt_cnt; i > 0; i >>= 1) - log++; - if (size > kt->pfrkt_cnt/log) { - /* full table scan */ - pfr_mark_addrs(kt); - } else { - /* iterate over addresses to delete */ - for (i = 0, ad = addr; i < size; i++, ad++) { - if (pfr_validate_addr(ad)) - return (EINVAL); - p = pfr_lookup_addr(kt, ad, 1); - if (p != NULL) - p->pfrke_mark = 0; - } - } - SLIST_INIT(&workq); - for (i = 0, ad = addr; i < size; i++, ad++) { - if (pfr_validate_addr(ad)) - senderr(EINVAL); - p = pfr_lookup_addr(kt, ad, 1); - if (flags & PFR_FLAG_FEEDBACK) { - if (p == NULL) - ad->pfra_fback = PFR_FB_NONE; - else if (p->pfrke_not != ad->pfra_not) - ad->pfra_fback = PFR_FB_CONFLICT; - else if (p->pfrke_mark) - ad->pfra_fback = PFR_FB_DUPLICATE; - else - ad->pfra_fback = PFR_FB_DELETED; - } - if (p != NULL && p->pfrke_not == ad->pfra_not && - !p->pfrke_mark) { - p->pfrke_mark = 1; - SLIST_INSERT_HEAD(&workq, p, pfrke_workq); - xdel++; - } - } - if (!(flags & PFR_FLAG_DUMMY)) - pfr_remove_kentries(kt, &workq); - if (ndel != NULL) - *ndel = xdel; - return (0); -_bad: - if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); - return (rv); -} - -int -pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *size2, int *nadd, int *ndel, int *nchange, int flags, - u_int32_t ignore_pfrt_flags) -{ - struct pfr_ktable *kt, *tmpkt; - struct pfr_kentryworkq addq, delq, changeq; - struct pfr_kentry *p, *q; - struct pfr_addr ad; - int i, rv, xadd = 0, xdel = 0, xchange = 0; - long tzero = time_second; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, ignore_pfrt_flags, flags & - PFR_FLAG_USERIOCTL)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - if (kt->pfrkt_flags & PFR_TFLAG_CONST) - return (EPERM); - tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); - if (tmpkt == NULL) - return (ENOMEM); - pfr_mark_addrs(kt); - SLIST_INIT(&addq); - SLIST_INIT(&delq); - SLIST_INIT(&changeq); - for (i = 0; i < size; i++) { - /* - * XXXGL: undertand pf_if usage of this function - * and make ad a moving pointer - */ - bcopy(addr + i, &ad, sizeof(ad)); - if (pfr_validate_addr(&ad)) - senderr(EINVAL); - ad.pfra_fback = PFR_FB_NONE; - p = pfr_lookup_addr(kt, &ad, 1); - if (p != NULL) { - if (p->pfrke_mark) { - ad.pfra_fback = PFR_FB_DUPLICATE; - goto _skip; - } - p->pfrke_mark = 1; - if (p->pfrke_not != ad.pfra_not) { - SLIST_INSERT_HEAD(&changeq, p, pfrke_workq); - ad.pfra_fback = PFR_FB_CHANGED; - xchange++; - } - } else { - q = pfr_lookup_addr(tmpkt, &ad, 1); - if (q != NULL) { - ad.pfra_fback = PFR_FB_DUPLICATE; - goto _skip; - } - p = pfr_create_kentry(&ad); - if (p == NULL) - senderr(ENOMEM); - if (pfr_route_kentry(tmpkt, p)) { - pfr_destroy_kentry(p); - ad.pfra_fback = PFR_FB_NONE; - } else { - SLIST_INSERT_HEAD(&addq, p, pfrke_workq); - ad.pfra_fback = PFR_FB_ADDED; - xadd++; - } - } -_skip: - if (flags & PFR_FLAG_FEEDBACK) - bcopy(&ad, addr + i, sizeof(ad)); - } - pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); - if ((flags & PFR_FLAG_FEEDBACK) && *size2) { - if (*size2 < size+xdel) { - *size2 = size+xdel; - senderr(0); - } - i = 0; - SLIST_FOREACH(p, &delq, pfrke_workq) { - pfr_copyout_addr(&ad, p); - ad.pfra_fback = PFR_FB_DELETED; - bcopy(&ad, addr + size + i, sizeof(ad)); - i++; - } - } - pfr_clean_node_mask(tmpkt, &addq); - if (!(flags & PFR_FLAG_DUMMY)) { - pfr_insert_kentries(kt, &addq, tzero); - pfr_remove_kentries(kt, &delq); - pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); - } else - pfr_destroy_kentries(&addq); - if (nadd != NULL) - *nadd = xadd; - if (ndel != NULL) - *ndel = xdel; - if (nchange != NULL) - *nchange = xchange; - if ((flags & PFR_FLAG_FEEDBACK) && size2) - *size2 = size+xdel; - pfr_destroy_ktable(tmpkt, 0); - return (0); -_bad: - pfr_clean_node_mask(tmpkt, &addq); - pfr_destroy_kentries(&addq); - if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); - pfr_destroy_ktable(tmpkt, 0); - return (rv); -} - -int -pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nmatch, int flags) -{ - struct pfr_ktable *kt; - struct pfr_kentry *p; - struct pfr_addr *ad; - int i, xmatch = 0; - - PF_RULES_RASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_REPLACE); - if (pfr_validate_table(tbl, 0, 0)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - - for (i = 0, ad = addr; i < size; i++, ad++) { - if (pfr_validate_addr(ad)) - return (EINVAL); - if (ADDR_NETWORK(ad)) - return (EINVAL); - p = pfr_lookup_addr(kt, ad, 0); - if (flags & PFR_FLAG_REPLACE) - pfr_copyout_addr(ad, p); - ad->pfra_fback = (p == NULL) ? PFR_FB_NONE : - (p->pfrke_not ? PFR_FB_NOTMATCH : PFR_FB_MATCH); - if (p != NULL && !p->pfrke_not) - xmatch++; - } - if (nmatch != NULL) - *nmatch = xmatch; - return (0); -} - -int -pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, - int flags) -{ - struct pfr_ktable *kt; - struct pfr_walktree w; - int rv; - - PF_RULES_RASSERT(); - - ACCEPT_FLAGS(flags, 0); - if (pfr_validate_table(tbl, 0, 0)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - if (kt->pfrkt_cnt > *size) { - *size = kt->pfrkt_cnt; - return (0); - } - - bzero(&w, sizeof(w)); - w.pfrw_op = PFRW_GET_ADDRS; - w.pfrw_addr = addr; - w.pfrw_free = kt->pfrkt_cnt; - rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); - if (!rv) - rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, - &w); - if (rv) - return (rv); - - KASSERT(w.pfrw_free == 0, ("%s: corruption detected (%d)", __func__, - w.pfrw_free)); - - *size = kt->pfrkt_cnt; - return (0); -} - -int -pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, - int flags) -{ - struct pfr_ktable *kt; - struct pfr_walktree w; - struct pfr_kentryworkq workq; - int rv; - long tzero = time_second; - - PF_RULES_RASSERT(); - - /* XXX PFR_FLAG_CLSTATS disabled */ - ACCEPT_FLAGS(flags, 0); - if (pfr_validate_table(tbl, 0, 0)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - if (kt->pfrkt_cnt > *size) { - *size = kt->pfrkt_cnt; - return (0); - } - - bzero(&w, sizeof(w)); - w.pfrw_op = PFRW_GET_ASTATS; - w.pfrw_astats = addr; - w.pfrw_free = kt->pfrkt_cnt; - rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); - if (!rv) - rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, - &w); - if (!rv && (flags & PFR_FLAG_CLSTATS)) { - pfr_enqueue_addrs(kt, &workq, NULL, 0); - pfr_clstats_kentries(&workq, tzero, 0); - } - if (rv) - return (rv); - - if (w.pfrw_free) { - printf("pfr_get_astats: corruption detected (%d).\n", - w.pfrw_free); - return (ENOTTY); - } - *size = kt->pfrkt_cnt; - return (0); -} - -int -pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nzero, int flags) -{ - struct pfr_ktable *kt; - struct pfr_kentryworkq workq; - struct pfr_kentry *p; - struct pfr_addr *ad; - int i, rv, xzero = 0; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0, 0)) - return (EINVAL); - kt = pfr_lookup_table(tbl); - if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (ESRCH); - SLIST_INIT(&workq); - for (i = 0, ad = addr; i < size; i++, ad++) { - if (pfr_validate_addr(ad)) - senderr(EINVAL); - p = pfr_lookup_addr(kt, ad, 1); - if (flags & PFR_FLAG_FEEDBACK) { - ad->pfra_fback = (p != NULL) ? - PFR_FB_CLEARED : PFR_FB_NONE; - } - if (p != NULL) { - SLIST_INSERT_HEAD(&workq, p, pfrke_workq); - xzero++; - } - } - - if (!(flags & PFR_FLAG_DUMMY)) - pfr_clstats_kentries(&workq, 0, 0); - if (nzero != NULL) - *nzero = xzero; - return (0); -_bad: - if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); - return (rv); -} - -static int -pfr_validate_addr(struct pfr_addr *ad) -{ - int i; - - switch (ad->pfra_af) { -#ifdef INET - case AF_INET: - if (ad->pfra_net > 32) - return (-1); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (ad->pfra_net > 128) - return (-1); - break; -#endif /* INET6 */ - default: - return (-1); - } - if (ad->pfra_net < 128 && - (((caddr_t)ad)[ad->pfra_net/8] & (0xFF >> (ad->pfra_net%8)))) - return (-1); - for (i = (ad->pfra_net+7)/8; i < sizeof(ad->pfra_u); i++) - if (((caddr_t)ad)[i]) - return (-1); - if (ad->pfra_not && ad->pfra_not != 1) - return (-1); - if (ad->pfra_fback) - return (-1); - return (0); -} - -static void -pfr_enqueue_addrs(struct pfr_ktable *kt, struct pfr_kentryworkq *workq, - int *naddr, int sweep) -{ - struct pfr_walktree w; - - SLIST_INIT(workq); - bzero(&w, sizeof(w)); - w.pfrw_op = sweep ? PFRW_SWEEP : PFRW_ENQUEUE; - w.pfrw_workq = workq; - if (kt->pfrkt_ip4 != NULL) - if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, - &w)) - printf("pfr_enqueue_addrs: IPv4 walktree failed.\n"); - if (kt->pfrkt_ip6 != NULL) - if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, - &w)) - printf("pfr_enqueue_addrs: IPv6 walktree failed.\n"); - if (naddr != NULL) - *naddr = w.pfrw_cnt; -} - -static void -pfr_mark_addrs(struct pfr_ktable *kt) -{ - struct pfr_walktree w; - - bzero(&w, sizeof(w)); - w.pfrw_op = PFRW_MARK; - if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) - printf("pfr_mark_addrs: IPv4 walktree failed.\n"); - if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) - printf("pfr_mark_addrs: IPv6 walktree failed.\n"); -} - - -static struct pfr_kentry * -pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact) -{ - union sockaddr_union sa, mask; - struct radix_node_head *head = NULL; - struct pfr_kentry *ke; - - bzero(&sa, sizeof(sa)); - if (ad->pfra_af == AF_INET) { - FILLIN_SIN(sa.sin, ad->pfra_ip4addr); - head = kt->pfrkt_ip4; - } else if ( ad->pfra_af == AF_INET6 ) { - FILLIN_SIN6(sa.sin6, ad->pfra_ip6addr); - head = kt->pfrkt_ip6; - } - if (ADDR_NETWORK(ad)) { - pfr_prepare_network(&mask, ad->pfra_af, ad->pfra_net); - ke = (struct pfr_kentry *)rn_lookup(&sa, &mask, head); - if (ke && KENTRY_RNF_ROOT(ke)) - ke = NULL; - } else { - ke = (struct pfr_kentry *)rn_match(&sa, head); - if (ke && KENTRY_RNF_ROOT(ke)) - ke = NULL; - if (exact && ke && KENTRY_NETWORK(ke)) - ke = NULL; - } - return (ke); -} - -static struct pfr_kentry * -pfr_create_kentry(struct pfr_addr *ad) -{ - struct pfr_kentry *ke; - - ke = uma_zalloc(V_pfr_kentry_z, M_NOWAIT | M_ZERO); - if (ke == NULL) - return (NULL); - - if (ad->pfra_af == AF_INET) - FILLIN_SIN(ke->pfrke_sa.sin, ad->pfra_ip4addr); - else if (ad->pfra_af == AF_INET6) - FILLIN_SIN6(ke->pfrke_sa.sin6, ad->pfra_ip6addr); - ke->pfrke_af = ad->pfra_af; - ke->pfrke_net = ad->pfra_net; - ke->pfrke_not = ad->pfra_not; - return (ke); -} - -static void -pfr_destroy_kentries(struct pfr_kentryworkq *workq) -{ - struct pfr_kentry *p, *q; - - for (p = SLIST_FIRST(workq); p != NULL; p = q) { - q = SLIST_NEXT(p, pfrke_workq); - pfr_destroy_kentry(p); - } -} - -static void -pfr_destroy_kentry(struct pfr_kentry *ke) -{ - if (ke->pfrke_counters) - uma_zfree(V_pfr_kcounters_z, ke->pfrke_counters); - uma_zfree(V_pfr_kentry_z, ke); -} - -static void -pfr_insert_kentries(struct pfr_ktable *kt, - struct pfr_kentryworkq *workq, long tzero) -{ - struct pfr_kentry *p; - int rv, n = 0; - - SLIST_FOREACH(p, workq, pfrke_workq) { - rv = pfr_route_kentry(kt, p); - if (rv) { - printf("pfr_insert_kentries: cannot route entry " - "(code=%d).\n", rv); - break; - } - p->pfrke_tzero = tzero; - n++; - } - kt->pfrkt_cnt += n; -} - -int -pfr_insert_kentry(struct pfr_ktable *kt, struct pfr_addr *ad, long tzero) -{ - struct pfr_kentry *p; - int rv; - - p = pfr_lookup_addr(kt, ad, 1); - if (p != NULL) - return (0); - p = pfr_create_kentry(ad); - if (p == NULL) - return (EINVAL); - - rv = pfr_route_kentry(kt, p); - if (rv) - return (rv); - - p->pfrke_tzero = tzero; - kt->pfrkt_cnt++; - - return (0); -} - -static void -pfr_remove_kentries(struct pfr_ktable *kt, - struct pfr_kentryworkq *workq) -{ - struct pfr_kentry *p; - int n = 0; - - SLIST_FOREACH(p, workq, pfrke_workq) { - pfr_unroute_kentry(kt, p); - n++; - } - kt->pfrkt_cnt -= n; - pfr_destroy_kentries(workq); -} - -static void -pfr_clean_node_mask(struct pfr_ktable *kt, - struct pfr_kentryworkq *workq) -{ - struct pfr_kentry *p; - - SLIST_FOREACH(p, workq, pfrke_workq) - pfr_unroute_kentry(kt, p); -} - -static void -pfr_clstats_kentries(struct pfr_kentryworkq *workq, long tzero, int negchange) -{ - struct pfr_kentry *p; - - SLIST_FOREACH(p, workq, pfrke_workq) { - if (negchange) - p->pfrke_not = !p->pfrke_not; - if (p->pfrke_counters) { - uma_zfree(V_pfr_kcounters_z, p->pfrke_counters); - p->pfrke_counters = NULL; - } - p->pfrke_tzero = tzero; - } -} - -static void -pfr_reset_feedback(struct pfr_addr *addr, int size) -{ - struct pfr_addr *ad; - int i; - - for (i = 0, ad = addr; i < size; i++, ad++) - ad->pfra_fback = PFR_FB_NONE; -} - -static void -pfr_prepare_network(union sockaddr_union *sa, int af, int net) -{ - int i; - - bzero(sa, sizeof(*sa)); - if (af == AF_INET) { - sa->sin.sin_len = sizeof(sa->sin); - sa->sin.sin_family = AF_INET; - sa->sin.sin_addr.s_addr = net ? htonl(-1 << (32-net)) : 0; - } else if (af == AF_INET6) { - sa->sin6.sin6_len = sizeof(sa->sin6); - sa->sin6.sin6_family = AF_INET6; - for (i = 0; i < 4; i++) { - if (net <= 32) { - sa->sin6.sin6_addr.s6_addr32[i] = - net ? htonl(-1 << (32-net)) : 0; - break; - } - sa->sin6.sin6_addr.s6_addr32[i] = 0xFFFFFFFF; - net -= 32; - } - } -} - -static int -pfr_route_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) -{ - union sockaddr_union mask; - struct radix_node *rn; - struct radix_node_head *head = NULL; - - bzero(ke->pfrke_node, sizeof(ke->pfrke_node)); - if (ke->pfrke_af == AF_INET) - head = kt->pfrkt_ip4; - else if (ke->pfrke_af == AF_INET6) - head = kt->pfrkt_ip6; - - if (KENTRY_NETWORK(ke)) { - pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); - rn = rn_addroute(&ke->pfrke_sa, &mask, head, ke->pfrke_node); - } else - rn = rn_addroute(&ke->pfrke_sa, NULL, head, ke->pfrke_node); - - return (rn == NULL ? -1 : 0); -} - -static int -pfr_unroute_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) -{ - union sockaddr_union mask; - struct radix_node *rn; - struct radix_node_head *head = NULL; - - if (ke->pfrke_af == AF_INET) - head = kt->pfrkt_ip4; - else if (ke->pfrke_af == AF_INET6) - head = kt->pfrkt_ip6; - - if (KENTRY_NETWORK(ke)) { - pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); - rn = rn_delete(&ke->pfrke_sa, &mask, head); - } else - rn = rn_delete(&ke->pfrke_sa, NULL, head); - - if (rn == NULL) { - printf("pfr_unroute_kentry: delete failed.\n"); - return (-1); - } - return (0); -} - -static void -pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke) -{ - bzero(ad, sizeof(*ad)); - if (ke == NULL) - return; - ad->pfra_af = ke->pfrke_af; - ad->pfra_net = ke->pfrke_net; - ad->pfra_not = ke->pfrke_not; - if (ad->pfra_af == AF_INET) - ad->pfra_ip4addr = ke->pfrke_sa.sin.sin_addr; - else if (ad->pfra_af == AF_INET6) - ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr; -} - -static int -pfr_walktree(struct radix_node *rn, void *arg) -{ - struct pfr_kentry *ke = (struct pfr_kentry *)rn; - struct pfr_walktree *w = arg; - - switch (w->pfrw_op) { - case PFRW_MARK: - ke->pfrke_mark = 0; - break; - case PFRW_SWEEP: - if (ke->pfrke_mark) - break; - /* FALLTHROUGH */ - case PFRW_ENQUEUE: - SLIST_INSERT_HEAD(w->pfrw_workq, ke, pfrke_workq); - w->pfrw_cnt++; - break; - case PFRW_GET_ADDRS: - if (w->pfrw_free-- > 0) { - pfr_copyout_addr(w->pfrw_addr, ke); - w->pfrw_addr++; - } - break; - case PFRW_GET_ASTATS: - if (w->pfrw_free-- > 0) { - struct pfr_astats as; - - pfr_copyout_addr(&as.pfras_a, ke); - - if (ke->pfrke_counters) { - bcopy(ke->pfrke_counters->pfrkc_packets, - as.pfras_packets, sizeof(as.pfras_packets)); - bcopy(ke->pfrke_counters->pfrkc_bytes, - as.pfras_bytes, sizeof(as.pfras_bytes)); - } else { - bzero(as.pfras_packets, sizeof(as.pfras_packets)); - bzero(as.pfras_bytes, sizeof(as.pfras_bytes)); - as.pfras_a.pfra_fback = PFR_FB_NOCOUNT; - } - as.pfras_tzero = ke->pfrke_tzero; - - bcopy(&as, w->pfrw_astats, sizeof(as)); - w->pfrw_astats++; - } - break; - case PFRW_POOL_GET: - if (ke->pfrke_not) - break; /* negative entries are ignored */ - if (!w->pfrw_cnt--) { - w->pfrw_kentry = ke; - return (1); /* finish search */ - } - break; - case PFRW_DYNADDR_UPDATE: - { - union sockaddr_union pfr_mask; - - if (ke->pfrke_af == AF_INET) { - if (w->pfrw_dyn->pfid_acnt4++ > 0) - break; - pfr_prepare_network(&pfr_mask, AF_INET, ke->pfrke_net); - w->pfrw_dyn->pfid_addr4 = *SUNION2PF(&ke->pfrke_sa, - AF_INET); - w->pfrw_dyn->pfid_mask4 = *SUNION2PF(&pfr_mask, - AF_INET); - } else if (ke->pfrke_af == AF_INET6){ - if (w->pfrw_dyn->pfid_acnt6++ > 0) - break; - pfr_prepare_network(&pfr_mask, AF_INET6, ke->pfrke_net); - w->pfrw_dyn->pfid_addr6 = *SUNION2PF(&ke->pfrke_sa, - AF_INET6); - w->pfrw_dyn->pfid_mask6 = *SUNION2PF(&pfr_mask, - AF_INET6); - } - break; - } - } - return (0); -} - -int -pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) -{ - struct pfr_ktableworkq workq; - struct pfr_ktable *p; - int xdel = 0; - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ALLRSETS); - if (pfr_fix_anchor(filter->pfrt_anchor)) - return (EINVAL); - if (pfr_table_count(filter, flags) < 0) - return (ENOENT); - - SLIST_INIT(&workq); - RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { - if (pfr_skip_table(filter, p, flags)) - continue; - if (!strcmp(p->pfrkt_anchor, PF_RESERVED_ANCHOR)) - continue; - if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) - continue; - p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - xdel++; - } - if (!(flags & PFR_FLAG_DUMMY)) - pfr_setflags_ktables(&workq); - if (ndel != NULL) - *ndel = xdel; - return (0); -} - -int -pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) -{ - struct pfr_ktableworkq addq, changeq; - struct pfr_ktable *p, *q, *r, key; - int i, rv, xadd = 0; - long tzero = time_second; - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - SLIST_INIT(&addq); - SLIST_INIT(&changeq); - for (i = 0; i < size; i++) { - bcopy(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t)); - if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK, - flags & PFR_FLAG_USERIOCTL)) - senderr(EINVAL); - key.pfrkt_flags |= PFR_TFLAG_ACTIVE; - p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); - if (p == NULL) { - p = pfr_create_ktable(&key.pfrkt_t, tzero, 1); - if (p == NULL) - senderr(ENOMEM); - SLIST_FOREACH(q, &addq, pfrkt_workq) { - if (!pfr_ktable_compare(p, q)) - goto _skip; - } - SLIST_INSERT_HEAD(&addq, p, pfrkt_workq); - xadd++; - if (!key.pfrkt_anchor[0]) - goto _skip; - - /* find or create root table */ - bzero(key.pfrkt_anchor, sizeof(key.pfrkt_anchor)); - r = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); - if (r != NULL) { - p->pfrkt_root = r; - goto _skip; - } - SLIST_FOREACH(q, &addq, pfrkt_workq) { - if (!pfr_ktable_compare(&key, q)) { - p->pfrkt_root = q; - goto _skip; - } - } - key.pfrkt_flags = 0; - r = pfr_create_ktable(&key.pfrkt_t, 0, 1); - if (r == NULL) - senderr(ENOMEM); - SLIST_INSERT_HEAD(&addq, r, pfrkt_workq); - p->pfrkt_root = r; - } else if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { - SLIST_FOREACH(q, &changeq, pfrkt_workq) - if (!pfr_ktable_compare(&key, q)) - goto _skip; - p->pfrkt_nflags = (p->pfrkt_flags & - ~PFR_TFLAG_USRMASK) | key.pfrkt_flags; - SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq); - xadd++; - } -_skip: - ; - } - if (!(flags & PFR_FLAG_DUMMY)) { - pfr_insert_ktables(&addq); - pfr_setflags_ktables(&changeq); - } else - pfr_destroy_ktables(&addq, 0); - if (nadd != NULL) - *nadd = xadd; - return (0); -_bad: - pfr_destroy_ktables(&addq, 0); - return (rv); -} - -int -pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) -{ - struct pfr_ktableworkq workq; - struct pfr_ktable *p, *q, key; - int i, xdel = 0; - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - SLIST_INIT(&workq); - for (i = 0; i < size; i++) { - bcopy(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t)); - if (pfr_validate_table(&key.pfrkt_t, 0, - flags & PFR_FLAG_USERIOCTL)) - return (EINVAL); - p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); - if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { - SLIST_FOREACH(q, &workq, pfrkt_workq) - if (!pfr_ktable_compare(p, q)) - goto _skip; - p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - xdel++; - } -_skip: - ; - } - - if (!(flags & PFR_FLAG_DUMMY)) - pfr_setflags_ktables(&workq); - if (ndel != NULL) - *ndel = xdel; - return (0); -} - -int -pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, - int flags) -{ - struct pfr_ktable *p; - int n, nn; - - PF_RULES_RASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS); - if (pfr_fix_anchor(filter->pfrt_anchor)) - return (EINVAL); - n = nn = pfr_table_count(filter, flags); - if (n < 0) - return (ENOENT); - if (n > *size) { - *size = n; - return (0); - } - RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { - if (pfr_skip_table(filter, p, flags)) - continue; - if (n-- <= 0) - continue; - bcopy(&p->pfrkt_t, tbl++, sizeof(*tbl)); - } - - KASSERT(n == 0, ("%s: corruption detected (%d)", __func__, n)); - - *size = nn; - return (0); -} - -int -pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, - int flags) -{ - struct pfr_ktable *p; - struct pfr_ktableworkq workq; - int n, nn; - long tzero = time_second; - - /* XXX PFR_FLAG_CLSTATS disabled */ - ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS); - if (pfr_fix_anchor(filter->pfrt_anchor)) - return (EINVAL); - n = nn = pfr_table_count(filter, flags); - if (n < 0) - return (ENOENT); - if (n > *size) { - *size = n; - return (0); - } - SLIST_INIT(&workq); - RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { - if (pfr_skip_table(filter, p, flags)) - continue; - if (n-- <= 0) - continue; - bcopy(&p->pfrkt_ts, tbl++, sizeof(*tbl)); - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - } - if (flags & PFR_FLAG_CLSTATS) - pfr_clstats_ktables(&workq, tzero, - flags & PFR_FLAG_ADDRSTOO); - - KASSERT(n == 0, ("%s: corruption detected (%d)", __func__, n)); - - *size = nn; - return (0); -} - -int -pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) -{ - struct pfr_ktableworkq workq; - struct pfr_ktable *p, key; - int i, xzero = 0; - long tzero = time_second; - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO); - SLIST_INIT(&workq); - for (i = 0; i < size; i++) { - bcopy(tbl + i, &key.pfrkt_t, sizeof(key.pfrkt_t)); - if (pfr_validate_table(&key.pfrkt_t, 0, 0)) - return (EINVAL); - p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); - if (p != NULL) { - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - xzero++; - } - } - if (!(flags & PFR_FLAG_DUMMY)) - pfr_clstats_ktables(&workq, tzero, flags & PFR_FLAG_ADDRSTOO); - if (nzero != NULL) - *nzero = xzero; - return (0); -} - -int -pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, - int *nchange, int *ndel, int flags) -{ - struct pfr_ktableworkq workq; - struct pfr_ktable *p, *q, key; - int i, xchange = 0, xdel = 0; - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - if ((setflag & ~PFR_TFLAG_USRMASK) || - (clrflag & ~PFR_TFLAG_USRMASK) || - (setflag & clrflag)) - return (EINVAL); - SLIST_INIT(&workq); - for (i = 0; i < size; i++) { - bcopy(tbl + i, &key.pfrkt_t, sizeof(key.pfrkt_t)); - if (pfr_validate_table(&key.pfrkt_t, 0, - flags & PFR_FLAG_USERIOCTL)) - return (EINVAL); - p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); - if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { - p->pfrkt_nflags = (p->pfrkt_flags | setflag) & - ~clrflag; - if (p->pfrkt_nflags == p->pfrkt_flags) - goto _skip; - SLIST_FOREACH(q, &workq, pfrkt_workq) - if (!pfr_ktable_compare(p, q)) - goto _skip; - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - if ((p->pfrkt_flags & PFR_TFLAG_PERSIST) && - (clrflag & PFR_TFLAG_PERSIST) && - !(p->pfrkt_flags & PFR_TFLAG_REFERENCED)) - xdel++; - else - xchange++; - } -_skip: - ; - } - if (!(flags & PFR_FLAG_DUMMY)) - pfr_setflags_ktables(&workq); - if (nchange != NULL) - *nchange = xchange; - if (ndel != NULL) - *ndel = xdel; - return (0); -} - -int -pfr_ina_begin(struct pfr_table *trs, u_int32_t *ticket, int *ndel, int flags) -{ - struct pfr_ktableworkq workq; - struct pfr_ktable *p; - struct pf_ruleset *rs; - int xdel = 0; - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - rs = pf_find_or_create_ruleset(trs->pfrt_anchor); - if (rs == NULL) - return (ENOMEM); - SLIST_INIT(&workq); - RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { - if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || - pfr_skip_table(trs, p, 0)) - continue; - p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE; - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - xdel++; - } - if (!(flags & PFR_FLAG_DUMMY)) { - pfr_setflags_ktables(&workq); - if (ticket != NULL) - *ticket = ++rs->tticket; - rs->topen = 1; - } else - pf_remove_if_empty_ruleset(rs); - if (ndel != NULL) - *ndel = xdel; - return (0); -} - -int -pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, - int *nadd, int *naddr, u_int32_t ticket, int flags) -{ - struct pfr_ktableworkq tableq; - struct pfr_kentryworkq addrq; - struct pfr_ktable *kt, *rt, *shadow, key; - struct pfr_kentry *p; - struct pfr_addr *ad; - struct pf_ruleset *rs; - int i, rv, xadd = 0, xaddr = 0; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO); - if (size && !(flags & PFR_FLAG_ADDRSTOO)) - return (EINVAL); - if (pfr_validate_table(tbl, PFR_TFLAG_USRMASK, - flags & PFR_FLAG_USERIOCTL)) - return (EINVAL); - rs = pf_find_ruleset(tbl->pfrt_anchor); - if (rs == NULL || !rs->topen || ticket != rs->tticket) - return (EBUSY); - tbl->pfrt_flags |= PFR_TFLAG_INACTIVE; - SLIST_INIT(&tableq); - kt = RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl); - if (kt == NULL) { - kt = pfr_create_ktable(tbl, 0, 1); - if (kt == NULL) - return (ENOMEM); - SLIST_INSERT_HEAD(&tableq, kt, pfrkt_workq); - xadd++; - if (!tbl->pfrt_anchor[0]) - goto _skip; - - /* find or create root table */ - bzero(&key, sizeof(key)); - strlcpy(key.pfrkt_name, tbl->pfrt_name, sizeof(key.pfrkt_name)); - rt = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); - if (rt != NULL) { - kt->pfrkt_root = rt; - goto _skip; - } - rt = pfr_create_ktable(&key.pfrkt_t, 0, 1); - if (rt == NULL) { - pfr_destroy_ktables(&tableq, 0); - return (ENOMEM); - } - SLIST_INSERT_HEAD(&tableq, rt, pfrkt_workq); - kt->pfrkt_root = rt; - } else if (!(kt->pfrkt_flags & PFR_TFLAG_INACTIVE)) - xadd++; -_skip: - shadow = pfr_create_ktable(tbl, 0, 0); - if (shadow == NULL) { - pfr_destroy_ktables(&tableq, 0); - return (ENOMEM); - } - SLIST_INIT(&addrq); - for (i = 0, ad = addr; i < size; i++, ad++) { - if (pfr_validate_addr(ad)) - senderr(EINVAL); - if (pfr_lookup_addr(shadow, ad, 1) != NULL) - continue; - p = pfr_create_kentry(ad); - if (p == NULL) - senderr(ENOMEM); - if (pfr_route_kentry(shadow, p)) { - pfr_destroy_kentry(p); - continue; - } - SLIST_INSERT_HEAD(&addrq, p, pfrke_workq); - xaddr++; - } - if (!(flags & PFR_FLAG_DUMMY)) { - if (kt->pfrkt_shadow != NULL) - pfr_destroy_ktable(kt->pfrkt_shadow, 1); - kt->pfrkt_flags |= PFR_TFLAG_INACTIVE; - pfr_insert_ktables(&tableq); - shadow->pfrkt_cnt = (flags & PFR_FLAG_ADDRSTOO) ? - xaddr : NO_ADDRESSES; - kt->pfrkt_shadow = shadow; - } else { - pfr_clean_node_mask(shadow, &addrq); - pfr_destroy_ktable(shadow, 0); - pfr_destroy_ktables(&tableq, 0); - pfr_destroy_kentries(&addrq); - } - if (nadd != NULL) - *nadd = xadd; - if (naddr != NULL) - *naddr = xaddr; - return (0); -_bad: - pfr_destroy_ktable(shadow, 0); - pfr_destroy_ktables(&tableq, 0); - pfr_destroy_kentries(&addrq); - return (rv); -} - -int -pfr_ina_rollback(struct pfr_table *trs, u_int32_t ticket, int *ndel, int flags) -{ - struct pfr_ktableworkq workq; - struct pfr_ktable *p; - struct pf_ruleset *rs; - int xdel = 0; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - rs = pf_find_ruleset(trs->pfrt_anchor); - if (rs == NULL || !rs->topen || ticket != rs->tticket) - return (0); - SLIST_INIT(&workq); - RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { - if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || - pfr_skip_table(trs, p, 0)) - continue; - p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE; - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - xdel++; - } - if (!(flags & PFR_FLAG_DUMMY)) { - pfr_setflags_ktables(&workq); - rs->topen = 0; - pf_remove_if_empty_ruleset(rs); - } - if (ndel != NULL) - *ndel = xdel; - return (0); -} - -int -pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd, - int *nchange, int flags) -{ - struct pfr_ktable *p, *q; - struct pfr_ktableworkq workq; - struct pf_ruleset *rs; - int xadd = 0, xchange = 0; - long tzero = time_second; - - PF_RULES_WASSERT(); - - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); - rs = pf_find_ruleset(trs->pfrt_anchor); - if (rs == NULL || !rs->topen || ticket != rs->tticket) - return (EBUSY); - - SLIST_INIT(&workq); - RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { - if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || - pfr_skip_table(trs, p, 0)) - continue; - SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); - if (p->pfrkt_flags & PFR_TFLAG_ACTIVE) - xchange++; - else - xadd++; - } - - if (!(flags & PFR_FLAG_DUMMY)) { - for (p = SLIST_FIRST(&workq); p != NULL; p = q) { - q = SLIST_NEXT(p, pfrkt_workq); - pfr_commit_ktable(p, tzero); - } - rs->topen = 0; - pf_remove_if_empty_ruleset(rs); - } - if (nadd != NULL) - *nadd = xadd; - if (nchange != NULL) - *nchange = xchange; - - return (0); -} - -static void -pfr_commit_ktable(struct pfr_ktable *kt, long tzero) -{ - struct pfr_ktable *shadow = kt->pfrkt_shadow; - int nflags; - - PF_RULES_WASSERT(); - - if (shadow->pfrkt_cnt == NO_ADDRESSES) { - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - pfr_clstats_ktable(kt, tzero, 1); - } else if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) { - /* kt might contain addresses */ - struct pfr_kentryworkq addrq, addq, changeq, delq, garbageq; - struct pfr_kentry *p, *q, *next; - struct pfr_addr ad; - - pfr_enqueue_addrs(shadow, &addrq, NULL, 0); - pfr_mark_addrs(kt); - SLIST_INIT(&addq); - SLIST_INIT(&changeq); - SLIST_INIT(&delq); - SLIST_INIT(&garbageq); - pfr_clean_node_mask(shadow, &addrq); - for (p = SLIST_FIRST(&addrq); p != NULL; p = next) { - next = SLIST_NEXT(p, pfrke_workq); /* XXX */ - pfr_copyout_addr(&ad, p); - q = pfr_lookup_addr(kt, &ad, 1); - if (q != NULL) { - if (q->pfrke_not != p->pfrke_not) - SLIST_INSERT_HEAD(&changeq, q, - pfrke_workq); - q->pfrke_mark = 1; - SLIST_INSERT_HEAD(&garbageq, p, pfrke_workq); - } else { - p->pfrke_tzero = tzero; - SLIST_INSERT_HEAD(&addq, p, pfrke_workq); - } - } - pfr_enqueue_addrs(kt, &delq, NULL, ENQUEUE_UNMARKED_ONLY); - pfr_insert_kentries(kt, &addq, tzero); - pfr_remove_kentries(kt, &delq); - pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); - pfr_destroy_kentries(&garbageq); - } else { - /* kt cannot contain addresses */ - SWAP(struct radix_node_head *, kt->pfrkt_ip4, - shadow->pfrkt_ip4); - SWAP(struct radix_node_head *, kt->pfrkt_ip6, - shadow->pfrkt_ip6); - SWAP(int, kt->pfrkt_cnt, shadow->pfrkt_cnt); - pfr_clstats_ktable(kt, tzero, 1); - } - nflags = ((shadow->pfrkt_flags & PFR_TFLAG_USRMASK) | - (kt->pfrkt_flags & PFR_TFLAG_SETMASK) | PFR_TFLAG_ACTIVE) - & ~PFR_TFLAG_INACTIVE; - pfr_destroy_ktable(shadow, 0); - kt->pfrkt_shadow = NULL; - pfr_setflags_ktable(kt, nflags); -} - -static int -pfr_validate_table(struct pfr_table *tbl, int allowedflags, int no_reserved) -{ - int i; - - if (!tbl->pfrt_name[0]) - return (-1); - if (no_reserved && !strcmp(tbl->pfrt_anchor, PF_RESERVED_ANCHOR)) - return (-1); - if (tbl->pfrt_name[PF_TABLE_NAME_SIZE-1]) - return (-1); - for (i = strlen(tbl->pfrt_name); i < PF_TABLE_NAME_SIZE; i++) - if (tbl->pfrt_name[i]) - return (-1); - if (pfr_fix_anchor(tbl->pfrt_anchor)) - return (-1); - if (tbl->pfrt_flags & ~allowedflags) - return (-1); - return (0); -} - -/* - * Rewrite anchors referenced by tables to remove slashes - * and check for validity. - */ -static int -pfr_fix_anchor(char *anchor) -{ - size_t siz = MAXPATHLEN; - int i; - - if (anchor[0] == '/') { - char *path; - int off; - - path = anchor; - off = 1; - while (*++path == '/') - off++; - bcopy(path, anchor, siz - off); - memset(anchor + siz - off, 0, off); - } - if (anchor[siz - 1]) - return (-1); - for (i = strlen(anchor); i < siz; i++) - if (anchor[i]) - return (-1); - return (0); -} - -static int -pfr_table_count(struct pfr_table *filter, int flags) -{ - struct pf_ruleset *rs; - - PF_RULES_ASSERT(); - - if (flags & PFR_FLAG_ALLRSETS) - return (pfr_ktable_cnt); - if (filter->pfrt_anchor[0]) { - rs = pf_find_ruleset(filter->pfrt_anchor); - return ((rs != NULL) ? rs->tables : -1); - } - return (pf_main_ruleset.tables); -} - -static int -pfr_skip_table(struct pfr_table *filter, struct pfr_ktable *kt, int flags) -{ - if (flags & PFR_FLAG_ALLRSETS) - return (0); - if (strcmp(filter->pfrt_anchor, kt->pfrkt_anchor)) - return (1); - return (0); -} - -static void -pfr_insert_ktables(struct pfr_ktableworkq *workq) -{ - struct pfr_ktable *p; - - SLIST_FOREACH(p, workq, pfrkt_workq) - pfr_insert_ktable(p); -} - -static void -pfr_insert_ktable(struct pfr_ktable *kt) -{ - - PF_RULES_WASSERT(); - - RB_INSERT(pfr_ktablehead, &pfr_ktables, kt); - pfr_ktable_cnt++; - if (kt->pfrkt_root != NULL) - if (!kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]++) - pfr_setflags_ktable(kt->pfrkt_root, - kt->pfrkt_root->pfrkt_flags|PFR_TFLAG_REFDANCHOR); -} - -static void -pfr_setflags_ktables(struct pfr_ktableworkq *workq) -{ - struct pfr_ktable *p, *q; - - for (p = SLIST_FIRST(workq); p; p = q) { - q = SLIST_NEXT(p, pfrkt_workq); - pfr_setflags_ktable(p, p->pfrkt_nflags); - } -} - -static void -pfr_setflags_ktable(struct pfr_ktable *kt, int newf) -{ - struct pfr_kentryworkq addrq; - - PF_RULES_WASSERT(); - - if (!(newf & PFR_TFLAG_REFERENCED) && - !(newf & PFR_TFLAG_PERSIST)) - newf &= ~PFR_TFLAG_ACTIVE; - if (!(newf & PFR_TFLAG_ACTIVE)) - newf &= ~PFR_TFLAG_USRMASK; - if (!(newf & PFR_TFLAG_SETMASK)) { - RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt); - if (kt->pfrkt_root != NULL) - if (!--kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]) - pfr_setflags_ktable(kt->pfrkt_root, - kt->pfrkt_root->pfrkt_flags & - ~PFR_TFLAG_REFDANCHOR); - pfr_destroy_ktable(kt, 1); - pfr_ktable_cnt--; - return; - } - if (!(newf & PFR_TFLAG_ACTIVE) && kt->pfrkt_cnt) { - pfr_enqueue_addrs(kt, &addrq, NULL, 0); - pfr_remove_kentries(kt, &addrq); - } - if (!(newf & PFR_TFLAG_INACTIVE) && kt->pfrkt_shadow != NULL) { - pfr_destroy_ktable(kt->pfrkt_shadow, 1); - kt->pfrkt_shadow = NULL; - } - kt->pfrkt_flags = newf; -} - -static void -pfr_clstats_ktables(struct pfr_ktableworkq *workq, long tzero, int recurse) -{ - struct pfr_ktable *p; - - SLIST_FOREACH(p, workq, pfrkt_workq) - pfr_clstats_ktable(p, tzero, recurse); -} - -static void -pfr_clstats_ktable(struct pfr_ktable *kt, long tzero, int recurse) -{ - struct pfr_kentryworkq addrq; - - if (recurse) { - pfr_enqueue_addrs(kt, &addrq, NULL, 0); - pfr_clstats_kentries(&addrq, tzero, 0); - } - bzero(kt->pfrkt_packets, sizeof(kt->pfrkt_packets)); - bzero(kt->pfrkt_bytes, sizeof(kt->pfrkt_bytes)); - kt->pfrkt_match = kt->pfrkt_nomatch = 0; - kt->pfrkt_tzero = tzero; -} - -static struct pfr_ktable * -pfr_create_ktable(struct pfr_table *tbl, long tzero, int attachruleset) -{ - struct pfr_ktable *kt; - struct pf_ruleset *rs; - - PF_RULES_WASSERT(); - - kt = malloc(sizeof(*kt), M_PFTABLE, M_NOWAIT|M_ZERO); - if (kt == NULL) - return (NULL); - kt->pfrkt_t = *tbl; - - if (attachruleset) { - rs = pf_find_or_create_ruleset(tbl->pfrt_anchor); - if (!rs) { - pfr_destroy_ktable(kt, 0); - return (NULL); - } - kt->pfrkt_rs = rs; - rs->tables++; - } - - if (!rn_inithead((void **)&kt->pfrkt_ip4, - offsetof(struct sockaddr_in, sin_addr) * 8) || - !rn_inithead((void **)&kt->pfrkt_ip6, - offsetof(struct sockaddr_in6, sin6_addr) * 8)) { - pfr_destroy_ktable(kt, 0); - return (NULL); - } - kt->pfrkt_tzero = tzero; - - return (kt); -} - -static void -pfr_destroy_ktables(struct pfr_ktableworkq *workq, int flushaddr) -{ - struct pfr_ktable *p, *q; - - for (p = SLIST_FIRST(workq); p; p = q) { - q = SLIST_NEXT(p, pfrkt_workq); - pfr_destroy_ktable(p, flushaddr); - } -} - -static void -pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) -{ - struct pfr_kentryworkq addrq; - - if (flushaddr) { - pfr_enqueue_addrs(kt, &addrq, NULL, 0); - pfr_clean_node_mask(kt, &addrq); - pfr_destroy_kentries(&addrq); - } - if (kt->pfrkt_ip4 != NULL) { - RADIX_NODE_HEAD_DESTROY(kt->pfrkt_ip4); - free((caddr_t)kt->pfrkt_ip4, M_RTABLE); - } - if (kt->pfrkt_ip6 != NULL) { - RADIX_NODE_HEAD_DESTROY(kt->pfrkt_ip6); - free((caddr_t)kt->pfrkt_ip6, M_RTABLE); - } - if (kt->pfrkt_shadow != NULL) - pfr_destroy_ktable(kt->pfrkt_shadow, flushaddr); - if (kt->pfrkt_rs != NULL) { - kt->pfrkt_rs->tables--; - pf_remove_if_empty_ruleset(kt->pfrkt_rs); - } - free(kt, M_PFTABLE); -} - -static 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 struct pfr_ktable * -pfr_lookup_table(struct pfr_table *tbl) -{ - /* struct pfr_ktable start like a struct pfr_table */ - return (RB_FIND(pfr_ktablehead, &pfr_ktables, - (struct pfr_ktable *)tbl)); -} - -int -pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) -{ - struct pfr_kentry *ke = NULL; - int match; - - PF_RULES_RASSERT(); - - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) - kt = kt->pfrkt_root; - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (0); - - switch (af) { -#ifdef INET - case AF_INET: - { - struct sockaddr_in sin; - - bzero(&sin, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = a->addr32[0]; - ke = (struct pfr_kentry *)rn_match(&sin, kt->pfrkt_ip4); - if (ke && KENTRY_RNF_ROOT(ke)) - ke = NULL; - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - { - struct sockaddr_in6 sin6; - - bzero(&sin6, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - bcopy(a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); - ke = (struct pfr_kentry *)rn_match(&sin6, kt->pfrkt_ip6); - if (ke && KENTRY_RNF_ROOT(ke)) - ke = NULL; - break; - } -#endif /* INET6 */ - } - match = (ke && !ke->pfrke_not); - if (match) - kt->pfrkt_match++; - else - kt->pfrkt_nomatch++; - return (match); -} - -void -pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, - u_int64_t len, int dir_out, int op_pass, int notrule) -{ - struct pfr_kentry *ke = NULL; - - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) - kt = kt->pfrkt_root; - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return; - - switch (af) { -#ifdef INET - case AF_INET: - { - struct sockaddr_in sin; - - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = a->addr32[0]; - ke = (struct pfr_kentry *)rn_match(&sin, kt->pfrkt_ip4); - if (ke && KENTRY_RNF_ROOT(ke)) - ke = NULL; - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - { - struct sockaddr_in6 sin6; - - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - bcopy(a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); - ke = (struct pfr_kentry *)rn_match(&sin6, kt->pfrkt_ip6); - if (ke && KENTRY_RNF_ROOT(ke)) - ke = NULL; - break; - } -#endif /* INET6 */ - default: - ; - } - if ((ke == NULL || ke->pfrke_not) != notrule) { - if (op_pass != PFR_OP_PASS) - printf("pfr_update_stats: assertion failed.\n"); - op_pass = PFR_OP_XPASS; - } - kt->pfrkt_packets[dir_out][op_pass]++; - kt->pfrkt_bytes[dir_out][op_pass] += len; - if (ke != NULL && op_pass != PFR_OP_XPASS && - (kt->pfrkt_flags & PFR_TFLAG_COUNTERS)) { - if (ke->pfrke_counters == NULL) - ke->pfrke_counters = uma_zalloc(V_pfr_kcounters_z, - M_NOWAIT | M_ZERO); - if (ke->pfrke_counters != NULL) { - ke->pfrke_counters->pfrkc_packets[dir_out][op_pass]++; - ke->pfrke_counters->pfrkc_bytes[dir_out][op_pass] += len; - } - } -} - -struct pfr_ktable * -pfr_attach_table(struct pf_ruleset *rs, char *name) -{ - struct pfr_ktable *kt, *rt; - struct pfr_table tbl; - struct pf_anchor *ac = rs->anchor; - - PF_RULES_WASSERT(); - - bzero(&tbl, sizeof(tbl)); - strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)); - if (ac != NULL) - strlcpy(tbl.pfrt_anchor, ac->path, sizeof(tbl.pfrt_anchor)); - kt = pfr_lookup_table(&tbl); - if (kt == NULL) { - kt = pfr_create_ktable(&tbl, time_second, 1); - if (kt == NULL) - return (NULL); - if (ac != NULL) { - bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor)); - rt = pfr_lookup_table(&tbl); - if (rt == NULL) { - rt = pfr_create_ktable(&tbl, 0, 1); - if (rt == NULL) { - pfr_destroy_ktable(kt, 0); - return (NULL); - } - pfr_insert_ktable(rt); - } - kt->pfrkt_root = rt; - } - pfr_insert_ktable(kt); - } - if (!kt->pfrkt_refcnt[PFR_REFCNT_RULE]++) - pfr_setflags_ktable(kt, kt->pfrkt_flags|PFR_TFLAG_REFERENCED); - return (kt); -} - -void -pfr_detach_table(struct pfr_ktable *kt) -{ - - PF_RULES_WASSERT(); - KASSERT(kt->pfrkt_refcnt[PFR_REFCNT_RULE] > 0, ("%s: refcount %d\n", - __func__, kt->pfrkt_refcnt[PFR_REFCNT_RULE])); - - if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE]) - pfr_setflags_ktable(kt, kt->pfrkt_flags&~PFR_TFLAG_REFERENCED); -} - -int -pfr_pool_get(struct pfr_ktable *kt, int *pidx, struct pf_addr *counter, - sa_family_t af) -{ - struct pf_addr *addr, *cur, *mask; - union sockaddr_union uaddr, umask; - struct pfr_kentry *ke, *ke2 = NULL; - int idx = -1, use_counter = 0; - - switch (af) { - case AF_INET: - uaddr.sin.sin_len = sizeof(struct sockaddr_in); - uaddr.sin.sin_family = AF_INET; - break; - case AF_INET6: - uaddr.sin6.sin6_len = sizeof(struct sockaddr_in6); - uaddr.sin6.sin6_family = AF_INET6; - break; - } - addr = SUNION2PF(&uaddr, af); - - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) - kt = kt->pfrkt_root; - if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return (-1); - - if (pidx != NULL) - idx = *pidx; - if (counter != NULL && idx >= 0) - use_counter = 1; - if (idx < 0) - idx = 0; - -_next_block: - ke = pfr_kentry_byidx(kt, idx, af); - if (ke == NULL) { - kt->pfrkt_nomatch++; - return (1); - } - pfr_prepare_network(&umask, af, ke->pfrke_net); - cur = SUNION2PF(&ke->pfrke_sa, af); - mask = SUNION2PF(&umask, af); - - if (use_counter) { - /* is supplied address within block? */ - if (!PF_MATCHA(0, cur, mask, counter, af)) { - /* no, go to next block in table */ - idx++; - use_counter = 0; - goto _next_block; - } - PF_ACPY(addr, counter, af); - } else { - /* use first address of block */ - PF_ACPY(addr, cur, af); - } - - if (!KENTRY_NETWORK(ke)) { - /* this is a single IP address - no possible nested block */ - PF_ACPY(counter, addr, af); - *pidx = idx; - kt->pfrkt_match++; - return (0); - } - for (;;) { - /* we don't want to use a nested block */ - switch (af) { - case AF_INET: - ke2 = (struct pfr_kentry *)rn_match(&uaddr, - kt->pfrkt_ip4); - break; - case AF_INET6: - ke2 = (struct pfr_kentry *)rn_match(&uaddr, - kt->pfrkt_ip6); - break; - } - /* no need to check KENTRY_RNF_ROOT() here */ - if (ke2 == ke) { - /* lookup return the same block - perfect */ - PF_ACPY(counter, addr, af); - *pidx = idx; - kt->pfrkt_match++; - return (0); - } - - /* we need to increase the counter past the nested block */ - pfr_prepare_network(&umask, AF_INET, ke2->pfrke_net); - PF_POOLMASK(addr, addr, SUNION2PF(&umask, af), &pfr_ffaddr, af); - PF_AINC(addr, af); - if (!PF_MATCHA(0, cur, mask, addr, af)) { - /* ok, we reached the end of our main block */ - /* go to next block in table */ - idx++; - use_counter = 0; - goto _next_block; - } - } -} - -static struct pfr_kentry * -pfr_kentry_byidx(struct pfr_ktable *kt, int idx, int af) -{ - struct pfr_walktree w; - - bzero(&w, sizeof(w)); - w.pfrw_op = PFRW_POOL_GET; - w.pfrw_cnt = idx; - - switch (af) { -#ifdef INET - case AF_INET: - kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); - return (w.pfrw_kentry); -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); - return (w.pfrw_kentry); -#endif /* INET6 */ - default: - return (NULL); - } -} - -void -pfr_dynaddr_update(struct pfr_ktable *kt, struct pfi_dynaddr *dyn) -{ - struct pfr_walktree w; - - bzero(&w, sizeof(w)); - w.pfrw_op = PFRW_DYNADDR_UPDATE; - w.pfrw_dyn = dyn; - - dyn->pfid_acnt4 = 0; - dyn->pfid_acnt6 = 0; - if (!dyn->pfid_af || dyn->pfid_af == AF_INET) - kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); - if (!dyn->pfid_af || dyn->pfid_af == AF_INET6) - kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); -} diff --git a/sys/contrib/pf/net/pfvar.h b/sys/contrib/pf/net/pfvar.h deleted file mode 100644 index ca4e449f9d362..0000000000000 --- a/sys/contrib/pf/net/pfvar.h +++ /dev/null @@ -1,1961 +0,0 @@ -/* $OpenBSD: pfvar.h,v 1.282 2009/01/29 15:12:28 pyr Exp $ */ - -/* - * Copyright (c) 2001 Daniel Hartmeier - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _NET_PFVAR_H_ -#define _NET_PFVAR_H_ - -#include -#include -#include -#include - -#include -#include - -#include - -#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) -#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) - -#define PF_MD5_DIGEST_LENGTH 16 -#ifdef MD5_DIGEST_LENGTH -#if PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH -#error -#endif -#endif - -enum { PF_INOUT, PF_IN, PF_OUT }; -enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, - PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER }; -enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, - PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX }; -enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, - PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG }; -enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY }; -enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, - PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, - PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; -enum { PF_GET_NONE, PF_GET_CLR_CNTR }; -enum { PF_SK_WIRE, PF_SK_STACK, PF_SK_BOTH }; - -/* - * Note about PFTM_*: real indices into pf_rule.timeout[] come before - * PFTM_MAX, special cases afterwards. See pf_state_expires(). - */ -enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, - PFTM_TCP_CLOSING, PFTM_TCP_FIN_WAIT, PFTM_TCP_CLOSED, - PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE, - PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, - PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, - PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, - PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, - PFTM_TS_DIFF, PFTM_MAX, PFTM_PURGE, PFTM_UNLINKED, - PFTM_UNTIL_PACKET }; - -/* PFTM default values */ -#define PFTM_TCP_FIRST_PACKET_VAL 120 /* First TCP packet */ -#define PFTM_TCP_OPENING_VAL 30 /* No response yet */ -#define PFTM_TCP_ESTABLISHED_VAL 24*60*60/* Established */ -#define PFTM_TCP_CLOSING_VAL 15 * 60 /* Half closed */ -#define PFTM_TCP_FIN_WAIT_VAL 45 /* Got both FINs */ -#define PFTM_TCP_CLOSED_VAL 90 /* Got a RST */ -#define PFTM_UDP_FIRST_PACKET_VAL 60 /* First UDP packet */ -#define PFTM_UDP_SINGLE_VAL 30 /* Unidirectional */ -#define PFTM_UDP_MULTIPLE_VAL 60 /* Bidirectional */ -#define PFTM_ICMP_FIRST_PACKET_VAL 20 /* First ICMP packet */ -#define PFTM_ICMP_ERROR_REPLY_VAL 10 /* Got error response */ -#define PFTM_OTHER_FIRST_PACKET_VAL 60 /* First packet */ -#define PFTM_OTHER_SINGLE_VAL 30 /* Unidirectional */ -#define PFTM_OTHER_MULTIPLE_VAL 60 /* Bidirectional */ -#define PFTM_FRAG_VAL 30 /* Fragment expire */ -#define PFTM_INTERVAL_VAL 10 /* Expire interval */ -#define PFTM_SRC_NODE_VAL 0 /* Source tracking */ -#define PFTM_TS_DIFF_VAL 30 /* Allowed TS diff */ - -enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; -enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; -#define PF_POOL_IDMASK 0x0f -enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, - PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; -enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, - PF_ADDR_TABLE, PF_ADDR_URPFFAILED, - PF_ADDR_RANGE }; -#define PF_POOL_TYPEMASK 0x0f -#define PF_POOL_STICKYADDR 0x20 -#define PF_WSCALE_FLAG 0x80 -#define PF_WSCALE_MASK 0x0f - -#define PF_LOG 0x01 -#define PF_LOG_ALL 0x02 -#define PF_LOG_SOCKET_LOOKUP 0x04 - -struct pf_addr { - union { - struct in_addr v4; - struct in6_addr v6; - u_int8_t addr8[16]; - u_int16_t addr16[8]; - u_int32_t addr32[4]; - } pfa; /* 128-bit address */ -#define v4 pfa.v4 -#define v6 pfa.v6 -#define addr8 pfa.addr8 -#define addr16 pfa.addr16 -#define addr32 pfa.addr32 -}; - -#define PF_TABLE_NAME_SIZE 32 - -#define PFI_AFLAG_NETWORK 0x01 -#define PFI_AFLAG_BROADCAST 0x02 -#define PFI_AFLAG_PEER 0x04 -#define PFI_AFLAG_MODEMASK 0x07 -#define PFI_AFLAG_NOALIAS 0x08 - -struct pf_addr_wrap { - union { - struct { - struct pf_addr addr; - struct pf_addr mask; - } a; - char ifname[IFNAMSIZ]; - char tblname[PF_TABLE_NAME_SIZE]; - } v; - union { - struct pfi_dynaddr *dyn; - struct pfr_ktable *tbl; - int dyncnt; - int tblcnt; - } p; - u_int8_t type; /* PF_ADDR_* */ - u_int8_t iflags; /* PFI_AFLAG_* */ -}; - -#ifdef _KERNEL - -struct pfi_dynaddr { - TAILQ_ENTRY(pfi_dynaddr) entry; - struct pf_addr pfid_addr4; - struct pf_addr pfid_mask4; - struct pf_addr pfid_addr6; - struct pf_addr pfid_mask6; - struct pfr_ktable *pfid_kt; - struct pfi_kif *pfid_kif; - int pfid_net; /* mask or 128 */ - int pfid_acnt4; /* address count IPv4 */ - int pfid_acnt6; /* address count IPv6 */ - sa_family_t pfid_af; /* rule af */ - u_int8_t pfid_iflags; /* PFI_AFLAG_* */ -}; - -/* - * Address manipulation macros - */ -#define HTONL(x) (x) = htonl((__uint32_t)(x)) -#define HTONS(x) (x) = htons((__uint16_t)(x)) -#define NTOHL(x) (x) = ntohl((__uint32_t)(x)) -#define NTOHS(x) (x) = ntohs((__uint16_t)(x)) - -#define PF_NAME "pf" - -#define PF_HASHROW_ASSERT(h) mtx_assert(&(h)->lock, MA_OWNED) -#define PF_HASHROW_LOCK(h) mtx_lock(&(h)->lock) -#define PF_HASHROW_UNLOCK(h) mtx_unlock(&(h)->lock) - -#define PF_STATE_LOCK(s) \ - do { \ - struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH(s)]; \ - PF_HASHROW_LOCK(_ih); \ - } while (0) - -#define PF_STATE_UNLOCK(s) \ - do { \ - struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH((s))]; \ - PF_HASHROW_UNLOCK(_ih); \ - } while (0) - -#ifdef INVARIANTS -#define PF_STATE_LOCK_ASSERT(s) \ - do { \ - struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH(s)]; \ - PF_HASHROW_ASSERT(_ih); \ - } while (0) -#else /* !INVARIANTS */ -#define PF_STATE_LOCK_ASSERT(s) do {} while (0) -#endif /* INVARIANTS */ - -extern struct mtx pf_unlnkdrules_mtx; -#define PF_UNLNKDRULES_LOCK() mtx_lock(&pf_unlnkdrules_mtx) -#define PF_UNLNKDRULES_UNLOCK() mtx_unlock(&pf_unlnkdrules_mtx) - -extern struct rwlock pf_rules_lock; -#define PF_RULES_RLOCK() rw_rlock(&pf_rules_lock) -#define PF_RULES_RUNLOCK() rw_runlock(&pf_rules_lock) -#define PF_RULES_WLOCK() rw_wlock(&pf_rules_lock) -#define PF_RULES_WUNLOCK() rw_wunlock(&pf_rules_lock) -#define PF_RULES_ASSERT() rw_assert(&pf_rules_lock, RA_LOCKED) -#define PF_RULES_RASSERT() rw_assert(&pf_rules_lock, RA_RLOCKED) -#define PF_RULES_WASSERT() rw_assert(&pf_rules_lock, RA_WLOCKED) - -#define PF_MODVER 1 -#define PFLOG_MODVER 1 -#define PFSYNC_MODVER 1 - -#define PFLOG_MINVER 1 -#define PFLOG_PREFVER PFLOG_MODVER -#define PFLOG_MAXVER 1 -#define PFSYNC_MINVER 1 -#define PFSYNC_PREFVER PFSYNC_MODVER -#define PFSYNC_MAXVER 1 - -#ifdef INET -#ifndef INET6 -#define PF_INET_ONLY -#endif /* ! INET6 */ -#endif /* INET */ - -#ifdef INET6 -#ifndef INET -#define PF_INET6_ONLY -#endif /* ! INET */ -#endif /* INET6 */ - -#ifdef INET -#ifdef INET6 -#define PF_INET_INET6 -#endif /* INET6 */ -#endif /* INET */ - -#else - -#define PF_INET_INET6 - -#endif /* _KERNEL */ - -/* Both IPv4 and IPv6 */ -#ifdef PF_INET_INET6 - -#define PF_AEQ(a, b, c) \ - ((c == AF_INET && (a)->addr32[0] == (b)->addr32[0]) || \ - ((a)->addr32[3] == (b)->addr32[3] && \ - (a)->addr32[2] == (b)->addr32[2] && \ - (a)->addr32[1] == (b)->addr32[1] && \ - (a)->addr32[0] == (b)->addr32[0])) \ - -#define PF_ANEQ(a, b, c) \ - ((c == AF_INET && (a)->addr32[0] != (b)->addr32[0]) || \ - ((a)->addr32[3] != (b)->addr32[3] || \ - (a)->addr32[2] != (b)->addr32[2] || \ - (a)->addr32[1] != (b)->addr32[1] || \ - (a)->addr32[0] != (b)->addr32[0])) \ - -#define PF_AZERO(a, c) \ - ((c == AF_INET && !(a)->addr32[0]) || \ - (!(a)->addr32[0] && !(a)->addr32[1] && \ - !(a)->addr32[2] && !(a)->addr32[3] )) \ - -#define PF_MATCHA(n, a, m, b, f) \ - pf_match_addr(n, a, m, b, f) - -#define PF_ACPY(a, b, f) \ - pf_addrcpy(a, b, f) - -#define PF_AINC(a, f) \ - pf_addr_inc(a, f) - -#define PF_POOLMASK(a, b, c, d, f) \ - pf_poolmask(a, b, c, d, f) - -#else - -/* Just IPv6 */ - -#ifdef PF_INET6_ONLY - -#define PF_AEQ(a, b, c) \ - ((a)->addr32[3] == (b)->addr32[3] && \ - (a)->addr32[2] == (b)->addr32[2] && \ - (a)->addr32[1] == (b)->addr32[1] && \ - (a)->addr32[0] == (b)->addr32[0]) \ - -#define PF_ANEQ(a, b, c) \ - ((a)->addr32[3] != (b)->addr32[3] || \ - (a)->addr32[2] != (b)->addr32[2] || \ - (a)->addr32[1] != (b)->addr32[1] || \ - (a)->addr32[0] != (b)->addr32[0]) \ - -#define PF_AZERO(a, c) \ - (!(a)->addr32[0] && \ - !(a)->addr32[1] && \ - !(a)->addr32[2] && \ - !(a)->addr32[3] ) \ - -#define PF_MATCHA(n, a, m, b, f) \ - pf_match_addr(n, a, m, b, f) - -#define PF_ACPY(a, b, f) \ - pf_addrcpy(a, b, f) - -#define PF_AINC(a, f) \ - pf_addr_inc(a, f) - -#define PF_POOLMASK(a, b, c, d, f) \ - pf_poolmask(a, b, c, d, f) - -#else - -/* Just IPv4 */ -#ifdef PF_INET_ONLY - -#define PF_AEQ(a, b, c) \ - ((a)->addr32[0] == (b)->addr32[0]) - -#define PF_ANEQ(a, b, c) \ - ((a)->addr32[0] != (b)->addr32[0]) - -#define PF_AZERO(a, c) \ - (!(a)->addr32[0]) - -#define PF_MATCHA(n, a, m, b, f) \ - pf_match_addr(n, a, m, b, f) - -#define PF_ACPY(a, b, f) \ - (a)->v4.s_addr = (b)->v4.s_addr - -#define PF_AINC(a, f) \ - do { \ - (a)->addr32[0] = htonl(ntohl((a)->addr32[0]) + 1); \ - } while (0) - -#define PF_POOLMASK(a, b, c, d, f) \ - do { \ - (a)->addr32[0] = ((b)->addr32[0] & (c)->addr32[0]) | \ - (((c)->addr32[0] ^ 0xffffffff ) & (d)->addr32[0]); \ - } while (0) - -#endif /* PF_INET_ONLY */ -#endif /* PF_INET6_ONLY */ -#endif /* PF_INET_INET6 */ - -/* - * XXX callers not FIB-aware in our version of pf yet. - * OpenBSD fixed it later it seems, 2010/05/07 13:33:16 claudio. - */ -#define PF_MISMATCHAW(aw, x, af, neg, ifp, rtid) \ - ( \ - (((aw)->type == PF_ADDR_NOROUTE && \ - pf_routable((x), (af), NULL, (rtid))) || \ - (((aw)->type == PF_ADDR_URPFFAILED && (ifp) != NULL && \ - pf_routable((x), (af), (ifp), (rtid))) || \ - ((aw)->type == PF_ADDR_TABLE && \ - !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ - ((aw)->type == PF_ADDR_DYNIFTL && \ - !pfi_match_addr((aw)->p.dyn, (x), (af))) || \ - ((aw)->type == PF_ADDR_RANGE && \ - !pf_match_addr_range(&(aw)->v.a.addr, \ - &(aw)->v.a.mask, (x), (af))) || \ - ((aw)->type == PF_ADDR_ADDRMASK && \ - !PF_AZERO(&(aw)->v.a.mask, (af)) && \ - !PF_MATCHA(0, &(aw)->v.a.addr, \ - &(aw)->v.a.mask, (x), (af))))) != \ - (neg) \ - ) - - -struct pf_rule_uid { - uid_t uid[2]; - u_int8_t op; -}; - -struct pf_rule_gid { - uid_t gid[2]; - u_int8_t op; -}; - -struct pf_rule_addr { - struct pf_addr_wrap addr; - u_int16_t port[2]; - u_int8_t neg; - u_int8_t port_op; -}; - -struct pf_pooladdr { - struct pf_addr_wrap addr; - TAILQ_ENTRY(pf_pooladdr) entries; - char ifname[IFNAMSIZ]; - struct pfi_kif *kif; -}; - -TAILQ_HEAD(pf_palist, pf_pooladdr); - -struct pf_poolhashkey { - union { - u_int8_t key8[16]; - u_int16_t key16[8]; - u_int32_t key32[4]; - } pfk; /* 128-bit hash key */ -#define key8 pfk.key8 -#define key16 pfk.key16 -#define key32 pfk.key32 -}; - -struct pf_pool { - struct pf_palist list; - struct pf_pooladdr *cur; - struct pf_poolhashkey key; - struct pf_addr counter; - int tblidx; - u_int16_t proxy_port[2]; - u_int8_t opts; -}; - - -/* A packed Operating System description for fingerprinting */ -typedef u_int32_t pf_osfp_t; -#define PF_OSFP_ANY ((pf_osfp_t)0) -#define PF_OSFP_UNKNOWN ((pf_osfp_t)-1) -#define PF_OSFP_NOMATCH ((pf_osfp_t)-2) - -struct pf_osfp_entry { - SLIST_ENTRY(pf_osfp_entry) fp_entry; - pf_osfp_t fp_os; - int fp_enflags; -#define PF_OSFP_EXPANDED 0x001 /* expanded entry */ -#define PF_OSFP_GENERIC 0x002 /* generic signature */ -#define PF_OSFP_NODETAIL 0x004 /* no p0f details */ -#define PF_OSFP_LEN 32 - char fp_class_nm[PF_OSFP_LEN]; - char fp_version_nm[PF_OSFP_LEN]; - char fp_subtype_nm[PF_OSFP_LEN]; -}; -#define PF_OSFP_ENTRY_EQ(a, b) \ - ((a)->fp_os == (b)->fp_os && \ - memcmp((a)->fp_class_nm, (b)->fp_class_nm, PF_OSFP_LEN) == 0 && \ - memcmp((a)->fp_version_nm, (b)->fp_version_nm, PF_OSFP_LEN) == 0 && \ - memcmp((a)->fp_subtype_nm, (b)->fp_subtype_nm, PF_OSFP_LEN) == 0) - -/* handle pf_osfp_t packing */ -#define _FP_RESERVED_BIT 1 /* For the special negative #defines */ -#define _FP_UNUSED_BITS 1 -#define _FP_CLASS_BITS 10 /* OS Class (Windows, Linux) */ -#define _FP_VERSION_BITS 10 /* OS version (95, 98, NT, 2.4.54, 3.2) */ -#define _FP_SUBTYPE_BITS 10 /* patch level (NT SP4, SP3, ECN patch) */ -#define PF_OSFP_UNPACK(osfp, class, version, subtype) do { \ - (class) = ((osfp) >> (_FP_VERSION_BITS+_FP_SUBTYPE_BITS)) & \ - ((1 << _FP_CLASS_BITS) - 1); \ - (version) = ((osfp) >> _FP_SUBTYPE_BITS) & \ - ((1 << _FP_VERSION_BITS) - 1);\ - (subtype) = (osfp) & ((1 << _FP_SUBTYPE_BITS) - 1); \ -} while(0) -#define PF_OSFP_PACK(osfp, class, version, subtype) do { \ - (osfp) = ((class) & ((1 << _FP_CLASS_BITS) - 1)) << (_FP_VERSION_BITS \ - + _FP_SUBTYPE_BITS); \ - (osfp) |= ((version) & ((1 << _FP_VERSION_BITS) - 1)) << \ - _FP_SUBTYPE_BITS; \ - (osfp) |= (subtype) & ((1 << _FP_SUBTYPE_BITS) - 1); \ -} while(0) - -/* the fingerprint of an OSes TCP SYN packet */ -typedef u_int64_t pf_tcpopts_t; -struct pf_os_fingerprint { - SLIST_HEAD(pf_osfp_enlist, pf_osfp_entry) fp_oses; /* list of matches */ - pf_tcpopts_t fp_tcpopts; /* packed TCP options */ - u_int16_t fp_wsize; /* TCP window size */ - u_int16_t fp_psize; /* ip->ip_len */ - u_int16_t fp_mss; /* TCP MSS */ - u_int16_t fp_flags; -#define PF_OSFP_WSIZE_MOD 0x0001 /* Window modulus */ -#define PF_OSFP_WSIZE_DC 0x0002 /* Window don't care */ -#define PF_OSFP_WSIZE_MSS 0x0004 /* Window multiple of MSS */ -#define PF_OSFP_WSIZE_MTU 0x0008 /* Window multiple of MTU */ -#define PF_OSFP_PSIZE_MOD 0x0010 /* packet size modulus */ -#define PF_OSFP_PSIZE_DC 0x0020 /* packet size don't care */ -#define PF_OSFP_WSCALE 0x0040 /* TCP window scaling */ -#define PF_OSFP_WSCALE_MOD 0x0080 /* TCP window scale modulus */ -#define PF_OSFP_WSCALE_DC 0x0100 /* TCP window scale dont-care */ -#define PF_OSFP_MSS 0x0200 /* TCP MSS */ -#define PF_OSFP_MSS_MOD 0x0400 /* TCP MSS modulus */ -#define PF_OSFP_MSS_DC 0x0800 /* TCP MSS dont-care */ -#define PF_OSFP_DF 0x1000 /* IPv4 don't fragment bit */ -#define PF_OSFP_TS0 0x2000 /* Zero timestamp */ -#define PF_OSFP_INET6 0x4000 /* IPv6 */ - u_int8_t fp_optcnt; /* TCP option count */ - u_int8_t fp_wscale; /* TCP window scaling */ - u_int8_t fp_ttl; /* IPv4 TTL */ -#define PF_OSFP_MAXTTL_OFFSET 40 -/* TCP options packing */ -#define PF_OSFP_TCPOPT_NOP 0x0 /* TCP NOP option */ -#define PF_OSFP_TCPOPT_WSCALE 0x1 /* TCP window scaling option */ -#define PF_OSFP_TCPOPT_MSS 0x2 /* TCP max segment size opt */ -#define PF_OSFP_TCPOPT_SACK 0x3 /* TCP SACK OK option */ -#define PF_OSFP_TCPOPT_TS 0x4 /* TCP timestamp option */ -#define PF_OSFP_TCPOPT_BITS 3 /* bits used by each option */ -#define PF_OSFP_MAX_OPTS \ - (sizeof(((struct pf_os_fingerprint *)0)->fp_tcpopts) * 8) \ - / PF_OSFP_TCPOPT_BITS - - SLIST_ENTRY(pf_os_fingerprint) fp_next; -}; - -struct pf_osfp_ioctl { - struct pf_osfp_entry fp_os; - pf_tcpopts_t fp_tcpopts; /* packed TCP options */ - u_int16_t fp_wsize; /* TCP window size */ - u_int16_t fp_psize; /* ip->ip_len */ - u_int16_t fp_mss; /* TCP MSS */ - u_int16_t fp_flags; - u_int8_t fp_optcnt; /* TCP option count */ - u_int8_t fp_wscale; /* TCP window scaling */ - u_int8_t fp_ttl; /* IPv4 TTL */ - - int fp_getnum; /* DIOCOSFPGET number */ -}; - - -union pf_rule_ptr { - struct pf_rule *ptr; - u_int32_t nr; -}; - -#define PF_ANCHOR_NAME_SIZE 64 - -struct pf_rule { - struct pf_rule_addr src; - struct pf_rule_addr dst; -#define PF_SKIP_IFP 0 -#define PF_SKIP_DIR 1 -#define PF_SKIP_AF 2 -#define PF_SKIP_PROTO 3 -#define PF_SKIP_SRC_ADDR 4 -#define PF_SKIP_SRC_PORT 5 -#define PF_SKIP_DST_ADDR 6 -#define PF_SKIP_DST_PORT 7 -#define PF_SKIP_COUNT 8 - union pf_rule_ptr skip[PF_SKIP_COUNT]; -#define PF_RULE_LABEL_SIZE 64 - char label[PF_RULE_LABEL_SIZE]; -#define PF_QNAME_SIZE 64 - char ifname[IFNAMSIZ]; - char qname[PF_QNAME_SIZE]; - char pqname[PF_QNAME_SIZE]; -#define PF_TAG_NAME_SIZE 64 - char tagname[PF_TAG_NAME_SIZE]; - char match_tagname[PF_TAG_NAME_SIZE]; - - char overload_tblname[PF_TABLE_NAME_SIZE]; - - TAILQ_ENTRY(pf_rule) entries; - struct pf_pool rpool; - - u_int64_t evaluations; - u_int64_t packets[2]; - u_int64_t bytes[2]; - - struct pfi_kif *kif; - struct pf_anchor *anchor; - struct pfr_ktable *overload_tbl; - - pf_osfp_t os_fingerprint; - - int rtableid; - u_int32_t timeout[PFTM_MAX]; - u_int32_t states_cur; - u_int32_t states_tot; - u_int32_t max_states; - u_int32_t src_nodes; - u_int32_t max_src_nodes; - u_int32_t max_src_states; - u_int32_t spare1; /* netgraph */ - u_int32_t max_src_conn; - struct { - u_int32_t limit; - u_int32_t seconds; - } max_src_conn_rate; - u_int32_t qid; - u_int32_t pqid; - u_int32_t rt_listid; - u_int32_t nr; - u_int32_t prob; - uid_t cuid; - pid_t cpid; - - u_int16_t return_icmp; - u_int16_t return_icmp6; - u_int16_t max_mss; - u_int16_t tag; - u_int16_t match_tag; - u_int16_t spare2; /* netgraph */ - - struct pf_rule_uid uid; - struct pf_rule_gid gid; - - u_int32_t rule_flag; - u_int8_t action; - u_int8_t direction; - u_int8_t log; - u_int8_t logif; - u_int8_t quick; - u_int8_t ifnot; - u_int8_t match_tag_not; - u_int8_t natpass; - -#define PF_STATE_NORMAL 0x1 -#define PF_STATE_MODULATE 0x2 -#define PF_STATE_SYNPROXY 0x3 - u_int8_t keep_state; - sa_family_t af; - u_int8_t proto; - u_int8_t type; - u_int8_t code; - u_int8_t flags; - u_int8_t flagset; - u_int8_t min_ttl; - u_int8_t allow_opts; - u_int8_t rt; - u_int8_t return_ttl; - u_int8_t tos; - u_int8_t set_tos; - u_int8_t anchor_relative; - u_int8_t anchor_wildcard; - -#define PF_FLUSH 0x01 -#define PF_FLUSH_GLOBAL 0x02 - u_int8_t flush; - - struct { - struct pf_addr addr; - u_int16_t port; - } divert; -}; - -/* rule flags */ -#define PFRULE_DROP 0x0000 -#define PFRULE_RETURNRST 0x0001 -#define PFRULE_FRAGMENT 0x0002 -#define PFRULE_RETURNICMP 0x0004 -#define PFRULE_RETURN 0x0008 -#define PFRULE_NOSYNC 0x0010 -#define PFRULE_SRCTRACK 0x0020 /* track source states */ -#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ -#define PFRULE_REFS 0x0080 /* rule has references */ - -/* scrub flags */ -#define PFRULE_NODF 0x0100 -#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ -#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ -#define PFRULE_RANDOMID 0x0800 -#define PFRULE_REASSEMBLE_TCP 0x1000 -#define PFRULE_SET_TOS 0x2000 - -/* rule flags again */ -#define PFRULE_IFBOUND 0x00010000 /* if-bound */ -#define PFRULE_STATESLOPPY 0x00020000 /* sloppy state tracking */ - -#define PFSTATE_HIWAT 10000 /* default state table size */ -#define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */ -#define PFSTATE_ADAPT_END 12000 /* default adaptive timeout end */ - - -struct pf_threshold { - u_int32_t limit; -#define PF_THRESHOLD_MULT 1000 -#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT - u_int32_t seconds; - u_int32_t count; - u_int32_t last; -}; - -struct pf_src_node { - LIST_ENTRY(pf_src_node) entry; - struct pf_addr addr; - struct pf_addr raddr; - union pf_rule_ptr rule; - struct pfi_kif *kif; - u_int64_t bytes[2]; - u_int64_t packets[2]; - u_int32_t states; - u_int32_t conn; - struct pf_threshold conn_rate; - u_int32_t creation; - u_int32_t expire; - sa_family_t af; - u_int8_t ruletype; -}; - -#define PFSNODE_HIWAT 10000 /* default source node table size */ - -struct pf_state_scrub { - struct timeval pfss_last; /* time received last packet */ - u_int32_t pfss_tsecr; /* last echoed timestamp */ - u_int32_t pfss_tsval; /* largest timestamp */ - u_int32_t pfss_tsval0; /* original timestamp */ - u_int16_t pfss_flags; -#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ -#define PFSS_PAWS 0x0010 /* stricter PAWS checks */ -#define PFSS_PAWS_IDLED 0x0020 /* was idle too long. no PAWS */ -#define PFSS_DATA_TS 0x0040 /* timestamp on data packets */ -#define PFSS_DATA_NOTS 0x0080 /* no timestamp on data packets */ - u_int8_t pfss_ttl; /* stashed TTL */ - u_int8_t pad; - u_int32_t pfss_ts_mod; /* timestamp modulation */ -}; - -struct pf_state_host { - struct pf_addr addr; - u_int16_t port; - u_int16_t pad; -}; - -struct pf_state_peer { - struct pf_state_scrub *scrub; /* state is scrubbed */ - u_int32_t seqlo; /* Max sequence number sent */ - u_int32_t seqhi; /* Max the other end ACKd + win */ - u_int32_t seqdiff; /* Sequence number modulator */ - u_int16_t max_win; /* largest window (pre scaling) */ - u_int16_t mss; /* Maximum segment size option */ - u_int8_t state; /* active state level */ - u_int8_t wscale; /* window scaling factor */ - u_int8_t tcp_est; /* Did we reach TCPS_ESTABLISHED */ - u_int8_t pad[1]; -}; - -/* Keep synced with struct pf_state_key. */ -struct pf_state_key_cmp { - struct pf_addr addr[2]; - u_int16_t port[2]; - sa_family_t af; - u_int8_t proto; - u_int8_t pad[2]; -}; - -struct pf_state_key { - struct pf_addr addr[2]; - u_int16_t port[2]; - sa_family_t af; - u_int8_t proto; - u_int8_t pad[2]; - - LIST_ENTRY(pf_state_key) entry; - TAILQ_HEAD(, pf_state) states[2]; -}; - -/* Keep synced with struct pf_state. */ -struct pf_state_cmp { - u_int64_t id; - u_int32_t creatorid; - u_int8_t direction; - u_int8_t pad[3]; -}; - -struct pf_state { - u_int64_t id; - u_int32_t creatorid; - u_int8_t direction; - u_int8_t pad[3]; - - u_int refs; - TAILQ_ENTRY(pf_state) sync_list; - TAILQ_ENTRY(pf_state) key_list[2]; - LIST_ENTRY(pf_state) entry; - struct pf_state_peer src; - struct pf_state_peer dst; - union pf_rule_ptr rule; - union pf_rule_ptr anchor; - union pf_rule_ptr nat_rule; - struct pf_addr rt_addr; - struct pf_state_key *key[2]; /* addresses stack and wire */ - struct pfi_kif *kif; - struct pfi_kif *rt_kif; - struct pf_src_node *src_node; - struct pf_src_node *nat_src_node; - u_int64_t packets[2]; - u_int64_t bytes[2]; - u_int32_t creation; - u_int32_t expire; - u_int32_t pfsync_time; - u_int16_t tag; - u_int8_t log; - u_int8_t state_flags; -#define PFSTATE_ALLOWOPTS 0x01 -#define PFSTATE_SLOPPY 0x02 -/* was PFSTATE_PFLOW 0x04 */ -#define PFSTATE_NOSYNC 0x08 -#define PFSTATE_ACK 0x10 - u_int8_t timeout; - u_int8_t sync_state; /* PFSYNC_S_x */ - - /* XXX */ - u_int8_t sync_updates; - u_int8_t _tail[3]; -}; - -/* - * Unified state structures for pulling states out of the kernel - * used by pfsync(4) and the pf(4) ioctl. - */ -struct pfsync_state_scrub { - u_int16_t pfss_flags; - u_int8_t pfss_ttl; /* stashed TTL */ -#define PFSYNC_SCRUB_FLAG_VALID 0x01 - u_int8_t scrub_flag; - u_int32_t pfss_ts_mod; /* timestamp modulation */ -} __packed; - -struct pfsync_state_peer { - struct pfsync_state_scrub scrub; /* state is scrubbed */ - u_int32_t seqlo; /* Max sequence number sent */ - u_int32_t seqhi; /* Max the other end ACKd + win */ - u_int32_t seqdiff; /* Sequence number modulator */ - u_int16_t max_win; /* largest window (pre scaling) */ - u_int16_t mss; /* Maximum segment size option */ - u_int8_t state; /* active state level */ - u_int8_t wscale; /* window scaling factor */ - u_int8_t pad[6]; -} __packed; - -struct pfsync_state_key { - struct pf_addr addr[2]; - u_int16_t port[2]; -}; - -struct pfsync_state { - u_int64_t id; - char ifname[IFNAMSIZ]; - struct pfsync_state_key key[2]; - struct pfsync_state_peer src; - struct pfsync_state_peer dst; - struct pf_addr rt_addr; - u_int32_t rule; - u_int32_t anchor; - u_int32_t nat_rule; - u_int32_t creation; - u_int32_t expire; - u_int32_t packets[2][2]; - u_int32_t bytes[2][2]; - u_int32_t creatorid; - sa_family_t af; - u_int8_t proto; - u_int8_t direction; - u_int8_t __spare[2]; - u_int8_t log; - u_int8_t state_flags; - u_int8_t timeout; - u_int8_t sync_flags; - u_int8_t updates; -} __packed; - -#ifdef _KERNEL -/* pfsync */ -typedef int pfsync_state_import_t(struct pfsync_state *, u_int8_t); -typedef void pfsync_insert_state_t(struct pf_state *); -typedef void pfsync_update_state_t(struct pf_state *); -typedef void pfsync_delete_state_t(struct pf_state *); -typedef void pfsync_clear_states_t(u_int32_t, const char *); -typedef int pfsync_defer_t(struct pf_state *, struct mbuf *); - -extern pfsync_state_import_t *pfsync_state_import_ptr; -extern pfsync_insert_state_t *pfsync_insert_state_ptr; -extern pfsync_update_state_t *pfsync_update_state_ptr; -extern pfsync_delete_state_t *pfsync_delete_state_ptr; -extern pfsync_clear_states_t *pfsync_clear_states_ptr; -extern pfsync_defer_t *pfsync_defer_ptr; - -void pfsync_state_export(struct pfsync_state *, - struct pf_state *); - -/* pflog */ -struct pf_ruleset; -struct pf_pdesc; -typedef int pflog_packet_t(struct pfi_kif *, struct mbuf *, sa_family_t, - u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, - struct pf_ruleset *, struct pf_pdesc *, int); -extern pflog_packet_t *pflog_packet_ptr; - -#define V_pf_end_threads VNET(pf_end_threads) -#endif /* _KERNEL */ - -#define PFSYNC_FLAG_SRCNODE 0x04 -#define PFSYNC_FLAG_NATSRCNODE 0x08 - -/* for copies to/from network byte order */ -/* ioctl interface also uses network byte order */ -#define pf_state_peer_hton(s,d) do { \ - (d)->seqlo = htonl((s)->seqlo); \ - (d)->seqhi = htonl((s)->seqhi); \ - (d)->seqdiff = htonl((s)->seqdiff); \ - (d)->max_win = htons((s)->max_win); \ - (d)->mss = htons((s)->mss); \ - (d)->state = (s)->state; \ - (d)->wscale = (s)->wscale; \ - if ((s)->scrub) { \ - (d)->scrub.pfss_flags = \ - htons((s)->scrub->pfss_flags & PFSS_TIMESTAMP); \ - (d)->scrub.pfss_ttl = (s)->scrub->pfss_ttl; \ - (d)->scrub.pfss_ts_mod = htonl((s)->scrub->pfss_ts_mod);\ - (d)->scrub.scrub_flag = PFSYNC_SCRUB_FLAG_VALID; \ - } \ -} while (0) - -#define pf_state_peer_ntoh(s,d) do { \ - (d)->seqlo = ntohl((s)->seqlo); \ - (d)->seqhi = ntohl((s)->seqhi); \ - (d)->seqdiff = ntohl((s)->seqdiff); \ - (d)->max_win = ntohs((s)->max_win); \ - (d)->mss = ntohs((s)->mss); \ - (d)->state = (s)->state; \ - (d)->wscale = (s)->wscale; \ - if ((s)->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID && \ - (d)->scrub != NULL) { \ - (d)->scrub->pfss_flags = \ - ntohs((s)->scrub.pfss_flags) & PFSS_TIMESTAMP; \ - (d)->scrub->pfss_ttl = (s)->scrub.pfss_ttl; \ - (d)->scrub->pfss_ts_mod = ntohl((s)->scrub.pfss_ts_mod);\ - } \ -} while (0) - -#define pf_state_counter_hton(s,d) do { \ - d[0] = htonl((s>>32)&0xffffffff); \ - d[1] = htonl(s&0xffffffff); \ -} while (0) - -#define pf_state_counter_from_pfsync(s) \ - (((u_int64_t)(s[0])<<32) | (u_int64_t)(s[1])) - -#define pf_state_counter_ntoh(s,d) do { \ - d = ntohl(s[0]); \ - d = d<<32; \ - d += ntohl(s[1]); \ -} while (0) - -TAILQ_HEAD(pf_rulequeue, pf_rule); - -struct pf_anchor; - -struct pf_ruleset { - struct { - struct pf_rulequeue queues[2]; - struct { - struct pf_rulequeue *ptr; - struct pf_rule **ptr_array; - u_int32_t rcount; - u_int32_t ticket; - int open; - } active, inactive; - } rules[PF_RULESET_MAX]; - struct pf_anchor *anchor; - u_int32_t tticket; - int tables; - int topen; -}; - -RB_HEAD(pf_anchor_global, pf_anchor); -RB_HEAD(pf_anchor_node, pf_anchor); -struct pf_anchor { - RB_ENTRY(pf_anchor) entry_global; - RB_ENTRY(pf_anchor) entry_node; - struct pf_anchor *parent; - struct pf_anchor_node children; - char name[PF_ANCHOR_NAME_SIZE]; - char path[MAXPATHLEN]; - struct pf_ruleset ruleset; - int refcnt; /* anchor rules */ - int match; -}; -RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); -RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); - -#define PF_RESERVED_ANCHOR "_pf" - -#define PFR_TFLAG_PERSIST 0x00000001 -#define PFR_TFLAG_CONST 0x00000002 -#define PFR_TFLAG_ACTIVE 0x00000004 -#define PFR_TFLAG_INACTIVE 0x00000008 -#define PFR_TFLAG_REFERENCED 0x00000010 -#define PFR_TFLAG_REFDANCHOR 0x00000020 -#define PFR_TFLAG_COUNTERS 0x00000040 -/* Adjust masks below when adding flags. */ -#define PFR_TFLAG_USRMASK (PFR_TFLAG_PERSIST | \ - PFR_TFLAG_CONST | \ - PFR_TFLAG_COUNTERS) -#define PFR_TFLAG_SETMASK (PFR_TFLAG_ACTIVE | \ - PFR_TFLAG_INACTIVE | \ - PFR_TFLAG_REFERENCED | \ - PFR_TFLAG_REFDANCHOR) -#define PFR_TFLAG_ALLMASK (PFR_TFLAG_PERSIST | \ - PFR_TFLAG_CONST | \ - PFR_TFLAG_ACTIVE | \ - PFR_TFLAG_INACTIVE | \ - PFR_TFLAG_REFERENCED | \ - PFR_TFLAG_REFDANCHOR | \ - PFR_TFLAG_COUNTERS) - -struct pfr_table { - char pfrt_anchor[MAXPATHLEN]; - char pfrt_name[PF_TABLE_NAME_SIZE]; - u_int32_t pfrt_flags; - u_int8_t pfrt_fback; -}; - -enum { PFR_FB_NONE, PFR_FB_MATCH, PFR_FB_ADDED, PFR_FB_DELETED, - PFR_FB_CHANGED, PFR_FB_CLEARED, PFR_FB_DUPLICATE, - PFR_FB_NOTMATCH, PFR_FB_CONFLICT, PFR_FB_NOCOUNT, PFR_FB_MAX }; - -struct pfr_addr { - union { - struct in_addr _pfra_ip4addr; - struct in6_addr _pfra_ip6addr; - } pfra_u; - u_int8_t pfra_af; - u_int8_t pfra_net; - u_int8_t pfra_not; - u_int8_t pfra_fback; -}; -#define pfra_ip4addr pfra_u._pfra_ip4addr -#define pfra_ip6addr pfra_u._pfra_ip6addr - -enum { PFR_DIR_IN, PFR_DIR_OUT, PFR_DIR_MAX }; -enum { PFR_OP_BLOCK, PFR_OP_PASS, PFR_OP_ADDR_MAX, PFR_OP_TABLE_MAX }; -#define PFR_OP_XPASS PFR_OP_ADDR_MAX - -struct pfr_astats { - struct pfr_addr pfras_a; - u_int64_t pfras_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfras_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - long pfras_tzero; -}; - -enum { PFR_REFCNT_RULE, PFR_REFCNT_ANCHOR, PFR_REFCNT_MAX }; - -struct pfr_tstats { - struct pfr_table pfrts_t; - u_int64_t pfrts_packets[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_bytes[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; - u_int64_t pfrts_match; - u_int64_t pfrts_nomatch; - long pfrts_tzero; - int pfrts_cnt; - int pfrts_refcnt[PFR_REFCNT_MAX]; -}; -#define pfrts_name pfrts_t.pfrt_name -#define pfrts_flags pfrts_t.pfrt_flags - -#ifndef _SOCKADDR_UNION_DEFINED -#define _SOCKADDR_UNION_DEFINED -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; -}; -#endif /* _SOCKADDR_UNION_DEFINED */ - -struct pfr_kcounters { - u_int64_t pfrkc_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; - u_int64_t pfrkc_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; -}; - -SLIST_HEAD(pfr_kentryworkq, pfr_kentry); -struct pfr_kentry { - struct radix_node pfrke_node[2]; - union sockaddr_union pfrke_sa; - SLIST_ENTRY(pfr_kentry) pfrke_workq; - struct pfr_kcounters *pfrke_counters; - long pfrke_tzero; - u_int8_t pfrke_af; - u_int8_t pfrke_net; - u_int8_t pfrke_not; - u_int8_t pfrke_mark; -}; - -SLIST_HEAD(pfr_ktableworkq, pfr_ktable); -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_ruleset *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_ts.pfrts_cnt -#define pfrkt_refcnt pfrkt_ts.pfrts_refcnt -#define pfrkt_packets pfrkt_ts.pfrts_packets -#define pfrkt_bytes pfrkt_ts.pfrts_bytes -#define pfrkt_match pfrkt_ts.pfrts_match -#define pfrkt_nomatch pfrkt_ts.pfrts_nomatch -#define pfrkt_tzero pfrkt_ts.pfrts_tzero - -/* keep synced with pfi_kif, used in RB_FIND */ -struct pfi_kif_cmp { - char pfik_name[IFNAMSIZ]; -}; - -struct pfi_kif { - char pfik_name[IFNAMSIZ]; - union { - RB_ENTRY(pfi_kif) _pfik_tree; - LIST_ENTRY(pfi_kif) _pfik_list; - } _pfik_glue; -#define pfik_tree _pfik_glue._pfik_tree -#define pfik_list _pfik_glue._pfik_list - u_int64_t pfik_packets[2][2][2]; - u_int64_t pfik_bytes[2][2][2]; - u_int32_t pfik_tzero; - u_int pfik_flags; - struct ifnet *pfik_ifp; - struct ifg_group *pfik_group; - u_int pfik_rulerefs; - TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; -}; - -#define PFI_IFLAG_REFS 0x0001 /* has state references */ -#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ - -struct pf_pdesc { - struct { - int done; - uid_t uid; - gid_t gid; - } lookup; - u_int64_t tot_len; /* Make Mickey money */ - union { - struct tcphdr *tcp; - struct udphdr *udp; - struct icmp *icmp; -#ifdef INET6 - struct icmp6_hdr *icmp6; -#endif /* INET6 */ - void *any; - } hdr; - - struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ - struct pf_addr *src; /* src address */ - struct pf_addr *dst; /* dst address */ - u_int16_t *sport; - u_int16_t *dport; - struct pf_mtag *pf_mtag; - - u_int32_t p_len; /* total length of payload */ - - u_int16_t *ip_sum; - u_int16_t *proto_sum; - u_int16_t flags; /* Let SCRUB trigger behavior in - * state code. Easier than tags */ -#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ -#define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */ - sa_family_t af; - u_int8_t proto; - u_int8_t tos; - u_int8_t dir; /* direction */ - u_int8_t sidx; /* key index for source */ - u_int8_t didx; /* key index for destination */ -}; - -/* flags for RDR options */ -#define PF_DPORT_RANGE 0x01 /* Dest port uses range */ -#define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ - -/* Reasons code for passing/dropping a packet */ -#define PFRES_MATCH 0 /* Explicit match of a rule */ -#define PFRES_BADOFF 1 /* Bad offset for pull_hdr */ -#define PFRES_FRAG 2 /* Dropping following fragment */ -#define PFRES_SHORT 3 /* Dropping short packet */ -#define PFRES_NORM 4 /* Dropping by normalizer */ -#define PFRES_MEMORY 5 /* Dropped due to lacking mem */ -#define PFRES_TS 6 /* Bad TCP Timestamp (RFC1323) */ -#define PFRES_CONGEST 7 /* Congestion (of ipintrq) */ -#define PFRES_IPOPTIONS 8 /* IP option */ -#define PFRES_PROTCKSUM 9 /* Protocol checksum invalid */ -#define PFRES_BADSTATE 10 /* State mismatch */ -#define PFRES_STATEINS 11 /* State insertion failure */ -#define PFRES_MAXSTATES 12 /* State limit */ -#define PFRES_SRCLIMIT 13 /* Source node/conn limit */ -#define PFRES_SYNPROXY 14 /* SYN proxy */ -#define PFRES_MAX 15 /* total+1 */ - -#define PFRES_NAMES { \ - "match", \ - "bad-offset", \ - "fragment", \ - "short", \ - "normalize", \ - "memory", \ - "bad-timestamp", \ - "congestion", \ - "ip-option", \ - "proto-cksum", \ - "state-mismatch", \ - "state-insert", \ - "state-limit", \ - "src-limit", \ - "synproxy", \ - NULL \ -} - -/* Counters for other things we want to keep track of */ -#define LCNT_STATES 0 /* states */ -#define LCNT_SRCSTATES 1 /* max-src-states */ -#define LCNT_SRCNODES 2 /* max-src-nodes */ -#define LCNT_SRCCONN 3 /* max-src-conn */ -#define LCNT_SRCCONNRATE 4 /* max-src-conn-rate */ -#define LCNT_OVERLOAD_TABLE 5 /* entry added to overload table */ -#define LCNT_OVERLOAD_FLUSH 6 /* state entries flushed */ -#define LCNT_MAX 7 /* total+1 */ - -#define LCNT_NAMES { \ - "max states per rule", \ - "max-src-states", \ - "max-src-nodes", \ - "max-src-conn", \ - "max-src-conn-rate", \ - "overload table insertion", \ - "overload flush states", \ - NULL \ -} - -/* UDP state enumeration */ -#define PFUDPS_NO_TRAFFIC 0 -#define PFUDPS_SINGLE 1 -#define PFUDPS_MULTIPLE 2 - -#define PFUDPS_NSTATES 3 /* number of state levels */ - -#define PFUDPS_NAMES { \ - "NO_TRAFFIC", \ - "SINGLE", \ - "MULTIPLE", \ - NULL \ -} - -/* Other protocol state enumeration */ -#define PFOTHERS_NO_TRAFFIC 0 -#define PFOTHERS_SINGLE 1 -#define PFOTHERS_MULTIPLE 2 - -#define PFOTHERS_NSTATES 3 /* number of state levels */ - -#define PFOTHERS_NAMES { \ - "NO_TRAFFIC", \ - "SINGLE", \ - "MULTIPLE", \ - NULL \ -} - -#define FCNT_STATE_SEARCH 0 -#define FCNT_STATE_INSERT 1 -#define FCNT_STATE_REMOVALS 2 -#define FCNT_MAX 3 - -#define SCNT_SRC_NODE_SEARCH 0 -#define SCNT_SRC_NODE_INSERT 1 -#define SCNT_SRC_NODE_REMOVALS 2 -#define SCNT_MAX 3 - -#define ACTION_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - } while (0) - -#define REASON_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - if (x < PFRES_MAX) \ - V_pf_status.counters[x]++; \ - } while (0) - -struct pf_status { - u_int64_t counters[PFRES_MAX]; - u_int64_t lcounters[LCNT_MAX]; /* limit counters */ - u_int64_t fcounters[FCNT_MAX]; - u_int64_t scounters[SCNT_MAX]; - u_int64_t pcounters[2][2][3]; - u_int64_t bcounters[2][2]; - u_int32_t running; - u_int32_t states; - u_int32_t src_nodes; - u_int32_t since; - u_int32_t debug; - u_int32_t hostid; - char ifname[IFNAMSIZ]; - u_int8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; -}; - -struct cbq_opts { - u_int minburst; - u_int maxburst; - u_int pktsize; - u_int maxpktsize; - u_int ns_per_byte; - u_int maxidle; - int minidle; - u_int offtime; - int flags; -}; - -struct priq_opts { - int flags; -}; - -struct hfsc_opts { - /* real-time service curve */ - u_int rtsc_m1; /* slope of the 1st segment in bps */ - u_int rtsc_d; /* the x-projection of m1 in msec */ - u_int rtsc_m2; /* slope of the 2nd segment in bps */ - /* link-sharing service curve */ - u_int lssc_m1; - u_int lssc_d; - u_int lssc_m2; - /* upper-limit service curve */ - u_int ulsc_m1; - u_int ulsc_d; - u_int ulsc_m2; - int flags; -}; - -struct pf_altq { - char ifname[IFNAMSIZ]; - - void *altq_disc; /* discipline-specific state */ - TAILQ_ENTRY(pf_altq) entries; - - /* scheduler spec */ - u_int8_t scheduler; /* scheduler type */ - u_int16_t tbrsize; /* tokenbucket regulator size */ - u_int32_t ifbandwidth; /* interface bandwidth */ - - /* queue spec */ - char qname[PF_QNAME_SIZE]; /* queue name */ - char parent[PF_QNAME_SIZE]; /* parent name */ - u_int32_t parent_qid; /* parent queue id */ - u_int32_t bandwidth; /* queue bandwidth */ - u_int8_t priority; /* priority */ - u_int8_t local_flags; /* dynamic interface */ -#define PFALTQ_FLAG_IF_REMOVED 0x01 - - u_int16_t qlimit; /* queue size limit */ - u_int16_t flags; /* misc flags */ - union { - struct cbq_opts cbq_opts; - struct priq_opts priq_opts; - struct hfsc_opts hfsc_opts; - } pq_u; - - u_int32_t qid; /* return value */ -}; - -struct pf_divert { - union { - struct in_addr ipv4; - struct in6_addr ipv6; - } addr; - u_int16_t port; -}; - -#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ -#define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ - -/* - * ioctl parameter structures - */ - -struct pfioc_pooladdr { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - u_int32_t r_num; - u_int8_t r_action; - u_int8_t r_last; - u_int8_t af; - char anchor[MAXPATHLEN]; - struct pf_pooladdr addr; -}; - -struct pfioc_rule { - u_int32_t action; - u_int32_t ticket; - u_int32_t pool_ticket; - u_int32_t nr; - char anchor[MAXPATHLEN]; - char anchor_call[MAXPATHLEN]; - struct pf_rule rule; -}; - -struct pfioc_natlook { - struct pf_addr saddr; - struct pf_addr daddr; - struct pf_addr rsaddr; - struct pf_addr rdaddr; - u_int16_t sport; - u_int16_t dport; - u_int16_t rsport; - u_int16_t rdport; - sa_family_t af; - u_int8_t proto; - u_int8_t direction; -}; - -struct pfioc_state { - struct pfsync_state state; -}; - -struct pfioc_src_node_kill { - sa_family_t psnk_af; - struct pf_rule_addr psnk_src; - struct pf_rule_addr psnk_dst; - u_int psnk_killed; -}; - -struct pfioc_state_kill { - struct pf_state_cmp psk_pfcmp; - sa_family_t psk_af; - int psk_proto; - struct pf_rule_addr psk_src; - struct pf_rule_addr psk_dst; - char psk_ifname[IFNAMSIZ]; - char psk_label[PF_RULE_LABEL_SIZE]; - u_int psk_killed; -}; - -struct pfioc_states { - int ps_len; - union { - caddr_t psu_buf; - struct pfsync_state *psu_states; - } ps_u; -#define ps_buf ps_u.psu_buf -#define ps_states ps_u.psu_states -}; - -struct pfioc_src_nodes { - int psn_len; - union { - caddr_t psu_buf; - struct pf_src_node *psu_src_nodes; - } psn_u; -#define psn_buf psn_u.psu_buf -#define psn_src_nodes psn_u.psu_src_nodes -}; - -struct pfioc_if { - char ifname[IFNAMSIZ]; -}; - -struct pfioc_tm { - int timeout; - int seconds; -}; - -struct pfioc_limit { - int index; - unsigned limit; -}; - -struct pfioc_altq { - u_int32_t action; - u_int32_t ticket; - u_int32_t nr; - struct pf_altq altq; -}; - -struct pfioc_qstats { - u_int32_t ticket; - u_int32_t nr; - void *buf; - int nbytes; - u_int8_t scheduler; -}; - -struct pfioc_ruleset { - u_int32_t nr; - char path[MAXPATHLEN]; - char name[PF_ANCHOR_NAME_SIZE]; -}; - -#define PF_RULESET_ALTQ (PF_RULESET_MAX) -#define PF_RULESET_TABLE (PF_RULESET_MAX+1) -struct pfioc_trans { - int size; /* number of elements */ - int esize; /* size of each element in bytes */ - struct pfioc_trans_e { - int rs_num; - char anchor[MAXPATHLEN]; - u_int32_t ticket; - } *array; -}; - -#define PFR_FLAG_ATOMIC 0x00000001 /* unused */ -#define PFR_FLAG_DUMMY 0x00000002 -#define PFR_FLAG_FEEDBACK 0x00000004 -#define PFR_FLAG_CLSTATS 0x00000008 -#define PFR_FLAG_ADDRSTOO 0x00000010 -#define PFR_FLAG_REPLACE 0x00000020 -#define PFR_FLAG_ALLRSETS 0x00000040 -#define PFR_FLAG_ALLMASK 0x0000007F -#ifdef _KERNEL -#define PFR_FLAG_USERIOCTL 0x10000000 -#endif - -struct pfioc_table { - struct pfr_table pfrio_table; - void *pfrio_buffer; - int pfrio_esize; - int pfrio_size; - int pfrio_size2; - int pfrio_nadd; - int pfrio_ndel; - int pfrio_nchange; - int pfrio_flags; - u_int32_t pfrio_ticket; -}; -#define pfrio_exists pfrio_nadd -#define pfrio_nzero pfrio_nadd -#define pfrio_nmatch pfrio_nadd -#define pfrio_naddr pfrio_size2 -#define pfrio_setflag pfrio_size2 -#define pfrio_clrflag pfrio_nadd - -struct pfioc_iface { - char pfiio_name[IFNAMSIZ]; - void *pfiio_buffer; - int pfiio_esize; - int pfiio_size; - int pfiio_nzero; - int pfiio_flags; -}; - - -/* - * ioctl operations - */ - -#define DIOCSTART _IO ('D', 1) -#define DIOCSTOP _IO ('D', 2) -#define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) -#define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) -#define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) -/* XXX cut 8 - 17 */ -#define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) -#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) -#define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) -#define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) -#define DIOCCLRSTATUS _IO ('D', 22) -#define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) -#define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) -#define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) -#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) -/* XXX cut 26 - 28 */ -#define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) -#define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) -#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) -#define DIOCCLRRULECTRS _IO ('D', 38) -#define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) -#define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) -#define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) -#define DIOCSTARTALTQ _IO ('D', 42) -#define DIOCSTOPALTQ _IO ('D', 43) -#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) -#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) -#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) -#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) -#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) -#define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) -#define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) -#define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) -#define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) -#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) -/* XXX cut 55 - 57 */ -#define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) -#define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) -#define DIOCRCLRTABLES _IOWR('D', 60, struct pfioc_table) -#define DIOCRADDTABLES _IOWR('D', 61, struct pfioc_table) -#define DIOCRDELTABLES _IOWR('D', 62, struct pfioc_table) -#define DIOCRGETTABLES _IOWR('D', 63, struct pfioc_table) -#define DIOCRGETTSTATS _IOWR('D', 64, struct pfioc_table) -#define DIOCRCLRTSTATS _IOWR('D', 65, struct pfioc_table) -#define DIOCRCLRADDRS _IOWR('D', 66, struct pfioc_table) -#define DIOCRADDADDRS _IOWR('D', 67, struct pfioc_table) -#define DIOCRDELADDRS _IOWR('D', 68, struct pfioc_table) -#define DIOCRSETADDRS _IOWR('D', 69, struct pfioc_table) -#define DIOCRGETADDRS _IOWR('D', 70, struct pfioc_table) -#define DIOCRGETASTATS _IOWR('D', 71, struct pfioc_table) -#define DIOCRCLRASTATS _IOWR('D', 72, struct pfioc_table) -#define DIOCRTSTADDRS _IOWR('D', 73, struct pfioc_table) -#define DIOCRSETTFLAGS _IOWR('D', 74, struct pfioc_table) -#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table) -#define DIOCOSFPFLUSH _IO('D', 78) -#define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl) -#define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl) -#define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans) -#define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans) -#define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans) -#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes) -#define DIOCCLRSRCNODES _IO('D', 85) -#define DIOCSETHOSTID _IOWR('D', 86, u_int32_t) -#define DIOCIGETIFACES _IOWR('D', 87, struct pfioc_iface) -#define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) -#define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) -#define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) -struct pf_ifspeed { - char ifname[IFNAMSIZ]; - u_int32_t baudrate; -}; -#define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) - -#ifdef _KERNEL -struct pf_srchash { - LIST_HEAD(, pf_src_node) nodes; - struct mtx lock; -}; - -struct pf_keyhash { - LIST_HEAD(, pf_state_key) keys; - struct mtx lock; -}; - -struct pf_idhash { - LIST_HEAD(, pf_state) states; - struct mtx lock; -}; - -#define PF_HASHSIZ (32768) -VNET_DECLARE(struct pf_keyhash *, pf_keyhash); -VNET_DECLARE(struct pf_idhash *, pf_idhash); -VNET_DECLARE(u_long, pf_hashmask); -#define V_pf_keyhash VNET(pf_keyhash) -#define V_pf_idhash VNET(pf_idhash) -#define V_pf_hashmask VNET(pf_hashmask) -VNET_DECLARE(struct pf_srchash *, pf_srchash); -VNET_DECLARE(u_long, pf_srchashmask); -#define V_pf_srchash VNET(pf_srchash) -#define V_pf_srchashmask VNET(pf_srchashmask) - -#define PF_IDHASH(s) (be64toh((s)->id) % (V_pf_hashmask + 1)) - -VNET_DECLARE(void *, pf_swi_cookie); -#define V_pf_swi_cookie VNET(pf_swi_cookie) - -VNET_DECLARE(uint64_t, pf_stateid[MAXCPU]); -#define V_pf_stateid VNET(pf_stateid) - -TAILQ_HEAD(pf_altqqueue, pf_altq); -VNET_DECLARE(struct pf_altqqueue, pf_altqs[2]); -#define V_pf_altqs VNET(pf_altqs) -VNET_DECLARE(struct pf_palist, pf_pabuf); -#define V_pf_pabuf VNET(pf_pabuf) - -VNET_DECLARE(u_int32_t, ticket_altqs_active); -#define V_ticket_altqs_active VNET(ticket_altqs_active) -VNET_DECLARE(u_int32_t, ticket_altqs_inactive); -#define V_ticket_altqs_inactive VNET(ticket_altqs_inactive) -VNET_DECLARE(int, altqs_inactive_open); -#define V_altqs_inactive_open VNET(altqs_inactive_open) -VNET_DECLARE(u_int32_t, ticket_pabuf); -#define V_ticket_pabuf VNET(ticket_pabuf) -VNET_DECLARE(struct pf_altqqueue *, pf_altqs_active); -#define V_pf_altqs_active VNET(pf_altqs_active) -VNET_DECLARE(struct pf_altqqueue *, pf_altqs_inactive); -#define V_pf_altqs_inactive VNET(pf_altqs_inactive) - -VNET_DECLARE(struct pf_rulequeue, pf_unlinked_rules); -#define V_pf_unlinked_rules VNET(pf_unlinked_rules) - -void pf_initialize(void); -void pf_cleanup(void); - -struct pf_mtag *pf_get_mtag(struct mbuf *); - -extern void pf_calc_skip_steps(struct pf_rulequeue *); -#ifdef ALTQ -extern void pf_altq_ifnet_event(struct ifnet *, int); -#endif -VNET_DECLARE(uma_zone_t, pf_state_z); -#define V_pf_state_z VNET(pf_state_z) -VNET_DECLARE(uma_zone_t, pf_state_key_z); -#define V_pf_state_key_z VNET(pf_state_key_z) -VNET_DECLARE(uma_zone_t, pf_state_scrub_z); -#define V_pf_state_scrub_z VNET(pf_state_scrub_z) - -extern void pf_purge_thread(void *); -extern void pf_intr(void *); -extern void pf_purge_expired_src_nodes(void); - -extern int pf_unlink_state(struct pf_state *, u_int); -#define PF_ENTER_LOCKED 0x00000001 -#define PF_RETURN_LOCKED 0x00000002 -extern int pf_state_insert(struct pfi_kif *, - struct pf_state_key *, - struct pf_state_key *, - struct pf_state *); -extern void pf_free_state(struct pf_state *); - -static __inline u_int -pf_hashsrc(struct pf_addr *addr, sa_family_t af) -{ - u_int h; - -#define ADDR_HASH(a) ((a) ^ ((a) >> 16)) - - switch (af) { - case AF_INET: - h = ADDR_HASH(addr->v4.s_addr); - break; - case AF_INET6: - h = ADDR_HASH(addr->v6.__u6_addr.__u6_addr32[3]); - default: - panic("%s: unknown address family %u", __func__, af); - } -#undef ADDR_HASH - - return (h & V_pf_srchashmask); -} - -static __inline void -pf_ref_state(struct pf_state *s) -{ - - refcount_acquire(&s->refs); -} - -static __inline int -pf_release_state(struct pf_state *s) -{ - - if (refcount_release(&s->refs)) { - pf_free_state(s); - return (1); - } else - return (0); -} - -extern struct pf_state *pf_find_state_byid(uint64_t, uint32_t); -extern struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, - u_int, int *); -struct pf_src_node *pf_find_src_node(struct pf_addr *, struct pf_rule *, - sa_family_t, int); -extern void pf_print_state(struct pf_state *); -extern void pf_print_flags(u_int8_t); -extern u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, - u_int8_t); - -VNET_DECLARE(struct ifnet *, sync_ifp); -#define V_sync_ifp VNET(sync_ifp); -VNET_DECLARE(struct pf_rule, pf_default_rule); -#define V_pf_default_rule VNET(pf_default_rule) -extern void pf_addrcpy(struct pf_addr *, struct pf_addr *, - u_int8_t); -void pf_free_rule(struct pf_rule *); - -#ifdef INET -int pf_test(int, struct ifnet *, struct mbuf **, struct inpcb *); -#endif /* INET */ - -#ifdef INET6 -int pf_test6(int, struct ifnet *, struct mbuf **, struct inpcb *); -void pf_poolmask(struct pf_addr *, struct pf_addr*, - struct pf_addr *, struct pf_addr *, u_int8_t); -void pf_addr_inc(struct pf_addr *, sa_family_t); -#endif /* INET6 */ - -u_int32_t pf_new_isn(struct pf_state *); -void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, - sa_family_t); -void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); -void pf_send_deferred_syn(struct pf_state *); -int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, - struct pf_addr *, sa_family_t); -int pf_match_addr_range(struct pf_addr *, struct pf_addr *, - struct pf_addr *, sa_family_t); -int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); - -void pf_normalize_init(void); -void pf_normalize_cleanup(void); -int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, u_short *, - struct pf_pdesc *); -int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, u_short *, - struct pf_pdesc *); -int pf_normalize_tcp(int, struct pfi_kif *, struct mbuf *, int, int, void *, - struct pf_pdesc *); -void pf_normalize_tcp_cleanup(struct pf_state *); -int pf_normalize_tcp_init(struct mbuf *, int, struct pf_pdesc *, - struct tcphdr *, struct pf_state_peer *, struct pf_state_peer *); -int pf_normalize_tcp_stateful(struct mbuf *, int, struct pf_pdesc *, - u_short *, struct tcphdr *, struct pf_state *, - struct pf_state_peer *, struct pf_state_peer *, int *); -u_int32_t - pf_state_expires(const struct pf_state *); -void pf_purge_expired_fragments(void); -int pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *, - int); -int pf_socket_lookup(int, struct pf_pdesc *, struct mbuf *); -struct pf_state_key *pf_alloc_state_key(int); -void pfr_initialize(void); -void pfr_cleanup(void); -int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); -void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t, - u_int64_t, int, int, int); -int pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, sa_family_t); -void pfr_dynaddr_update(struct pfr_ktable *, struct pfi_dynaddr *); -struct pfr_ktable * - pfr_attach_table(struct pf_ruleset *, char *); -void pfr_detach_table(struct pfr_ktable *); -int pfr_clr_tables(struct pfr_table *, int *, int); -int pfr_add_tables(struct pfr_table *, int, int *, int); -int pfr_del_tables(struct pfr_table *, int, int *, int); -int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int); -int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); -int pfr_clr_tstats(struct pfr_table *, int, int *, int); -int pfr_set_tflags(struct pfr_table *, int, int, int, int *, int *, int); -int pfr_clr_addrs(struct pfr_table *, int *, int); -int pfr_insert_kentry(struct pfr_ktable *, struct pfr_addr *, long); -int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, - int); -int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, - int); -int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *, - int *, int *, int *, int, u_int32_t); -int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int); -int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int); -int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, - int); -int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, - int); -int pfr_ina_begin(struct pfr_table *, u_int32_t *, int *, int); -int pfr_ina_rollback(struct pfr_table *, u_int32_t, int *, int); -int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, int *, int); -int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, - int *, u_int32_t, int); - -MALLOC_DECLARE(PFI_MTYPE); -VNET_DECLARE(struct pfi_kif *, pfi_all); -#define V_pfi_all VNET(pfi_all) - -void pfi_initialize(void); -void pfi_cleanup(void); -void pfi_kif_ref(struct pfi_kif *); -void pfi_kif_unref(struct pfi_kif *); -struct pfi_kif *pfi_kif_find(const char *); -struct pfi_kif *pfi_kif_attach(struct pfi_kif *, const char *); -int pfi_kif_match(struct pfi_kif *, struct pfi_kif *); -void pfi_kif_purge(void); -int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, - sa_family_t); -int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); -void pfi_dynaddr_remove(struct pfi_dynaddr *); -void pfi_dynaddr_copyout(struct pf_addr_wrap *); -void pfi_update_status(const char *, struct pf_status *); -void pfi_get_ifaces(const char *, struct pfi_kif *, int *); -int pfi_set_flags(const char *, int); -int pfi_clear_flags(const char *, int); - -int pf_match_tag(struct mbuf *, struct pf_rule *, int *, int); -int pf_tag_packet(struct mbuf *, struct pf_pdesc *, int); -void pf_qid2qname(u_int32_t, char *); - -VNET_DECLARE(struct pf_status, pf_status); -#define V_pf_status VNET(pf_status) - -struct pf_limit { - uma_zone_t zone; - u_int limit; -}; -VNET_DECLARE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); -#define V_pf_limits VNET(pf_limits) - -#endif /* _KERNEL */ - -#ifdef _KERNEL -VNET_DECLARE(struct pf_anchor_global, pf_anchors); -#define V_pf_anchors VNET(pf_anchors) -VNET_DECLARE(struct pf_anchor, pf_main_anchor); -#define V_pf_main_anchor VNET(pf_main_anchor) -#define pf_main_ruleset V_pf_main_anchor.ruleset -#endif - -/* these ruleset functions can be linked into userland programs (pfctl) */ -int pf_get_ruleset_number(u_int8_t); -void pf_init_ruleset(struct pf_ruleset *); -int pf_anchor_setup(struct pf_rule *, - const struct pf_ruleset *, const char *); -int pf_anchor_copyout(const struct pf_ruleset *, - const struct pf_rule *, struct pfioc_rule *); -void pf_anchor_remove(struct pf_rule *); -void pf_remove_if_empty_ruleset(struct pf_ruleset *); -struct pf_ruleset *pf_find_ruleset(const char *); -struct pf_ruleset *pf_find_or_create_ruleset(const char *); -void pf_rs_initialize(void); - -/* The fingerprint functions can be linked into userland programs (tcpdump) */ -int pf_osfp_add(struct pf_osfp_ioctl *); -#ifdef _KERNEL -struct pf_osfp_enlist * - pf_osfp_fingerprint(struct pf_pdesc *, struct mbuf *, int, - const struct tcphdr *); -#endif /* _KERNEL */ -void pf_osfp_flush(void); -int pf_osfp_get(struct pf_osfp_ioctl *); -int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); - -#ifdef _KERNEL -void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); - -void pf_step_into_anchor(int *, struct pf_ruleset **, int, - struct pf_rule **, struct pf_rule **, int *); -int pf_step_out_of_anchor(int *, struct pf_ruleset **, - int, struct pf_rule **, struct pf_rule **, - int *); - -int pf_map_addr(u_int8_t, struct pf_rule *, - struct pf_addr *, struct pf_addr *, - struct pf_addr *, struct pf_src_node **); -struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, - int, int, struct pfi_kif *, struct pf_src_node **, - struct pf_state_key **, struct pf_state_key **, - struct pf_addr *, struct pf_addr *, - u_int16_t, u_int16_t); - -struct pf_state_key *pf_state_key_setup(struct pf_pdesc *, struct pf_addr *, - struct pf_addr *, u_int16_t, u_int16_t); -struct pf_state_key *pf_state_key_clone(struct pf_state_key *); -#endif /* _KERNEL */ - -#endif /* _NET_PFVAR_H_ */ diff --git a/sys/contrib/pf/netinet/in4_cksum.c b/sys/contrib/pf/netinet/in4_cksum.c deleted file mode 100644 index bf25baf97a0a1..0000000000000 --- a/sys/contrib/pf/netinet/in4_cksum.c +++ /dev/null @@ -1,120 +0,0 @@ -/* $FreeBSD$ */ -/* $OpenBSD: in4_cksum.c,v 1.7 2003/06/02 23:28:13 millert Exp $ */ -/* $KAME: in4_cksum.c,v 1.10 2001/11/30 10:06:15 itojun Exp $ */ -/* $NetBSD: in_cksum.c,v 1.13 1996/10/13 02:03:03 christos Exp $ */ - -/* - * Copyright (C) 1999 WIDE Project. - * 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 project 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 PROJECT 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 PROJECT 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. - */ - -/* - * Copyright (c) 1988, 1992, 1993 - * 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. - * - * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) -#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);} - -int in4_cksum(struct mbuf *, u_int8_t, int, int); - -int -in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len) -{ - union { - struct ipovly ipov; - u_int16_t w[10]; - } u; - union { - u_int16_t s[2]; - u_int32_t l; - } l_util; - - u_int16_t *w; - int psum; - int sum = 0; - - if (nxt != 0) { - /* pseudo header */ - if (off < sizeof(struct ipovly)) - panic("in4_cksum: offset too short"); - if (m->m_len < sizeof(struct ip)) - panic("in4_cksum: bad mbuf chain"); - bzero(&u.ipov, sizeof(u.ipov)); - u.ipov.ih_len = htons(len); - u.ipov.ih_pr = nxt; - u.ipov.ih_src = mtod(m, struct ip *)->ip_src; - u.ipov.ih_dst = mtod(m, struct ip *)->ip_dst; - w = u.w; - /* assumes sizeof(ipov) == 20 */ - sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; sum += w[4]; - sum += w[5]; sum += w[6]; sum += w[7]; sum += w[8]; sum += w[9]; - } - - psum = in_cksum_skip(m, len + off, off); - psum = ~psum & 0xffff; - sum += psum; - REDUCE; - return (~sum & 0xffff); -} diff --git a/sys/modules/dummynet/Makefile b/sys/modules/dummynet/Makefile index dd96e0eb62b8a..dfddbce893325 100644 --- a/sys/modules/dummynet/Makefile +++ b/sys/modules/dummynet/Makefile @@ -2,7 +2,7 @@ .include -.PATH: ${.CURDIR}/../../netinet/ipfw +.PATH: ${.CURDIR}/../../netpfil/ipfw KMOD= dummynet SRCS= ip_dummynet.c SRCS+= ip_dn_glue.c ip_dn_io.c diff --git a/sys/modules/ipfw/Makefile b/sys/modules/ipfw/Makefile index 575a6ce4895b5..48f9ce163747f 100644 --- a/sys/modules/ipfw/Makefile +++ b/sys/modules/ipfw/Makefile @@ -2,7 +2,7 @@ .include -.PATH: ${.CURDIR}/../../netinet/ipfw +.PATH: ${.CURDIR}/../../netpfil/ipfw KMOD= ipfw SRCS= ip_fw2.c ip_fw_pfil.c @@ -11,7 +11,6 @@ SRCS+= ip_fw_sockopt.c ip_fw_table.c SRCS+= opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h opt_ipsec.h CFLAGS+= -DIPFIREWALL -CFLAGS+= -I${.CURDIR}/../../contrib/pf # #If you want it verbose #CFLAGS+= -DIPFIREWALL_VERBOSE diff --git a/sys/modules/ipfw_nat/Makefile b/sys/modules/ipfw_nat/Makefile index 1c957abe9f704..e4638a65616fc 100644 --- a/sys/modules/ipfw_nat/Makefile +++ b/sys/modules/ipfw_nat/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -.PATH: ${.CURDIR}/../../netinet/ipfw +.PATH: ${.CURDIR}/../../netpfil/ipfw KMOD= ipfw_nat SRCS= ip_fw_nat.c diff --git a/sys/modules/pf/Makefile b/sys/modules/pf/Makefile index 2f941e1d90618..9dc6c0dac72f7 100644 --- a/sys/modules/pf/Makefile +++ b/sys/modules/pf/Makefile @@ -2,8 +2,7 @@ .include -.PATH: ${.CURDIR}/../../contrib/pf/net -.PATH: ${.CURDIR}/../../contrib/pf/netinet +.PATH: ${.CURDIR}/../../netpfil/pf KMOD= pf SRCS= pf.c pf_if.c pf_lb.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \ @@ -11,8 +10,6 @@ SRCS= pf.c pf_if.c pf_lb.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \ bus_if.h device_if.h \ opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h opt_global.h -CFLAGS+= -I${.CURDIR}/../../contrib/pf - .if !defined(KERNBUILDDIR) .if ${MK_INET_SUPPORT} != "no" opt_inet.h: diff --git a/sys/modules/pflog/Makefile b/sys/modules/pflog/Makefile index 0e8339e69049e..70a5234698a12 100644 --- a/sys/modules/pflog/Makefile +++ b/sys/modules/pflog/Makefile @@ -2,13 +2,11 @@ .include -.PATH: ${.CURDIR}/../../contrib/pf/net +.PATH: ${.CURDIR}/../../netpfil/pf KMOD= pflog SRCS= if_pflog.c \ opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h - -CFLAGS+= -I${.CURDIR}/../../contrib/pf SRCS+= bus_if.h device_if.h .if defined(KERNBUILDDIR) diff --git a/sys/modules/pfsync/Makefile b/sys/modules/pfsync/Makefile index 8c9bb1b6a7aa5..92c6edb2fd532 100644 --- a/sys/modules/pfsync/Makefile +++ b/sys/modules/pfsync/Makefile @@ -2,13 +2,11 @@ .include -.PATH: ${.CURDIR}/../../contrib/pf/net +.PATH: ${.CURDIR}/../../netpfil/pf KMOD= pfsync SRCS= if_pfsync.c \ opt_pf.h opt_inet.h opt_inet6.h - -CFLAGS+= -I${.CURDIR}/../../contrib/pf SRCS+= bus_if.h device_if.h .if defined(KERNBUILDDIR) diff --git a/sys/net/if_pflog.h b/sys/net/if_pflog.h new file mode 100644 index 0000000000000..0faeb7d46322c --- /dev/null +++ b/sys/net/if_pflog.h @@ -0,0 +1,66 @@ +/* $OpenBSD: if_pflog.h,v 1.13 2006/10/23 12:46:09 henning Exp $ */ +/* + * Copyright 2001 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_IF_PFLOG_H_ +#define _NET_IF_PFLOG_H_ + +#define PFLOGIFS_MAX 16 + +#define PFLOG_RULESET_NAME_SIZE 16 + +struct pfloghdr { + u_int8_t length; + sa_family_t af; + u_int8_t action; + u_int8_t reason; + char ifname[IFNAMSIZ]; + char ruleset[PFLOG_RULESET_NAME_SIZE]; + u_int32_t rulenr; + u_int32_t subrulenr; + uid_t uid; + pid_t pid; + uid_t rule_uid; + pid_t rule_pid; + u_int8_t dir; + u_int8_t pad[3]; +}; + +#define PFLOG_HDRLEN sizeof(struct pfloghdr) +/* minus pad, also used as a signature */ +#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad) + +#ifdef _KERNEL +struct pf_rule; +struct pf_ruleset; +struct pfi_kif; +struct pf_pdesc; + +#define PFLOG_PACKET(i,a,b,c,d,e,f,g,h,di) do { \ + if (pflog_packet_ptr != NULL) \ + pflog_packet_ptr(i,a,b,c,d,e,f,g,h,di); \ +} while (0) +#endif /* _KERNEL */ +#endif /* _NET_IF_PFLOG_H_ */ diff --git a/sys/net/if_pfsync.h b/sys/net/if_pfsync.h new file mode 100644 index 0000000000000..5e71dd8e46f5c --- /dev/null +++ b/sys/net/if_pfsync.h @@ -0,0 +1,298 @@ +/* $OpenBSD: if_pfsync.h,v 1.35 2008/06/29 08:42:15 mcbride Exp $ */ + +/* + * Copyright (c) 2001 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES 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 MIND, 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) 2008 David Gwynne + * + * 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. + */ + +#ifndef _NET_IF_PFSYNC_H_ +#define _NET_IF_PFSYNC_H_ + +#define PFSYNC_VERSION 5 +#define PFSYNC_DFLTTL 255 + +#define PFSYNC_ACT_CLR 0 /* clear all states */ +#define PFSYNC_ACT_INS 1 /* insert state */ +#define PFSYNC_ACT_INS_ACK 2 /* ack of insterted state */ +#define PFSYNC_ACT_UPD 3 /* update state */ +#define PFSYNC_ACT_UPD_C 4 /* "compressed" update state */ +#define PFSYNC_ACT_UPD_REQ 5 /* request "uncompressed" state */ +#define PFSYNC_ACT_DEL 6 /* delete state */ +#define PFSYNC_ACT_DEL_C 7 /* "compressed" delete state */ +#define PFSYNC_ACT_INS_F 8 /* insert fragment */ +#define PFSYNC_ACT_DEL_F 9 /* delete fragments */ +#define PFSYNC_ACT_BUS 10 /* bulk update status */ +#define PFSYNC_ACT_TDB 11 /* TDB replay counter update */ +#define PFSYNC_ACT_EOF 12 /* end of frame */ +#define PFSYNC_ACT_MAX 13 + +#define PFSYNC_ACTIONS "CLR ST", \ + "INS ST", \ + "INS ST ACK", \ + "UPD ST", \ + "UPD ST COMP", \ + "UPD ST REQ", \ + "DEL ST", \ + "DEL ST COMP", \ + "INS FR", \ + "DEL FR", \ + "BULK UPD STAT", \ + "TDB UPD", \ + "EOF" + +#define PFSYNC_HMAC_LEN 20 + +/* + * A pfsync frame is built from a header followed by several sections which + * are all prefixed with their own subheaders. Frames must be terminated with + * an EOF subheader. + * + * | ... | + * | IP header | + * +============================+ + * | pfsync_header | + * +----------------------------+ + * | pfsync_subheader | + * +----------------------------+ + * | first action fields | + * | ... | + * +----------------------------+ + * | pfsync_subheader | + * +----------------------------+ + * | second action fields | + * | ... | + * +----------------------------+ + * | EOF pfsync_subheader | + * +----------------------------+ + * | HMAC | + * +============================+ + */ + +/* + * Frame header + */ + +struct pfsync_header { + u_int8_t version; + u_int8_t _pad; + u_int16_t len; + u_int8_t pfcksum[PF_MD5_DIGEST_LENGTH]; +} __packed; + +/* + * Frame region subheader + */ + +struct pfsync_subheader { + u_int8_t action; + u_int8_t _pad; + u_int16_t count; +} __packed; + +/* + * CLR + */ + +struct pfsync_clr { + char ifname[IFNAMSIZ]; + u_int32_t creatorid; +} __packed; + +/* + * INS, UPD, DEL + */ + +/* these use struct pfsync_state in pfvar.h */ + +/* + * INS_ACK + */ + +struct pfsync_ins_ack { + u_int64_t id; + u_int32_t creatorid; +} __packed; + +/* + * UPD_C + */ + +struct pfsync_upd_c { + u_int64_t id; + struct pfsync_state_peer src; + struct pfsync_state_peer dst; + u_int32_t creatorid; + u_int32_t expire; + u_int8_t timeout; + u_int8_t _pad[3]; +} __packed; + +/* + * UPD_REQ + */ + +struct pfsync_upd_req { + u_int64_t id; + u_int32_t creatorid; +} __packed; + +/* + * DEL_C + */ + +struct pfsync_del_c { + u_int64_t id; + u_int32_t creatorid; +} __packed; + +/* + * INS_F, DEL_F + */ + +/* not implemented (yet) */ + +/* + * BUS + */ + +struct pfsync_bus { + u_int32_t creatorid; + u_int32_t endtime; + u_int8_t status; +#define PFSYNC_BUS_START 1 +#define PFSYNC_BUS_END 2 + u_int8_t _pad[3]; +} __packed; + +/* + * TDB + */ + +struct pfsync_tdb { + u_int32_t spi; + union sockaddr_union dst; + u_int32_t rpl; + u_int64_t cur_bytes; + u_int8_t sproto; + u_int8_t updates; + u_int8_t _pad[2]; +} __packed; + +/* + * EOF + */ + +struct pfsync_eof { + u_int8_t hmac[PFSYNC_HMAC_LEN]; +} __packed; + +#define PFSYNC_HDRLEN sizeof(struct pfsync_header) + + + +/* + * Names for PFSYNC sysctl objects + */ +#define PFSYNCCTL_STATS 1 /* PFSYNC stats */ +#define PFSYNCCTL_MAXID 2 + +#define PFSYNCCTL_NAMES { \ + { 0, 0 }, \ + { "stats", CTLTYPE_STRUCT }, \ +} + +struct pfsyncstats { + u_int64_t pfsyncs_ipackets; /* total input packets, IPv4 */ + u_int64_t pfsyncs_ipackets6; /* total input packets, IPv6 */ + u_int64_t pfsyncs_badif; /* not the right interface */ + u_int64_t pfsyncs_badttl; /* TTL is not PFSYNC_DFLTTL */ + u_int64_t pfsyncs_hdrops; /* packets shorter than hdr */ + u_int64_t pfsyncs_badver; /* bad (incl unsupp) version */ + u_int64_t pfsyncs_badact; /* bad action */ + u_int64_t pfsyncs_badlen; /* data length does not match */ + u_int64_t pfsyncs_badauth; /* bad authentication */ + u_int64_t pfsyncs_stale; /* stale state */ + u_int64_t pfsyncs_badval; /* bad values */ + u_int64_t pfsyncs_badstate; /* insert/lookup failed */ + + u_int64_t pfsyncs_opackets; /* total output packets, IPv4 */ + u_int64_t pfsyncs_opackets6; /* total output packets, IPv6 */ + u_int64_t pfsyncs_onomem; /* no memory for an mbuf */ + u_int64_t pfsyncs_oerrors; /* ip output error */ + + u_int64_t pfsyncs_iacts[PFSYNC_ACT_MAX]; + u_int64_t pfsyncs_oacts[PFSYNC_ACT_MAX]; +}; + +/* + * Configuration structure for SIOCSETPFSYNC SIOCGETPFSYNC + */ +struct pfsyncreq { + char pfsyncr_syncdev[IFNAMSIZ]; + struct in_addr pfsyncr_syncpeer; + int pfsyncr_maxupdates; + int pfsyncr_defer; +}; + +#define SIOCSETPFSYNC _IOW('i', 247, struct ifreq) +#define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq) + +#ifdef _KERNEL + +/* + * this shows where a pf state is with respect to the syncing. + */ +#define PFSYNC_S_INS 0x00 +#define PFSYNC_S_IACK 0x01 +#define PFSYNC_S_UPD 0x02 +#define PFSYNC_S_UPD_C 0x03 +#define PFSYNC_S_DEL 0x04 +#define PFSYNC_S_COUNT 0x05 + +#define PFSYNC_S_DEFER 0xfe +#define PFSYNC_S_NONE 0xff + +#define PFSYNC_SI_IOCTL 0x01 +#define PFSYNC_SI_CKSUM 0x02 +#define PFSYNC_SI_ACK 0x04 + +#endif /* _KERNEL */ + +#endif /* _NET_IF_PFSYNC_H_ */ diff --git a/sys/net/pf_mtag.h b/sys/net/pf_mtag.h new file mode 100644 index 0000000000000..baff00a699b9b --- /dev/null +++ b/sys/net/pf_mtag.h @@ -0,0 +1,62 @@ +/* $FreeBSD$ */ +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _NET_PF_MTAG_H_ +#define _NET_PF_MTAG_H_ + +#ifdef _KERNEL + +#define PF_TAG_GENERATED 0x01 +#define PF_TAG_FRAGCACHE 0x02 +#define PF_TAG_TRANSLATE_LOCALHOST 0x04 +#define PF_PACKET_LOOPED 0x08 +#define PF_FASTFWD_OURS_PRESENT 0x10 + +struct pf_mtag { + void *hdr; /* saved hdr pos in mbuf, for ECN */ + u_int32_t qid; /* queue id */ + u_int16_t tag; /* tag id */ + u_int8_t flags; + u_int8_t routed; +}; + +static __inline struct pf_mtag * +pf_find_mtag(struct mbuf *m) +{ + struct m_tag *mtag; + + if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) == NULL) + return (NULL); + + return ((struct pf_mtag *)(mtag + 1)); +} +#endif /* _KERNEL */ +#endif /* _NET_PF_MTAG_H_ */ diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h new file mode 100644 index 0000000000000..ca4e449f9d362 --- /dev/null +++ b/sys/net/pfvar.h @@ -0,0 +1,1961 @@ +/* $OpenBSD: pfvar.h,v 1.282 2009/01/29 15:12:28 pyr Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _NET_PFVAR_H_ +#define _NET_PFVAR_H_ + +#include +#include +#include +#include + +#include +#include + +#include + +#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) +#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) + +#define PF_MD5_DIGEST_LENGTH 16 +#ifdef MD5_DIGEST_LENGTH +#if PF_MD5_DIGEST_LENGTH != MD5_DIGEST_LENGTH +#error +#endif +#endif + +enum { PF_INOUT, PF_IN, PF_OUT }; +enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, + PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER }; +enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, + PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX }; +enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, + PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG }; +enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY }; +enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, + PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, + PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; +enum { PF_GET_NONE, PF_GET_CLR_CNTR }; +enum { PF_SK_WIRE, PF_SK_STACK, PF_SK_BOTH }; + +/* + * Note about PFTM_*: real indices into pf_rule.timeout[] come before + * PFTM_MAX, special cases afterwards. See pf_state_expires(). + */ +enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, + PFTM_TCP_CLOSING, PFTM_TCP_FIN_WAIT, PFTM_TCP_CLOSED, + PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE, + PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, + PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, + PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, + PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, + PFTM_TS_DIFF, PFTM_MAX, PFTM_PURGE, PFTM_UNLINKED, + PFTM_UNTIL_PACKET }; + +/* PFTM default values */ +#define PFTM_TCP_FIRST_PACKET_VAL 120 /* First TCP packet */ +#define PFTM_TCP_OPENING_VAL 30 /* No response yet */ +#define PFTM_TCP_ESTABLISHED_VAL 24*60*60/* Established */ +#define PFTM_TCP_CLOSING_VAL 15 * 60 /* Half closed */ +#define PFTM_TCP_FIN_WAIT_VAL 45 /* Got both FINs */ +#define PFTM_TCP_CLOSED_VAL 90 /* Got a RST */ +#define PFTM_UDP_FIRST_PACKET_VAL 60 /* First UDP packet */ +#define PFTM_UDP_SINGLE_VAL 30 /* Unidirectional */ +#define PFTM_UDP_MULTIPLE_VAL 60 /* Bidirectional */ +#define PFTM_ICMP_FIRST_PACKET_VAL 20 /* First ICMP packet */ +#define PFTM_ICMP_ERROR_REPLY_VAL 10 /* Got error response */ +#define PFTM_OTHER_FIRST_PACKET_VAL 60 /* First packet */ +#define PFTM_OTHER_SINGLE_VAL 30 /* Unidirectional */ +#define PFTM_OTHER_MULTIPLE_VAL 60 /* Bidirectional */ +#define PFTM_FRAG_VAL 30 /* Fragment expire */ +#define PFTM_INTERVAL_VAL 10 /* Expire interval */ +#define PFTM_SRC_NODE_VAL 0 /* Source tracking */ +#define PFTM_TS_DIFF_VAL 30 /* Allowed TS diff */ + +enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; +enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, + PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; +#define PF_POOL_IDMASK 0x0f +enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, + PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; +enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, + PF_ADDR_TABLE, PF_ADDR_URPFFAILED, + PF_ADDR_RANGE }; +#define PF_POOL_TYPEMASK 0x0f +#define PF_POOL_STICKYADDR 0x20 +#define PF_WSCALE_FLAG 0x80 +#define PF_WSCALE_MASK 0x0f + +#define PF_LOG 0x01 +#define PF_LOG_ALL 0x02 +#define PF_LOG_SOCKET_LOOKUP 0x04 + +struct pf_addr { + union { + struct in_addr v4; + struct in6_addr v6; + u_int8_t addr8[16]; + u_int16_t addr16[8]; + u_int32_t addr32[4]; + } pfa; /* 128-bit address */ +#define v4 pfa.v4 +#define v6 pfa.v6 +#define addr8 pfa.addr8 +#define addr16 pfa.addr16 +#define addr32 pfa.addr32 +}; + +#define PF_TABLE_NAME_SIZE 32 + +#define PFI_AFLAG_NETWORK 0x01 +#define PFI_AFLAG_BROADCAST 0x02 +#define PFI_AFLAG_PEER 0x04 +#define PFI_AFLAG_MODEMASK 0x07 +#define PFI_AFLAG_NOALIAS 0x08 + +struct pf_addr_wrap { + union { + struct { + struct pf_addr addr; + struct pf_addr mask; + } a; + char ifname[IFNAMSIZ]; + char tblname[PF_TABLE_NAME_SIZE]; + } v; + union { + struct pfi_dynaddr *dyn; + struct pfr_ktable *tbl; + int dyncnt; + int tblcnt; + } p; + u_int8_t type; /* PF_ADDR_* */ + u_int8_t iflags; /* PFI_AFLAG_* */ +}; + +#ifdef _KERNEL + +struct pfi_dynaddr { + TAILQ_ENTRY(pfi_dynaddr) entry; + struct pf_addr pfid_addr4; + struct pf_addr pfid_mask4; + struct pf_addr pfid_addr6; + struct pf_addr pfid_mask6; + struct pfr_ktable *pfid_kt; + struct pfi_kif *pfid_kif; + int pfid_net; /* mask or 128 */ + int pfid_acnt4; /* address count IPv4 */ + int pfid_acnt6; /* address count IPv6 */ + sa_family_t pfid_af; /* rule af */ + u_int8_t pfid_iflags; /* PFI_AFLAG_* */ +}; + +/* + * Address manipulation macros + */ +#define HTONL(x) (x) = htonl((__uint32_t)(x)) +#define HTONS(x) (x) = htons((__uint16_t)(x)) +#define NTOHL(x) (x) = ntohl((__uint32_t)(x)) +#define NTOHS(x) (x) = ntohs((__uint16_t)(x)) + +#define PF_NAME "pf" + +#define PF_HASHROW_ASSERT(h) mtx_assert(&(h)->lock, MA_OWNED) +#define PF_HASHROW_LOCK(h) mtx_lock(&(h)->lock) +#define PF_HASHROW_UNLOCK(h) mtx_unlock(&(h)->lock) + +#define PF_STATE_LOCK(s) \ + do { \ + struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH(s)]; \ + PF_HASHROW_LOCK(_ih); \ + } while (0) + +#define PF_STATE_UNLOCK(s) \ + do { \ + struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH((s))]; \ + PF_HASHROW_UNLOCK(_ih); \ + } while (0) + +#ifdef INVARIANTS +#define PF_STATE_LOCK_ASSERT(s) \ + do { \ + struct pf_idhash *_ih = &V_pf_idhash[PF_IDHASH(s)]; \ + PF_HASHROW_ASSERT(_ih); \ + } while (0) +#else /* !INVARIANTS */ +#define PF_STATE_LOCK_ASSERT(s) do {} while (0) +#endif /* INVARIANTS */ + +extern struct mtx pf_unlnkdrules_mtx; +#define PF_UNLNKDRULES_LOCK() mtx_lock(&pf_unlnkdrules_mtx) +#define PF_UNLNKDRULES_UNLOCK() mtx_unlock(&pf_unlnkdrules_mtx) + +extern struct rwlock pf_rules_lock; +#define PF_RULES_RLOCK() rw_rlock(&pf_rules_lock) +#define PF_RULES_RUNLOCK() rw_runlock(&pf_rules_lock) +#define PF_RULES_WLOCK() rw_wlock(&pf_rules_lock) +#define PF_RULES_WUNLOCK() rw_wunlock(&pf_rules_lock) +#define PF_RULES_ASSERT() rw_assert(&pf_rules_lock, RA_LOCKED) +#define PF_RULES_RASSERT() rw_assert(&pf_rules_lock, RA_RLOCKED) +#define PF_RULES_WASSERT() rw_assert(&pf_rules_lock, RA_WLOCKED) + +#define PF_MODVER 1 +#define PFLOG_MODVER 1 +#define PFSYNC_MODVER 1 + +#define PFLOG_MINVER 1 +#define PFLOG_PREFVER PFLOG_MODVER +#define PFLOG_MAXVER 1 +#define PFSYNC_MINVER 1 +#define PFSYNC_PREFVER PFSYNC_MODVER +#define PFSYNC_MAXVER 1 + +#ifdef INET +#ifndef INET6 +#define PF_INET_ONLY +#endif /* ! INET6 */ +#endif /* INET */ + +#ifdef INET6 +#ifndef INET +#define PF_INET6_ONLY +#endif /* ! INET */ +#endif /* INET6 */ + +#ifdef INET +#ifdef INET6 +#define PF_INET_INET6 +#endif /* INET6 */ +#endif /* INET */ + +#else + +#define PF_INET_INET6 + +#endif /* _KERNEL */ + +/* Both IPv4 and IPv6 */ +#ifdef PF_INET_INET6 + +#define PF_AEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] == (b)->addr32[0]) || \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0])) \ + +#define PF_ANEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] != (b)->addr32[0]) || \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0])) \ + +#define PF_AZERO(a, c) \ + ((c == AF_INET && !(a)->addr32[0]) || \ + (!(a)->addr32[0] && !(a)->addr32[1] && \ + !(a)->addr32[2] && !(a)->addr32[3] )) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv6 */ + +#ifdef PF_INET6_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0]) \ + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0]) \ + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0] && \ + !(a)->addr32[1] && \ + !(a)->addr32[2] && \ + !(a)->addr32[3] ) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv4 */ +#ifdef PF_INET_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[0] == (b)->addr32[0]) + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[0] != (b)->addr32[0]) + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0]) + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + (a)->v4.s_addr = (b)->v4.s_addr + +#define PF_AINC(a, f) \ + do { \ + (a)->addr32[0] = htonl(ntohl((a)->addr32[0]) + 1); \ + } while (0) + +#define PF_POOLMASK(a, b, c, d, f) \ + do { \ + (a)->addr32[0] = ((b)->addr32[0] & (c)->addr32[0]) | \ + (((c)->addr32[0] ^ 0xffffffff ) & (d)->addr32[0]); \ + } while (0) + +#endif /* PF_INET_ONLY */ +#endif /* PF_INET6_ONLY */ +#endif /* PF_INET_INET6 */ + +/* + * XXX callers not FIB-aware in our version of pf yet. + * OpenBSD fixed it later it seems, 2010/05/07 13:33:16 claudio. + */ +#define PF_MISMATCHAW(aw, x, af, neg, ifp, rtid) \ + ( \ + (((aw)->type == PF_ADDR_NOROUTE && \ + pf_routable((x), (af), NULL, (rtid))) || \ + (((aw)->type == PF_ADDR_URPFFAILED && (ifp) != NULL && \ + pf_routable((x), (af), (ifp), (rtid))) || \ + ((aw)->type == PF_ADDR_TABLE && \ + !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ + ((aw)->type == PF_ADDR_DYNIFTL && \ + !pfi_match_addr((aw)->p.dyn, (x), (af))) || \ + ((aw)->type == PF_ADDR_RANGE && \ + !pf_match_addr_range(&(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))) || \ + ((aw)->type == PF_ADDR_ADDRMASK && \ + !PF_AZERO(&(aw)->v.a.mask, (af)) && \ + !PF_MATCHA(0, &(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))))) != \ + (neg) \ + ) + + +struct pf_rule_uid { + uid_t uid[2]; + u_int8_t op; +}; + +struct pf_rule_gid { + uid_t gid[2]; + u_int8_t op; +}; + +struct pf_rule_addr { + struct pf_addr_wrap addr; + u_int16_t port[2]; + u_int8_t neg; + u_int8_t port_op; +}; + +struct pf_pooladdr { + struct pf_addr_wrap addr; + TAILQ_ENTRY(pf_pooladdr) entries; + char ifname[IFNAMSIZ]; + struct pfi_kif *kif; +}; + +TAILQ_HEAD(pf_palist, pf_pooladdr); + +struct pf_poolhashkey { + union { + u_int8_t key8[16]; + u_int16_t key16[8]; + u_int32_t key32[4]; + } pfk; /* 128-bit hash key */ +#define key8 pfk.key8 +#define key16 pfk.key16 +#define key32 pfk.key32 +}; + +struct pf_pool { + struct pf_palist list; + struct pf_pooladdr *cur; + struct pf_poolhashkey key; + struct pf_addr counter; + int tblidx; + u_int16_t proxy_port[2]; + u_int8_t opts; +}; + + +/* A packed Operating System description for fingerprinting */ +typedef u_int32_t pf_osfp_t; +#define PF_OSFP_ANY ((pf_osfp_t)0) +#define PF_OSFP_UNKNOWN ((pf_osfp_t)-1) +#define PF_OSFP_NOMATCH ((pf_osfp_t)-2) + +struct pf_osfp_entry { + SLIST_ENTRY(pf_osfp_entry) fp_entry; + pf_osfp_t fp_os; + int fp_enflags; +#define PF_OSFP_EXPANDED 0x001 /* expanded entry */ +#define PF_OSFP_GENERIC 0x002 /* generic signature */ +#define PF_OSFP_NODETAIL 0x004 /* no p0f details */ +#define PF_OSFP_LEN 32 + char fp_class_nm[PF_OSFP_LEN]; + char fp_version_nm[PF_OSFP_LEN]; + char fp_subtype_nm[PF_OSFP_LEN]; +}; +#define PF_OSFP_ENTRY_EQ(a, b) \ + ((a)->fp_os == (b)->fp_os && \ + memcmp((a)->fp_class_nm, (b)->fp_class_nm, PF_OSFP_LEN) == 0 && \ + memcmp((a)->fp_version_nm, (b)->fp_version_nm, PF_OSFP_LEN) == 0 && \ + memcmp((a)->fp_subtype_nm, (b)->fp_subtype_nm, PF_OSFP_LEN) == 0) + +/* handle pf_osfp_t packing */ +#define _FP_RESERVED_BIT 1 /* For the special negative #defines */ +#define _FP_UNUSED_BITS 1 +#define _FP_CLASS_BITS 10 /* OS Class (Windows, Linux) */ +#define _FP_VERSION_BITS 10 /* OS version (95, 98, NT, 2.4.54, 3.2) */ +#define _FP_SUBTYPE_BITS 10 /* patch level (NT SP4, SP3, ECN patch) */ +#define PF_OSFP_UNPACK(osfp, class, version, subtype) do { \ + (class) = ((osfp) >> (_FP_VERSION_BITS+_FP_SUBTYPE_BITS)) & \ + ((1 << _FP_CLASS_BITS) - 1); \ + (version) = ((osfp) >> _FP_SUBTYPE_BITS) & \ + ((1 << _FP_VERSION_BITS) - 1);\ + (subtype) = (osfp) & ((1 << _FP_SUBTYPE_BITS) - 1); \ +} while(0) +#define PF_OSFP_PACK(osfp, class, version, subtype) do { \ + (osfp) = ((class) & ((1 << _FP_CLASS_BITS) - 1)) << (_FP_VERSION_BITS \ + + _FP_SUBTYPE_BITS); \ + (osfp) |= ((version) & ((1 << _FP_VERSION_BITS) - 1)) << \ + _FP_SUBTYPE_BITS; \ + (osfp) |= (subtype) & ((1 << _FP_SUBTYPE_BITS) - 1); \ +} while(0) + +/* the fingerprint of an OSes TCP SYN packet */ +typedef u_int64_t pf_tcpopts_t; +struct pf_os_fingerprint { + SLIST_HEAD(pf_osfp_enlist, pf_osfp_entry) fp_oses; /* list of matches */ + pf_tcpopts_t fp_tcpopts; /* packed TCP options */ + u_int16_t fp_wsize; /* TCP window size */ + u_int16_t fp_psize; /* ip->ip_len */ + u_int16_t fp_mss; /* TCP MSS */ + u_int16_t fp_flags; +#define PF_OSFP_WSIZE_MOD 0x0001 /* Window modulus */ +#define PF_OSFP_WSIZE_DC 0x0002 /* Window don't care */ +#define PF_OSFP_WSIZE_MSS 0x0004 /* Window multiple of MSS */ +#define PF_OSFP_WSIZE_MTU 0x0008 /* Window multiple of MTU */ +#define PF_OSFP_PSIZE_MOD 0x0010 /* packet size modulus */ +#define PF_OSFP_PSIZE_DC 0x0020 /* packet size don't care */ +#define PF_OSFP_WSCALE 0x0040 /* TCP window scaling */ +#define PF_OSFP_WSCALE_MOD 0x0080 /* TCP window scale modulus */ +#define PF_OSFP_WSCALE_DC 0x0100 /* TCP window scale dont-care */ +#define PF_OSFP_MSS 0x0200 /* TCP MSS */ +#define PF_OSFP_MSS_MOD 0x0400 /* TCP MSS modulus */ +#define PF_OSFP_MSS_DC 0x0800 /* TCP MSS dont-care */ +#define PF_OSFP_DF 0x1000 /* IPv4 don't fragment bit */ +#define PF_OSFP_TS0 0x2000 /* Zero timestamp */ +#define PF_OSFP_INET6 0x4000 /* IPv6 */ + u_int8_t fp_optcnt; /* TCP option count */ + u_int8_t fp_wscale; /* TCP window scaling */ + u_int8_t fp_ttl; /* IPv4 TTL */ +#define PF_OSFP_MAXTTL_OFFSET 40 +/* TCP options packing */ +#define PF_OSFP_TCPOPT_NOP 0x0 /* TCP NOP option */ +#define PF_OSFP_TCPOPT_WSCALE 0x1 /* TCP window scaling option */ +#define PF_OSFP_TCPOPT_MSS 0x2 /* TCP max segment size opt */ +#define PF_OSFP_TCPOPT_SACK 0x3 /* TCP SACK OK option */ +#define PF_OSFP_TCPOPT_TS 0x4 /* TCP timestamp option */ +#define PF_OSFP_TCPOPT_BITS 3 /* bits used by each option */ +#define PF_OSFP_MAX_OPTS \ + (sizeof(((struct pf_os_fingerprint *)0)->fp_tcpopts) * 8) \ + / PF_OSFP_TCPOPT_BITS + + SLIST_ENTRY(pf_os_fingerprint) fp_next; +}; + +struct pf_osfp_ioctl { + struct pf_osfp_entry fp_os; + pf_tcpopts_t fp_tcpopts; /* packed TCP options */ + u_int16_t fp_wsize; /* TCP window size */ + u_int16_t fp_psize; /* ip->ip_len */ + u_int16_t fp_mss; /* TCP MSS */ + u_int16_t fp_flags; + u_int8_t fp_optcnt; /* TCP option count */ + u_int8_t fp_wscale; /* TCP window scaling */ + u_int8_t fp_ttl; /* IPv4 TTL */ + + int fp_getnum; /* DIOCOSFPGET number */ +}; + + +union pf_rule_ptr { + struct pf_rule *ptr; + u_int32_t nr; +}; + +#define PF_ANCHOR_NAME_SIZE 64 + +struct pf_rule { + struct pf_rule_addr src; + struct pf_rule_addr dst; +#define PF_SKIP_IFP 0 +#define PF_SKIP_DIR 1 +#define PF_SKIP_AF 2 +#define PF_SKIP_PROTO 3 +#define PF_SKIP_SRC_ADDR 4 +#define PF_SKIP_SRC_PORT 5 +#define PF_SKIP_DST_ADDR 6 +#define PF_SKIP_DST_PORT 7 +#define PF_SKIP_COUNT 8 + union pf_rule_ptr skip[PF_SKIP_COUNT]; +#define PF_RULE_LABEL_SIZE 64 + char label[PF_RULE_LABEL_SIZE]; +#define PF_QNAME_SIZE 64 + char ifname[IFNAMSIZ]; + char qname[PF_QNAME_SIZE]; + char pqname[PF_QNAME_SIZE]; +#define PF_TAG_NAME_SIZE 64 + char tagname[PF_TAG_NAME_SIZE]; + char match_tagname[PF_TAG_NAME_SIZE]; + + char overload_tblname[PF_TABLE_NAME_SIZE]; + + TAILQ_ENTRY(pf_rule) entries; + struct pf_pool rpool; + + u_int64_t evaluations; + u_int64_t packets[2]; + u_int64_t bytes[2]; + + struct pfi_kif *kif; + struct pf_anchor *anchor; + struct pfr_ktable *overload_tbl; + + pf_osfp_t os_fingerprint; + + int rtableid; + u_int32_t timeout[PFTM_MAX]; + u_int32_t states_cur; + u_int32_t states_tot; + u_int32_t max_states; + u_int32_t src_nodes; + u_int32_t max_src_nodes; + u_int32_t max_src_states; + u_int32_t spare1; /* netgraph */ + u_int32_t max_src_conn; + struct { + u_int32_t limit; + u_int32_t seconds; + } max_src_conn_rate; + u_int32_t qid; + u_int32_t pqid; + u_int32_t rt_listid; + u_int32_t nr; + u_int32_t prob; + uid_t cuid; + pid_t cpid; + + u_int16_t return_icmp; + u_int16_t return_icmp6; + u_int16_t max_mss; + u_int16_t tag; + u_int16_t match_tag; + u_int16_t spare2; /* netgraph */ + + struct pf_rule_uid uid; + struct pf_rule_gid gid; + + u_int32_t rule_flag; + u_int8_t action; + u_int8_t direction; + u_int8_t log; + u_int8_t logif; + u_int8_t quick; + u_int8_t ifnot; + u_int8_t match_tag_not; + u_int8_t natpass; + +#define PF_STATE_NORMAL 0x1 +#define PF_STATE_MODULATE 0x2 +#define PF_STATE_SYNPROXY 0x3 + u_int8_t keep_state; + sa_family_t af; + u_int8_t proto; + u_int8_t type; + u_int8_t code; + u_int8_t flags; + u_int8_t flagset; + u_int8_t min_ttl; + u_int8_t allow_opts; + u_int8_t rt; + u_int8_t return_ttl; + u_int8_t tos; + u_int8_t set_tos; + u_int8_t anchor_relative; + u_int8_t anchor_wildcard; + +#define PF_FLUSH 0x01 +#define PF_FLUSH_GLOBAL 0x02 + u_int8_t flush; + + struct { + struct pf_addr addr; + u_int16_t port; + } divert; +}; + +/* rule flags */ +#define PFRULE_DROP 0x0000 +#define PFRULE_RETURNRST 0x0001 +#define PFRULE_FRAGMENT 0x0002 +#define PFRULE_RETURNICMP 0x0004 +#define PFRULE_RETURN 0x0008 +#define PFRULE_NOSYNC 0x0010 +#define PFRULE_SRCTRACK 0x0020 /* track source states */ +#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ +#define PFRULE_REFS 0x0080 /* rule has references */ + +/* scrub flags */ +#define PFRULE_NODF 0x0100 +#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ +#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ +#define PFRULE_RANDOMID 0x0800 +#define PFRULE_REASSEMBLE_TCP 0x1000 +#define PFRULE_SET_TOS 0x2000 + +/* rule flags again */ +#define PFRULE_IFBOUND 0x00010000 /* if-bound */ +#define PFRULE_STATESLOPPY 0x00020000 /* sloppy state tracking */ + +#define PFSTATE_HIWAT 10000 /* default state table size */ +#define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */ +#define PFSTATE_ADAPT_END 12000 /* default adaptive timeout end */ + + +struct pf_threshold { + u_int32_t limit; +#define PF_THRESHOLD_MULT 1000 +#define PF_THRESHOLD_MAX 0xffffffff / PF_THRESHOLD_MULT + u_int32_t seconds; + u_int32_t count; + u_int32_t last; +}; + +struct pf_src_node { + LIST_ENTRY(pf_src_node) entry; + struct pf_addr addr; + struct pf_addr raddr; + union pf_rule_ptr rule; + struct pfi_kif *kif; + u_int64_t bytes[2]; + u_int64_t packets[2]; + u_int32_t states; + u_int32_t conn; + struct pf_threshold conn_rate; + u_int32_t creation; + u_int32_t expire; + sa_family_t af; + u_int8_t ruletype; +}; + +#define PFSNODE_HIWAT 10000 /* default source node table size */ + +struct pf_state_scrub { + struct timeval pfss_last; /* time received last packet */ + u_int32_t pfss_tsecr; /* last echoed timestamp */ + u_int32_t pfss_tsval; /* largest timestamp */ + u_int32_t pfss_tsval0; /* original timestamp */ + u_int16_t pfss_flags; +#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ +#define PFSS_PAWS 0x0010 /* stricter PAWS checks */ +#define PFSS_PAWS_IDLED 0x0020 /* was idle too long. no PAWS */ +#define PFSS_DATA_TS 0x0040 /* timestamp on data packets */ +#define PFSS_DATA_NOTS 0x0080 /* no timestamp on data packets */ + u_int8_t pfss_ttl; /* stashed TTL */ + u_int8_t pad; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +}; + +struct pf_state_host { + struct pf_addr addr; + u_int16_t port; + u_int16_t pad; +}; + +struct pf_state_peer { + struct pf_state_scrub *scrub; /* state is scrubbed */ + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int8_t tcp_est; /* Did we reach TCPS_ESTABLISHED */ + u_int8_t pad[1]; +}; + +/* Keep synced with struct pf_state_key. */ +struct pf_state_key_cmp { + struct pf_addr addr[2]; + u_int16_t port[2]; + sa_family_t af; + u_int8_t proto; + u_int8_t pad[2]; +}; + +struct pf_state_key { + struct pf_addr addr[2]; + u_int16_t port[2]; + sa_family_t af; + u_int8_t proto; + u_int8_t pad[2]; + + LIST_ENTRY(pf_state_key) entry; + TAILQ_HEAD(, pf_state) states[2]; +}; + +/* Keep synced with struct pf_state. */ +struct pf_state_cmp { + u_int64_t id; + u_int32_t creatorid; + u_int8_t direction; + u_int8_t pad[3]; +}; + +struct pf_state { + u_int64_t id; + u_int32_t creatorid; + u_int8_t direction; + u_int8_t pad[3]; + + u_int refs; + TAILQ_ENTRY(pf_state) sync_list; + TAILQ_ENTRY(pf_state) key_list[2]; + LIST_ENTRY(pf_state) entry; + struct pf_state_peer src; + struct pf_state_peer dst; + union pf_rule_ptr rule; + union pf_rule_ptr anchor; + union pf_rule_ptr nat_rule; + struct pf_addr rt_addr; + struct pf_state_key *key[2]; /* addresses stack and wire */ + struct pfi_kif *kif; + struct pfi_kif *rt_kif; + struct pf_src_node *src_node; + struct pf_src_node *nat_src_node; + u_int64_t packets[2]; + u_int64_t bytes[2]; + u_int32_t creation; + u_int32_t expire; + u_int32_t pfsync_time; + u_int16_t tag; + u_int8_t log; + u_int8_t state_flags; +#define PFSTATE_ALLOWOPTS 0x01 +#define PFSTATE_SLOPPY 0x02 +/* was PFSTATE_PFLOW 0x04 */ +#define PFSTATE_NOSYNC 0x08 +#define PFSTATE_ACK 0x10 + u_int8_t timeout; + u_int8_t sync_state; /* PFSYNC_S_x */ + + /* XXX */ + u_int8_t sync_updates; + u_int8_t _tail[3]; +}; + +/* + * Unified state structures for pulling states out of the kernel + * used by pfsync(4) and the pf(4) ioctl. + */ +struct pfsync_state_scrub { + u_int16_t pfss_flags; + u_int8_t pfss_ttl; /* stashed TTL */ +#define PFSYNC_SCRUB_FLAG_VALID 0x01 + u_int8_t scrub_flag; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +} __packed; + +struct pfsync_state_peer { + struct pfsync_state_scrub scrub; /* state is scrubbed */ + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int8_t pad[6]; +} __packed; + +struct pfsync_state_key { + struct pf_addr addr[2]; + u_int16_t port[2]; +}; + +struct pfsync_state { + u_int64_t id; + char ifname[IFNAMSIZ]; + struct pfsync_state_key key[2]; + struct pfsync_state_peer src; + struct pfsync_state_peer dst; + struct pf_addr rt_addr; + u_int32_t rule; + u_int32_t anchor; + u_int32_t nat_rule; + u_int32_t creation; + u_int32_t expire; + u_int32_t packets[2][2]; + u_int32_t bytes[2][2]; + u_int32_t creatorid; + sa_family_t af; + u_int8_t proto; + u_int8_t direction; + u_int8_t __spare[2]; + u_int8_t log; + u_int8_t state_flags; + u_int8_t timeout; + u_int8_t sync_flags; + u_int8_t updates; +} __packed; + +#ifdef _KERNEL +/* pfsync */ +typedef int pfsync_state_import_t(struct pfsync_state *, u_int8_t); +typedef void pfsync_insert_state_t(struct pf_state *); +typedef void pfsync_update_state_t(struct pf_state *); +typedef void pfsync_delete_state_t(struct pf_state *); +typedef void pfsync_clear_states_t(u_int32_t, const char *); +typedef int pfsync_defer_t(struct pf_state *, struct mbuf *); + +extern pfsync_state_import_t *pfsync_state_import_ptr; +extern pfsync_insert_state_t *pfsync_insert_state_ptr; +extern pfsync_update_state_t *pfsync_update_state_ptr; +extern pfsync_delete_state_t *pfsync_delete_state_ptr; +extern pfsync_clear_states_t *pfsync_clear_states_ptr; +extern pfsync_defer_t *pfsync_defer_ptr; + +void pfsync_state_export(struct pfsync_state *, + struct pf_state *); + +/* pflog */ +struct pf_ruleset; +struct pf_pdesc; +typedef int pflog_packet_t(struct pfi_kif *, struct mbuf *, sa_family_t, + u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, + struct pf_ruleset *, struct pf_pdesc *, int); +extern pflog_packet_t *pflog_packet_ptr; + +#define V_pf_end_threads VNET(pf_end_threads) +#endif /* _KERNEL */ + +#define PFSYNC_FLAG_SRCNODE 0x04 +#define PFSYNC_FLAG_NATSRCNODE 0x08 + +/* for copies to/from network byte order */ +/* ioctl interface also uses network byte order */ +#define pf_state_peer_hton(s,d) do { \ + (d)->seqlo = htonl((s)->seqlo); \ + (d)->seqhi = htonl((s)->seqhi); \ + (d)->seqdiff = htonl((s)->seqdiff); \ + (d)->max_win = htons((s)->max_win); \ + (d)->mss = htons((s)->mss); \ + (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ + if ((s)->scrub) { \ + (d)->scrub.pfss_flags = \ + htons((s)->scrub->pfss_flags & PFSS_TIMESTAMP); \ + (d)->scrub.pfss_ttl = (s)->scrub->pfss_ttl; \ + (d)->scrub.pfss_ts_mod = htonl((s)->scrub->pfss_ts_mod);\ + (d)->scrub.scrub_flag = PFSYNC_SCRUB_FLAG_VALID; \ + } \ +} while (0) + +#define pf_state_peer_ntoh(s,d) do { \ + (d)->seqlo = ntohl((s)->seqlo); \ + (d)->seqhi = ntohl((s)->seqhi); \ + (d)->seqdiff = ntohl((s)->seqdiff); \ + (d)->max_win = ntohs((s)->max_win); \ + (d)->mss = ntohs((s)->mss); \ + (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ + if ((s)->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID && \ + (d)->scrub != NULL) { \ + (d)->scrub->pfss_flags = \ + ntohs((s)->scrub.pfss_flags) & PFSS_TIMESTAMP; \ + (d)->scrub->pfss_ttl = (s)->scrub.pfss_ttl; \ + (d)->scrub->pfss_ts_mod = ntohl((s)->scrub.pfss_ts_mod);\ + } \ +} while (0) + +#define pf_state_counter_hton(s,d) do { \ + d[0] = htonl((s>>32)&0xffffffff); \ + d[1] = htonl(s&0xffffffff); \ +} while (0) + +#define pf_state_counter_from_pfsync(s) \ + (((u_int64_t)(s[0])<<32) | (u_int64_t)(s[1])) + +#define pf_state_counter_ntoh(s,d) do { \ + d = ntohl(s[0]); \ + d = d<<32; \ + d += ntohl(s[1]); \ +} while (0) + +TAILQ_HEAD(pf_rulequeue, pf_rule); + +struct pf_anchor; + +struct pf_ruleset { + struct { + struct pf_rulequeue queues[2]; + struct { + struct pf_rulequeue *ptr; + struct pf_rule **ptr_array; + u_int32_t rcount; + u_int32_t ticket; + int open; + } active, inactive; + } rules[PF_RULESET_MAX]; + struct pf_anchor *anchor; + u_int32_t tticket; + int tables; + int topen; +}; + +RB_HEAD(pf_anchor_global, pf_anchor); +RB_HEAD(pf_anchor_node, pf_anchor); +struct pf_anchor { + RB_ENTRY(pf_anchor) entry_global; + RB_ENTRY(pf_anchor) entry_node; + struct pf_anchor *parent; + struct pf_anchor_node children; + char name[PF_ANCHOR_NAME_SIZE]; + char path[MAXPATHLEN]; + struct pf_ruleset ruleset; + int refcnt; /* anchor rules */ + int match; +}; +RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); +RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); + +#define PF_RESERVED_ANCHOR "_pf" + +#define PFR_TFLAG_PERSIST 0x00000001 +#define PFR_TFLAG_CONST 0x00000002 +#define PFR_TFLAG_ACTIVE 0x00000004 +#define PFR_TFLAG_INACTIVE 0x00000008 +#define PFR_TFLAG_REFERENCED 0x00000010 +#define PFR_TFLAG_REFDANCHOR 0x00000020 +#define PFR_TFLAG_COUNTERS 0x00000040 +/* Adjust masks below when adding flags. */ +#define PFR_TFLAG_USRMASK (PFR_TFLAG_PERSIST | \ + PFR_TFLAG_CONST | \ + PFR_TFLAG_COUNTERS) +#define PFR_TFLAG_SETMASK (PFR_TFLAG_ACTIVE | \ + PFR_TFLAG_INACTIVE | \ + PFR_TFLAG_REFERENCED | \ + PFR_TFLAG_REFDANCHOR) +#define PFR_TFLAG_ALLMASK (PFR_TFLAG_PERSIST | \ + PFR_TFLAG_CONST | \ + PFR_TFLAG_ACTIVE | \ + PFR_TFLAG_INACTIVE | \ + PFR_TFLAG_REFERENCED | \ + PFR_TFLAG_REFDANCHOR | \ + PFR_TFLAG_COUNTERS) + +struct pfr_table { + char pfrt_anchor[MAXPATHLEN]; + char pfrt_name[PF_TABLE_NAME_SIZE]; + u_int32_t pfrt_flags; + u_int8_t pfrt_fback; +}; + +enum { PFR_FB_NONE, PFR_FB_MATCH, PFR_FB_ADDED, PFR_FB_DELETED, + PFR_FB_CHANGED, PFR_FB_CLEARED, PFR_FB_DUPLICATE, + PFR_FB_NOTMATCH, PFR_FB_CONFLICT, PFR_FB_NOCOUNT, PFR_FB_MAX }; + +struct pfr_addr { + union { + struct in_addr _pfra_ip4addr; + struct in6_addr _pfra_ip6addr; + } pfra_u; + u_int8_t pfra_af; + u_int8_t pfra_net; + u_int8_t pfra_not; + u_int8_t pfra_fback; +}; +#define pfra_ip4addr pfra_u._pfra_ip4addr +#define pfra_ip6addr pfra_u._pfra_ip6addr + +enum { PFR_DIR_IN, PFR_DIR_OUT, PFR_DIR_MAX }; +enum { PFR_OP_BLOCK, PFR_OP_PASS, PFR_OP_ADDR_MAX, PFR_OP_TABLE_MAX }; +#define PFR_OP_XPASS PFR_OP_ADDR_MAX + +struct pfr_astats { + struct pfr_addr pfras_a; + u_int64_t pfras_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + long pfras_tzero; +}; + +enum { PFR_REFCNT_RULE, PFR_REFCNT_ANCHOR, PFR_REFCNT_MAX }; + +struct pfr_tstats { + struct pfr_table pfrts_t; + u_int64_t pfrts_packets[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_bytes[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_match; + u_int64_t pfrts_nomatch; + long pfrts_tzero; + int pfrts_cnt; + int pfrts_refcnt[PFR_REFCNT_MAX]; +}; +#define pfrts_name pfrts_t.pfrt_name +#define pfrts_flags pfrts_t.pfrt_flags + +#ifndef _SOCKADDR_UNION_DEFINED +#define _SOCKADDR_UNION_DEFINED +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +#endif /* _SOCKADDR_UNION_DEFINED */ + +struct pfr_kcounters { + u_int64_t pfrkc_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfrkc_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; +}; + +SLIST_HEAD(pfr_kentryworkq, pfr_kentry); +struct pfr_kentry { + struct radix_node pfrke_node[2]; + union sockaddr_union pfrke_sa; + SLIST_ENTRY(pfr_kentry) pfrke_workq; + struct pfr_kcounters *pfrke_counters; + long pfrke_tzero; + u_int8_t pfrke_af; + u_int8_t pfrke_net; + u_int8_t pfrke_not; + u_int8_t pfrke_mark; +}; + +SLIST_HEAD(pfr_ktableworkq, pfr_ktable); +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_ruleset *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_ts.pfrts_cnt +#define pfrkt_refcnt pfrkt_ts.pfrts_refcnt +#define pfrkt_packets pfrkt_ts.pfrts_packets +#define pfrkt_bytes pfrkt_ts.pfrts_bytes +#define pfrkt_match pfrkt_ts.pfrts_match +#define pfrkt_nomatch pfrkt_ts.pfrts_nomatch +#define pfrkt_tzero pfrkt_ts.pfrts_tzero + +/* keep synced with pfi_kif, used in RB_FIND */ +struct pfi_kif_cmp { + char pfik_name[IFNAMSIZ]; +}; + +struct pfi_kif { + char pfik_name[IFNAMSIZ]; + union { + RB_ENTRY(pfi_kif) _pfik_tree; + LIST_ENTRY(pfi_kif) _pfik_list; + } _pfik_glue; +#define pfik_tree _pfik_glue._pfik_tree +#define pfik_list _pfik_glue._pfik_list + u_int64_t pfik_packets[2][2][2]; + u_int64_t pfik_bytes[2][2][2]; + u_int32_t pfik_tzero; + u_int pfik_flags; + struct ifnet *pfik_ifp; + struct ifg_group *pfik_group; + u_int pfik_rulerefs; + TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs; +}; + +#define PFI_IFLAG_REFS 0x0001 /* has state references */ +#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */ + +struct pf_pdesc { + struct { + int done; + uid_t uid; + gid_t gid; + } lookup; + u_int64_t tot_len; /* Make Mickey money */ + union { + struct tcphdr *tcp; + struct udphdr *udp; + struct icmp *icmp; +#ifdef INET6 + struct icmp6_hdr *icmp6; +#endif /* INET6 */ + void *any; + } hdr; + + struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ + struct pf_addr *src; /* src address */ + struct pf_addr *dst; /* dst address */ + u_int16_t *sport; + u_int16_t *dport; + struct pf_mtag *pf_mtag; + + u_int32_t p_len; /* total length of payload */ + + u_int16_t *ip_sum; + u_int16_t *proto_sum; + u_int16_t flags; /* Let SCRUB trigger behavior in + * state code. Easier than tags */ +#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ +#define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */ + sa_family_t af; + u_int8_t proto; + u_int8_t tos; + u_int8_t dir; /* direction */ + u_int8_t sidx; /* key index for source */ + u_int8_t didx; /* key index for destination */ +}; + +/* flags for RDR options */ +#define PF_DPORT_RANGE 0x01 /* Dest port uses range */ +#define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ + +/* Reasons code for passing/dropping a packet */ +#define PFRES_MATCH 0 /* Explicit match of a rule */ +#define PFRES_BADOFF 1 /* Bad offset for pull_hdr */ +#define PFRES_FRAG 2 /* Dropping following fragment */ +#define PFRES_SHORT 3 /* Dropping short packet */ +#define PFRES_NORM 4 /* Dropping by normalizer */ +#define PFRES_MEMORY 5 /* Dropped due to lacking mem */ +#define PFRES_TS 6 /* Bad TCP Timestamp (RFC1323) */ +#define PFRES_CONGEST 7 /* Congestion (of ipintrq) */ +#define PFRES_IPOPTIONS 8 /* IP option */ +#define PFRES_PROTCKSUM 9 /* Protocol checksum invalid */ +#define PFRES_BADSTATE 10 /* State mismatch */ +#define PFRES_STATEINS 11 /* State insertion failure */ +#define PFRES_MAXSTATES 12 /* State limit */ +#define PFRES_SRCLIMIT 13 /* Source node/conn limit */ +#define PFRES_SYNPROXY 14 /* SYN proxy */ +#define PFRES_MAX 15 /* total+1 */ + +#define PFRES_NAMES { \ + "match", \ + "bad-offset", \ + "fragment", \ + "short", \ + "normalize", \ + "memory", \ + "bad-timestamp", \ + "congestion", \ + "ip-option", \ + "proto-cksum", \ + "state-mismatch", \ + "state-insert", \ + "state-limit", \ + "src-limit", \ + "synproxy", \ + NULL \ +} + +/* Counters for other things we want to keep track of */ +#define LCNT_STATES 0 /* states */ +#define LCNT_SRCSTATES 1 /* max-src-states */ +#define LCNT_SRCNODES 2 /* max-src-nodes */ +#define LCNT_SRCCONN 3 /* max-src-conn */ +#define LCNT_SRCCONNRATE 4 /* max-src-conn-rate */ +#define LCNT_OVERLOAD_TABLE 5 /* entry added to overload table */ +#define LCNT_OVERLOAD_FLUSH 6 /* state entries flushed */ +#define LCNT_MAX 7 /* total+1 */ + +#define LCNT_NAMES { \ + "max states per rule", \ + "max-src-states", \ + "max-src-nodes", \ + "max-src-conn", \ + "max-src-conn-rate", \ + "overload table insertion", \ + "overload flush states", \ + NULL \ +} + +/* UDP state enumeration */ +#define PFUDPS_NO_TRAFFIC 0 +#define PFUDPS_SINGLE 1 +#define PFUDPS_MULTIPLE 2 + +#define PFUDPS_NSTATES 3 /* number of state levels */ + +#define PFUDPS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +/* Other protocol state enumeration */ +#define PFOTHERS_NO_TRAFFIC 0 +#define PFOTHERS_SINGLE 1 +#define PFOTHERS_MULTIPLE 2 + +#define PFOTHERS_NSTATES 3 /* number of state levels */ + +#define PFOTHERS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +#define FCNT_STATE_SEARCH 0 +#define FCNT_STATE_INSERT 1 +#define FCNT_STATE_REMOVALS 2 +#define FCNT_MAX 3 + +#define SCNT_SRC_NODE_SEARCH 0 +#define SCNT_SRC_NODE_INSERT 1 +#define SCNT_SRC_NODE_REMOVALS 2 +#define SCNT_MAX 3 + +#define ACTION_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + } while (0) + +#define REASON_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + if (x < PFRES_MAX) \ + V_pf_status.counters[x]++; \ + } while (0) + +struct pf_status { + u_int64_t counters[PFRES_MAX]; + u_int64_t lcounters[LCNT_MAX]; /* limit counters */ + u_int64_t fcounters[FCNT_MAX]; + u_int64_t scounters[SCNT_MAX]; + u_int64_t pcounters[2][2][3]; + u_int64_t bcounters[2][2]; + u_int32_t running; + u_int32_t states; + u_int32_t src_nodes; + u_int32_t since; + u_int32_t debug; + u_int32_t hostid; + char ifname[IFNAMSIZ]; + u_int8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; +}; + +struct cbq_opts { + u_int minburst; + u_int maxburst; + u_int pktsize; + u_int maxpktsize; + u_int ns_per_byte; + u_int maxidle; + int minidle; + u_int offtime; + int flags; +}; + +struct priq_opts { + int flags; +}; + +struct hfsc_opts { + /* real-time service curve */ + u_int rtsc_m1; /* slope of the 1st segment in bps */ + u_int rtsc_d; /* the x-projection of m1 in msec */ + u_int rtsc_m2; /* slope of the 2nd segment in bps */ + /* link-sharing service curve */ + u_int lssc_m1; + u_int lssc_d; + u_int lssc_m2; + /* upper-limit service curve */ + u_int ulsc_m1; + u_int ulsc_d; + u_int ulsc_m2; + int flags; +}; + +struct pf_altq { + char ifname[IFNAMSIZ]; + + void *altq_disc; /* discipline-specific state */ + TAILQ_ENTRY(pf_altq) entries; + + /* scheduler spec */ + u_int8_t scheduler; /* scheduler type */ + u_int16_t tbrsize; /* tokenbucket regulator size */ + u_int32_t ifbandwidth; /* interface bandwidth */ + + /* queue spec */ + char qname[PF_QNAME_SIZE]; /* queue name */ + char parent[PF_QNAME_SIZE]; /* parent name */ + u_int32_t parent_qid; /* parent queue id */ + u_int32_t bandwidth; /* queue bandwidth */ + u_int8_t priority; /* priority */ + u_int8_t local_flags; /* dynamic interface */ +#define PFALTQ_FLAG_IF_REMOVED 0x01 + + u_int16_t qlimit; /* queue size limit */ + u_int16_t flags; /* misc flags */ + union { + struct cbq_opts cbq_opts; + struct priq_opts priq_opts; + struct hfsc_opts hfsc_opts; + } pq_u; + + u_int32_t qid; /* return value */ +}; + +struct pf_divert { + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } addr; + u_int16_t port; +}; + +#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ +#define PFR_KENTRY_HIWAT 200000 /* Number of table entries */ + +/* + * ioctl parameter structures + */ + +struct pfioc_pooladdr { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + u_int32_t r_num; + u_int8_t r_action; + u_int8_t r_last; + u_int8_t af; + char anchor[MAXPATHLEN]; + struct pf_pooladdr addr; +}; + +struct pfioc_rule { + u_int32_t action; + u_int32_t ticket; + u_int32_t pool_ticket; + u_int32_t nr; + char anchor[MAXPATHLEN]; + char anchor_call[MAXPATHLEN]; + struct pf_rule rule; +}; + +struct pfioc_natlook { + struct pf_addr saddr; + struct pf_addr daddr; + struct pf_addr rsaddr; + struct pf_addr rdaddr; + u_int16_t sport; + u_int16_t dport; + u_int16_t rsport; + u_int16_t rdport; + sa_family_t af; + u_int8_t proto; + u_int8_t direction; +}; + +struct pfioc_state { + struct pfsync_state state; +}; + +struct pfioc_src_node_kill { + sa_family_t psnk_af; + struct pf_rule_addr psnk_src; + struct pf_rule_addr psnk_dst; + u_int psnk_killed; +}; + +struct pfioc_state_kill { + struct pf_state_cmp psk_pfcmp; + sa_family_t psk_af; + int psk_proto; + struct pf_rule_addr psk_src; + struct pf_rule_addr psk_dst; + char psk_ifname[IFNAMSIZ]; + char psk_label[PF_RULE_LABEL_SIZE]; + u_int psk_killed; +}; + +struct pfioc_states { + int ps_len; + union { + caddr_t psu_buf; + struct pfsync_state *psu_states; + } ps_u; +#define ps_buf ps_u.psu_buf +#define ps_states ps_u.psu_states +}; + +struct pfioc_src_nodes { + int psn_len; + union { + caddr_t psu_buf; + struct pf_src_node *psu_src_nodes; + } psn_u; +#define psn_buf psn_u.psu_buf +#define psn_src_nodes psn_u.psu_src_nodes +}; + +struct pfioc_if { + char ifname[IFNAMSIZ]; +}; + +struct pfioc_tm { + int timeout; + int seconds; +}; + +struct pfioc_limit { + int index; + unsigned limit; +}; + +struct pfioc_altq { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + struct pf_altq altq; +}; + +struct pfioc_qstats { + u_int32_t ticket; + u_int32_t nr; + void *buf; + int nbytes; + u_int8_t scheduler; +}; + +struct pfioc_ruleset { + u_int32_t nr; + char path[MAXPATHLEN]; + char name[PF_ANCHOR_NAME_SIZE]; +}; + +#define PF_RULESET_ALTQ (PF_RULESET_MAX) +#define PF_RULESET_TABLE (PF_RULESET_MAX+1) +struct pfioc_trans { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + struct pfioc_trans_e { + int rs_num; + char anchor[MAXPATHLEN]; + u_int32_t ticket; + } *array; +}; + +#define PFR_FLAG_ATOMIC 0x00000001 /* unused */ +#define PFR_FLAG_DUMMY 0x00000002 +#define PFR_FLAG_FEEDBACK 0x00000004 +#define PFR_FLAG_CLSTATS 0x00000008 +#define PFR_FLAG_ADDRSTOO 0x00000010 +#define PFR_FLAG_REPLACE 0x00000020 +#define PFR_FLAG_ALLRSETS 0x00000040 +#define PFR_FLAG_ALLMASK 0x0000007F +#ifdef _KERNEL +#define PFR_FLAG_USERIOCTL 0x10000000 +#endif + +struct pfioc_table { + struct pfr_table pfrio_table; + void *pfrio_buffer; + int pfrio_esize; + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + u_int32_t pfrio_ticket; +}; +#define pfrio_exists pfrio_nadd +#define pfrio_nzero pfrio_nadd +#define pfrio_nmatch pfrio_nadd +#define pfrio_naddr pfrio_size2 +#define pfrio_setflag pfrio_size2 +#define pfrio_clrflag pfrio_nadd + +struct pfioc_iface { + char pfiio_name[IFNAMSIZ]; + void *pfiio_buffer; + int pfiio_esize; + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; + + +/* + * ioctl operations + */ + +#define DIOCSTART _IO ('D', 1) +#define DIOCSTOP _IO ('D', 2) +#define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) +#define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) +#define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) +/* XXX cut 8 - 17 */ +#define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) +#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) +#define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) +#define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) +#define DIOCCLRSTATUS _IO ('D', 22) +#define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) +#define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) +#define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) +#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) +/* XXX cut 26 - 28 */ +#define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) +#define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) +#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) +#define DIOCCLRRULECTRS _IO ('D', 38) +#define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) +#define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) +#define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) +#define DIOCSTARTALTQ _IO ('D', 42) +#define DIOCSTOPALTQ _IO ('D', 43) +#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) +#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) +#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) +#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) +#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) +#define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) +#define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) +#define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) +#define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) +#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) +/* XXX cut 55 - 57 */ +#define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) +#define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) +#define DIOCRCLRTABLES _IOWR('D', 60, struct pfioc_table) +#define DIOCRADDTABLES _IOWR('D', 61, struct pfioc_table) +#define DIOCRDELTABLES _IOWR('D', 62, struct pfioc_table) +#define DIOCRGETTABLES _IOWR('D', 63, struct pfioc_table) +#define DIOCRGETTSTATS _IOWR('D', 64, struct pfioc_table) +#define DIOCRCLRTSTATS _IOWR('D', 65, struct pfioc_table) +#define DIOCRCLRADDRS _IOWR('D', 66, struct pfioc_table) +#define DIOCRADDADDRS _IOWR('D', 67, struct pfioc_table) +#define DIOCRDELADDRS _IOWR('D', 68, struct pfioc_table) +#define DIOCRSETADDRS _IOWR('D', 69, struct pfioc_table) +#define DIOCRGETADDRS _IOWR('D', 70, struct pfioc_table) +#define DIOCRGETASTATS _IOWR('D', 71, struct pfioc_table) +#define DIOCRCLRASTATS _IOWR('D', 72, struct pfioc_table) +#define DIOCRTSTADDRS _IOWR('D', 73, struct pfioc_table) +#define DIOCRSETTFLAGS _IOWR('D', 74, struct pfioc_table) +#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table) +#define DIOCOSFPFLUSH _IO('D', 78) +#define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl) +#define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl) +#define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans) +#define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans) +#define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans) +#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes) +#define DIOCCLRSRCNODES _IO('D', 85) +#define DIOCSETHOSTID _IOWR('D', 86, u_int32_t) +#define DIOCIGETIFACES _IOWR('D', 87, struct pfioc_iface) +#define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) +#define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) +#define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) +struct pf_ifspeed { + char ifname[IFNAMSIZ]; + u_int32_t baudrate; +}; +#define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) + +#ifdef _KERNEL +struct pf_srchash { + LIST_HEAD(, pf_src_node) nodes; + struct mtx lock; +}; + +struct pf_keyhash { + LIST_HEAD(, pf_state_key) keys; + struct mtx lock; +}; + +struct pf_idhash { + LIST_HEAD(, pf_state) states; + struct mtx lock; +}; + +#define PF_HASHSIZ (32768) +VNET_DECLARE(struct pf_keyhash *, pf_keyhash); +VNET_DECLARE(struct pf_idhash *, pf_idhash); +VNET_DECLARE(u_long, pf_hashmask); +#define V_pf_keyhash VNET(pf_keyhash) +#define V_pf_idhash VNET(pf_idhash) +#define V_pf_hashmask VNET(pf_hashmask) +VNET_DECLARE(struct pf_srchash *, pf_srchash); +VNET_DECLARE(u_long, pf_srchashmask); +#define V_pf_srchash VNET(pf_srchash) +#define V_pf_srchashmask VNET(pf_srchashmask) + +#define PF_IDHASH(s) (be64toh((s)->id) % (V_pf_hashmask + 1)) + +VNET_DECLARE(void *, pf_swi_cookie); +#define V_pf_swi_cookie VNET(pf_swi_cookie) + +VNET_DECLARE(uint64_t, pf_stateid[MAXCPU]); +#define V_pf_stateid VNET(pf_stateid) + +TAILQ_HEAD(pf_altqqueue, pf_altq); +VNET_DECLARE(struct pf_altqqueue, pf_altqs[2]); +#define V_pf_altqs VNET(pf_altqs) +VNET_DECLARE(struct pf_palist, pf_pabuf); +#define V_pf_pabuf VNET(pf_pabuf) + +VNET_DECLARE(u_int32_t, ticket_altqs_active); +#define V_ticket_altqs_active VNET(ticket_altqs_active) +VNET_DECLARE(u_int32_t, ticket_altqs_inactive); +#define V_ticket_altqs_inactive VNET(ticket_altqs_inactive) +VNET_DECLARE(int, altqs_inactive_open); +#define V_altqs_inactive_open VNET(altqs_inactive_open) +VNET_DECLARE(u_int32_t, ticket_pabuf); +#define V_ticket_pabuf VNET(ticket_pabuf) +VNET_DECLARE(struct pf_altqqueue *, pf_altqs_active); +#define V_pf_altqs_active VNET(pf_altqs_active) +VNET_DECLARE(struct pf_altqqueue *, pf_altqs_inactive); +#define V_pf_altqs_inactive VNET(pf_altqs_inactive) + +VNET_DECLARE(struct pf_rulequeue, pf_unlinked_rules); +#define V_pf_unlinked_rules VNET(pf_unlinked_rules) + +void pf_initialize(void); +void pf_cleanup(void); + +struct pf_mtag *pf_get_mtag(struct mbuf *); + +extern void pf_calc_skip_steps(struct pf_rulequeue *); +#ifdef ALTQ +extern void pf_altq_ifnet_event(struct ifnet *, int); +#endif +VNET_DECLARE(uma_zone_t, pf_state_z); +#define V_pf_state_z VNET(pf_state_z) +VNET_DECLARE(uma_zone_t, pf_state_key_z); +#define V_pf_state_key_z VNET(pf_state_key_z) +VNET_DECLARE(uma_zone_t, pf_state_scrub_z); +#define V_pf_state_scrub_z VNET(pf_state_scrub_z) + +extern void pf_purge_thread(void *); +extern void pf_intr(void *); +extern void pf_purge_expired_src_nodes(void); + +extern int pf_unlink_state(struct pf_state *, u_int); +#define PF_ENTER_LOCKED 0x00000001 +#define PF_RETURN_LOCKED 0x00000002 +extern int pf_state_insert(struct pfi_kif *, + struct pf_state_key *, + struct pf_state_key *, + struct pf_state *); +extern void pf_free_state(struct pf_state *); + +static __inline u_int +pf_hashsrc(struct pf_addr *addr, sa_family_t af) +{ + u_int h; + +#define ADDR_HASH(a) ((a) ^ ((a) >> 16)) + + switch (af) { + case AF_INET: + h = ADDR_HASH(addr->v4.s_addr); + break; + case AF_INET6: + h = ADDR_HASH(addr->v6.__u6_addr.__u6_addr32[3]); + default: + panic("%s: unknown address family %u", __func__, af); + } +#undef ADDR_HASH + + return (h & V_pf_srchashmask); +} + +static __inline void +pf_ref_state(struct pf_state *s) +{ + + refcount_acquire(&s->refs); +} + +static __inline int +pf_release_state(struct pf_state *s) +{ + + if (refcount_release(&s->refs)) { + pf_free_state(s); + return (1); + } else + return (0); +} + +extern struct pf_state *pf_find_state_byid(uint64_t, uint32_t); +extern struct pf_state *pf_find_state_all(struct pf_state_key_cmp *, + u_int, int *); +struct pf_src_node *pf_find_src_node(struct pf_addr *, struct pf_rule *, + sa_family_t, int); +extern void pf_print_state(struct pf_state *); +extern void pf_print_flags(u_int8_t); +extern u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, + u_int8_t); + +VNET_DECLARE(struct ifnet *, sync_ifp); +#define V_sync_ifp VNET(sync_ifp); +VNET_DECLARE(struct pf_rule, pf_default_rule); +#define V_pf_default_rule VNET(pf_default_rule) +extern void pf_addrcpy(struct pf_addr *, struct pf_addr *, + u_int8_t); +void pf_free_rule(struct pf_rule *); + +#ifdef INET +int pf_test(int, struct ifnet *, struct mbuf **, struct inpcb *); +#endif /* INET */ + +#ifdef INET6 +int pf_test6(int, struct ifnet *, struct mbuf **, struct inpcb *); +void pf_poolmask(struct pf_addr *, struct pf_addr*, + struct pf_addr *, struct pf_addr *, u_int8_t); +void pf_addr_inc(struct pf_addr *, sa_family_t); +#endif /* INET6 */ + +u_int32_t pf_new_isn(struct pf_state *); +void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, + sa_family_t); +void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); +void pf_send_deferred_syn(struct pf_state *); +int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, + struct pf_addr *, sa_family_t); +int pf_match_addr_range(struct pf_addr *, struct pf_addr *, + struct pf_addr *, sa_family_t); +int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); + +void pf_normalize_init(void); +void pf_normalize_cleanup(void); +int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, u_short *, + struct pf_pdesc *); +int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, u_short *, + struct pf_pdesc *); +int pf_normalize_tcp(int, struct pfi_kif *, struct mbuf *, int, int, void *, + struct pf_pdesc *); +void pf_normalize_tcp_cleanup(struct pf_state *); +int pf_normalize_tcp_init(struct mbuf *, int, struct pf_pdesc *, + struct tcphdr *, struct pf_state_peer *, struct pf_state_peer *); +int pf_normalize_tcp_stateful(struct mbuf *, int, struct pf_pdesc *, + u_short *, struct tcphdr *, struct pf_state *, + struct pf_state_peer *, struct pf_state_peer *, int *); +u_int32_t + pf_state_expires(const struct pf_state *); +void pf_purge_expired_fragments(void); +int pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *, + int); +int pf_socket_lookup(int, struct pf_pdesc *, struct mbuf *); +struct pf_state_key *pf_alloc_state_key(int); +void pfr_initialize(void); +void pfr_cleanup(void); +int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); +void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t, + u_int64_t, int, int, int); +int pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, sa_family_t); +void pfr_dynaddr_update(struct pfr_ktable *, struct pfi_dynaddr *); +struct pfr_ktable * + pfr_attach_table(struct pf_ruleset *, char *); +void pfr_detach_table(struct pfr_ktable *); +int pfr_clr_tables(struct pfr_table *, int *, int); +int pfr_add_tables(struct pfr_table *, int, int *, int); +int pfr_del_tables(struct pfr_table *, int, int *, int); +int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int); +int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); +int pfr_clr_tstats(struct pfr_table *, int, int *, int); +int pfr_set_tflags(struct pfr_table *, int, int, int, int *, int *, int); +int pfr_clr_addrs(struct pfr_table *, int *, int); +int pfr_insert_kentry(struct pfr_ktable *, struct pfr_addr *, long); +int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int *, int *, int *, int, u_int32_t); +int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int); +int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int); +int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_ina_begin(struct pfr_table *, u_int32_t *, int *, int); +int pfr_ina_rollback(struct pfr_table *, u_int32_t, int *, int); +int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, int *, int); +int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, + int *, u_int32_t, int); + +MALLOC_DECLARE(PFI_MTYPE); +VNET_DECLARE(struct pfi_kif *, pfi_all); +#define V_pfi_all VNET(pfi_all) + +void pfi_initialize(void); +void pfi_cleanup(void); +void pfi_kif_ref(struct pfi_kif *); +void pfi_kif_unref(struct pfi_kif *); +struct pfi_kif *pfi_kif_find(const char *); +struct pfi_kif *pfi_kif_attach(struct pfi_kif *, const char *); +int pfi_kif_match(struct pfi_kif *, struct pfi_kif *); +void pfi_kif_purge(void); +int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, + sa_family_t); +int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); +void pfi_dynaddr_remove(struct pfi_dynaddr *); +void pfi_dynaddr_copyout(struct pf_addr_wrap *); +void pfi_update_status(const char *, struct pf_status *); +void pfi_get_ifaces(const char *, struct pfi_kif *, int *); +int pfi_set_flags(const char *, int); +int pfi_clear_flags(const char *, int); + +int pf_match_tag(struct mbuf *, struct pf_rule *, int *, int); +int pf_tag_packet(struct mbuf *, struct pf_pdesc *, int); +void pf_qid2qname(u_int32_t, char *); + +VNET_DECLARE(struct pf_status, pf_status); +#define V_pf_status VNET(pf_status) + +struct pf_limit { + uma_zone_t zone; + u_int limit; +}; +VNET_DECLARE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); +#define V_pf_limits VNET(pf_limits) + +#endif /* _KERNEL */ + +#ifdef _KERNEL +VNET_DECLARE(struct pf_anchor_global, pf_anchors); +#define V_pf_anchors VNET(pf_anchors) +VNET_DECLARE(struct pf_anchor, pf_main_anchor); +#define V_pf_main_anchor VNET(pf_main_anchor) +#define pf_main_ruleset V_pf_main_anchor.ruleset +#endif + +/* these ruleset functions can be linked into userland programs (pfctl) */ +int pf_get_ruleset_number(u_int8_t); +void pf_init_ruleset(struct pf_ruleset *); +int pf_anchor_setup(struct pf_rule *, + const struct pf_ruleset *, const char *); +int pf_anchor_copyout(const struct pf_ruleset *, + const struct pf_rule *, struct pfioc_rule *); +void pf_anchor_remove(struct pf_rule *); +void pf_remove_if_empty_ruleset(struct pf_ruleset *); +struct pf_ruleset *pf_find_ruleset(const char *); +struct pf_ruleset *pf_find_or_create_ruleset(const char *); +void pf_rs_initialize(void); + +/* The fingerprint functions can be linked into userland programs (tcpdump) */ +int pf_osfp_add(struct pf_osfp_ioctl *); +#ifdef _KERNEL +struct pf_osfp_enlist * + pf_osfp_fingerprint(struct pf_pdesc *, struct mbuf *, int, + const struct tcphdr *); +#endif /* _KERNEL */ +void pf_osfp_flush(void); +int pf_osfp_get(struct pf_osfp_ioctl *); +int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); + +#ifdef _KERNEL +void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); + +void pf_step_into_anchor(int *, struct pf_ruleset **, int, + struct pf_rule **, struct pf_rule **, int *); +int pf_step_out_of_anchor(int *, struct pf_ruleset **, + int, struct pf_rule **, struct pf_rule **, + int *); + +int pf_map_addr(u_int8_t, struct pf_rule *, + struct pf_addr *, struct pf_addr *, + struct pf_addr *, struct pf_src_node **); +struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, + int, int, struct pfi_kif *, struct pf_src_node **, + struct pf_state_key **, struct pf_state_key **, + struct pf_addr *, struct pf_addr *, + u_int16_t, u_int16_t); + +struct pf_state_key *pf_state_key_setup(struct pf_pdesc *, struct pf_addr *, + struct pf_addr *, u_int16_t, u_int16_t); +struct pf_state_key *pf_state_key_clone(struct pf_state_key *); +#endif /* _KERNEL */ + +#endif /* _NET_PFVAR_H_ */ diff --git a/sys/netgraph/ng_ipfw.c b/sys/netgraph/ng_ipfw.c index fe8ea893077e5..907b5cbf56458 100644 --- a/sys/netgraph/ng_ipfw.c +++ b/sys/netgraph/ng_ipfw.c @@ -48,11 +48,12 @@ #include #include #include -#include #include #include #include +#include + #include #include #include diff --git a/sys/netinet/ipfw/dn_heap.c b/sys/netinet/ipfw/dn_heap.c deleted file mode 100644 index 3bdfd9def2889..0000000000000 --- a/sys/netinet/ipfw/dn_heap.c +++ /dev/null @@ -1,552 +0,0 @@ -/*- - * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa - * 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. - */ - -/* - * Binary heap and hash tables, used in dummynet - * - * $FreeBSD$ - */ - -#include -#include -#ifdef _KERNEL -__FBSDID("$FreeBSD$"); -#include -#include -#include -#include -#ifndef log -#define log(x, arg...) -#endif - -#else /* !_KERNEL */ - -#include -#include -#include -#include - -#include "dn_heap.h" -#define log(x, arg...) fprintf(stderr, ## arg) -#define panic(x...) fprintf(stderr, ## x), exit(1) -#define MALLOC_DEFINE(a, b, c) -static void *my_malloc(int s) { return malloc(s); } -static void my_free(void *p) { free(p); } -#define malloc(s, t, w) my_malloc(s) -#define free(p, t) my_free(p) -#endif /* !_KERNEL */ - -static MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap"); - -/* - * Heap management functions. - * - * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2. - * Some macros help finding parent/children so we can optimize them. - * - * heap_init() is called to expand the heap when needed. - * Increment size in blocks of 16 entries. - * Returns 1 on error, 0 on success - */ -#define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 ) -#define HEAP_LEFT(x) ( (x)+(x) + 1 ) -#define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; } -#define HEAP_INCREMENT 15 - -static int -heap_resize(struct dn_heap *h, unsigned int new_size) -{ - struct dn_heap_entry *p; - - if (h->size >= new_size ) /* have enough room */ - return 0; -#if 1 /* round to the next power of 2 */ - new_size |= new_size >> 1; - new_size |= new_size >> 2; - new_size |= new_size >> 4; - new_size |= new_size >> 8; - new_size |= new_size >> 16; -#else - new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT; -#endif - p = malloc(new_size * sizeof(*p), M_DN_HEAP, M_NOWAIT); - if (p == NULL) { - printf("--- %s, resize %d failed\n", __func__, new_size ); - return 1; /* error */ - } - if (h->size > 0) { - bcopy(h->p, p, h->size * sizeof(*p) ); - free(h->p, M_DN_HEAP); - } - h->p = p; - h->size = new_size; - return 0; -} - -int -heap_init(struct dn_heap *h, int size, int ofs) -{ - if (heap_resize(h, size)) - return 1; - h->elements = 0; - h->ofs = ofs; - return 0; -} - -/* - * Insert element in heap. Normally, p != NULL, we insert p in - * a new position and bubble up. If p == NULL, then the element is - * already in place, and key is the position where to start the - * bubble-up. - * Returns 1 on failure (cannot allocate new heap entry) - * - * If ofs > 0 the position (index, int) of the element in the heap is - * also stored in the element itself at the given offset in bytes. - */ -#define SET_OFFSET(h, i) do { \ - if (h->ofs > 0) \ - *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i; \ - } while (0) -/* - * RESET_OFFSET is used for sanity checks. It sets ofs - * to an invalid value. - */ -#define RESET_OFFSET(h, i) do { \ - if (h->ofs > 0) \ - *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16; \ - } while (0) - -int -heap_insert(struct dn_heap *h, uint64_t key1, void *p) -{ - int son = h->elements; - - //log("%s key %llu p %p\n", __FUNCTION__, key1, p); - if (p == NULL) { /* data already there, set starting point */ - son = key1; - } else { /* insert new element at the end, possibly resize */ - son = h->elements; - if (son == h->size) /* need resize... */ - // XXX expand by 16 or so - if (heap_resize(h, h->elements+16) ) - return 1; /* failure... */ - h->p[son].object = p; - h->p[son].key = key1; - h->elements++; - } - /* make sure that son >= father along the path */ - while (son > 0) { - int father = HEAP_FATHER(son); - struct dn_heap_entry tmp; - - if (DN_KEY_LT( h->p[father].key, h->p[son].key ) ) - break; /* found right position */ - /* son smaller than father, swap and repeat */ - HEAP_SWAP(h->p[son], h->p[father], tmp); - SET_OFFSET(h, son); - son = father; - } - SET_OFFSET(h, son); - return 0; -} - -/* - * remove top element from heap, or obj if obj != NULL - */ -void -heap_extract(struct dn_heap *h, void *obj) -{ - int child, father, max = h->elements - 1; - - if (max < 0) { - printf("--- %s: empty heap 0x%p\n", __FUNCTION__, h); - return; - } - if (obj == NULL) - father = 0; /* default: move up smallest child */ - else { /* extract specific element, index is at offset */ - if (h->ofs <= 0) - panic("%s: extract from middle not set on %p\n", - __FUNCTION__, h); - father = *((int *)((char *)obj + h->ofs)); - if (father < 0 || father >= h->elements) { - panic("%s: father %d out of bound 0..%d\n", - __FUNCTION__, father, h->elements); - } - } - /* - * below, father is the index of the empty element, which - * we replace at each step with the smallest child until we - * reach the bottom level. - */ - // XXX why removing RESET_OFFSET increases runtime by 10% ? - RESET_OFFSET(h, father); - while ( (child = HEAP_LEFT(father)) <= max ) { - if (child != max && - DN_KEY_LT(h->p[child+1].key, h->p[child].key) ) - child++; /* take right child, otherwise left */ - h->p[father] = h->p[child]; - SET_OFFSET(h, father); - father = child; - } - h->elements--; - if (father != max) { - /* - * Fill hole with last entry and bubble up, - * reusing the insert code - */ - h->p[father] = h->p[max]; - heap_insert(h, father, NULL); - } -} - -#if 0 -/* - * change object position and update references - * XXX this one is never used! - */ -static void -heap_move(struct dn_heap *h, uint64_t new_key, void *object) -{ - int temp, i, max = h->elements-1; - struct dn_heap_entry *p, buf; - - if (h->ofs <= 0) - panic("cannot move items on this heap"); - p = h->p; /* shortcut */ - - i = *((int *)((char *)object + h->ofs)); - if (DN_KEY_LT(new_key, p[i].key) ) { /* must move up */ - p[i].key = new_key; - for (; i>0 && - DN_KEY_LT(new_key, p[(temp = HEAP_FATHER(i))].key); - i = temp ) { /* bubble up */ - HEAP_SWAP(p[i], p[temp], buf); - SET_OFFSET(h, i); - } - } else { /* must move down */ - p[i].key = new_key; - while ( (temp = HEAP_LEFT(i)) <= max ) { - /* found left child */ - if (temp != max && - DN_KEY_LT(p[temp+1].key, p[temp].key)) - temp++; /* select child with min key */ - if (DN_KEY_LT(>p[temp].key, new_key)) { - /* go down */ - HEAP_SWAP(p[i], p[temp], buf); - SET_OFFSET(h, i); - } else - break; - i = temp; - } - } - SET_OFFSET(h, i); -} -#endif /* heap_move, unused */ - -/* - * heapify() will reorganize data inside an array to maintain the - * heap property. It is needed when we delete a bunch of entries. - */ -static void -heapify(struct dn_heap *h) -{ - int i; - - for (i = 0; i < h->elements; i++ ) - heap_insert(h, i , NULL); -} - -int -heap_scan(struct dn_heap *h, int (*fn)(void *, uintptr_t), - uintptr_t arg) -{ - int i, ret, found; - - for (i = found = 0 ; i < h->elements ;) { - ret = fn(h->p[i].object, arg); - if (ret & HEAP_SCAN_DEL) { - h->elements-- ; - h->p[i] = h->p[h->elements] ; - found++ ; - } else - i++ ; - if (ret & HEAP_SCAN_END) - break; - } - if (found) - heapify(h); - return found; -} - -/* - * cleanup the heap and free data structure - */ -void -heap_free(struct dn_heap *h) -{ - if (h->size >0 ) - free(h->p, M_DN_HEAP); - bzero(h, sizeof(*h) ); -} - -/* - * hash table support. - */ - -struct dn_ht { - int buckets; /* how many buckets, really buckets - 1*/ - int entries; /* how many entries */ - int ofs; /* offset of link field */ - uint32_t (*hash)(uintptr_t, int, void *arg); - int (*match)(void *_el, uintptr_t key, int, void *); - void *(*newh)(uintptr_t, int, void *); - void **ht; /* bucket heads */ -}; -/* - * Initialize, allocating bucket pointers inline. - * Recycle previous record if possible. - * If the 'newh' function is not supplied, we assume that the - * key passed to ht_find is the same object to be stored in. - */ -struct dn_ht * -dn_ht_init(struct dn_ht *ht, int buckets, int ofs, - uint32_t (*h)(uintptr_t, int, void *), - int (*match)(void *, uintptr_t, int, void *), - void *(*newh)(uintptr_t, int, void *)) -{ - int l; - - /* - * Notes about rounding bucket size to a power of two. - * Given the original bucket size, we compute the nearest lower and - * higher power of two, minus 1 (respectively b_min and b_max) because - * this value will be used to do an AND with the index returned - * by hash function. - * To choice between these two values, the original bucket size is - * compared with b_min. If the original size is greater than 4/3 b_min, - * we round the bucket size to b_max, else to b_min. - * This ratio try to round to the nearest power of two, advantaging - * the greater size if the different between two power is relatively - * big. - * Rounding the bucket size to a power of two avoid the use of - * module when calculating the correct bucket. - * The ht->buckets variable store the bucket size - 1 to simply - * do an AND between the index returned by hash function and ht->bucket - * instead of a module. - */ - int b_min; /* min buckets */ - int b_max; /* max buckets */ - int b_ori; /* original buckets */ - - if (h == NULL || match == NULL) { - printf("--- missing hash or match function"); - return NULL; - } - if (buckets < 1 || buckets > 65536) - return NULL; - - b_ori = buckets; - /* calculate next power of 2, - 1*/ - buckets |= buckets >> 1; - buckets |= buckets >> 2; - buckets |= buckets >> 4; - buckets |= buckets >> 8; - buckets |= buckets >> 16; - - b_max = buckets; /* Next power */ - b_min = buckets >> 1; /* Previous power */ - - /* Calculate the 'nearest' bucket size */ - if (b_min * 4000 / 3000 < b_ori) - buckets = b_max; - else - buckets = b_min; - - if (ht) { /* see if we can reuse */ - if (buckets <= ht->buckets) { - ht->buckets = buckets; - } else { - /* free pointers if not allocated inline */ - if (ht->ht != (void *)(ht + 1)) - free(ht->ht, M_DN_HEAP); - free(ht, M_DN_HEAP); - ht = NULL; - } - } - if (ht == NULL) { - /* Allocate buckets + 1 entries because buckets is use to - * do the AND with the index returned by hash function - */ - l = sizeof(*ht) + (buckets + 1) * sizeof(void **); - ht = malloc(l, M_DN_HEAP, M_NOWAIT | M_ZERO); - } - if (ht) { - ht->ht = (void **)(ht + 1); - ht->buckets = buckets; - ht->ofs = ofs; - ht->hash = h; - ht->match = match; - ht->newh = newh; - } - return ht; -} - -/* dummy callback for dn_ht_free to unlink all */ -static int -do_del(void *obj, void *arg) -{ - return DNHT_SCAN_DEL; -} - -void -dn_ht_free(struct dn_ht *ht, int flags) -{ - if (ht == NULL) - return; - if (flags & DNHT_REMOVE) { - (void)dn_ht_scan(ht, do_del, NULL); - } else { - if (ht->ht && ht->ht != (void *)(ht + 1)) - free(ht->ht, M_DN_HEAP); - free(ht, M_DN_HEAP); - } -} - -int -dn_ht_entries(struct dn_ht *ht) -{ - return ht ? ht->entries : 0; -} - -/* lookup and optionally create or delete element */ -void * -dn_ht_find(struct dn_ht *ht, uintptr_t key, int flags, void *arg) -{ - int i; - void **pp, *p; - - if (ht == NULL) /* easy on an empty hash */ - return NULL; - i = (ht->buckets == 1) ? 0 : - (ht->hash(key, flags, arg) & ht->buckets); - - for (pp = &ht->ht[i]; (p = *pp); pp = (void **)((char *)p + ht->ofs)) { - if (flags & DNHT_MATCH_PTR) { - if (key == (uintptr_t)p) - break; - } else if (ht->match(p, key, flags, arg)) /* found match */ - break; - } - if (p) { - if (flags & DNHT_REMOVE) { - /* link in the next element */ - *pp = *(void **)((char *)p + ht->ofs); - *(void **)((char *)p + ht->ofs) = NULL; - ht->entries--; - } - } else if (flags & DNHT_INSERT) { - // printf("%s before calling new, bucket %d ofs %d\n", - // __FUNCTION__, i, ht->ofs); - p = ht->newh ? ht->newh(key, flags, arg) : (void *)key; - // printf("%s newh returns %p\n", __FUNCTION__, p); - if (p) { - ht->entries++; - *(void **)((char *)p + ht->ofs) = ht->ht[i]; - ht->ht[i] = p; - } - } - return p; -} - -/* - * do a scan with the option to delete the object. Extract next before - * running the callback because the element may be destroyed there. - */ -int -dn_ht_scan(struct dn_ht *ht, int (*fn)(void *, void *), void *arg) -{ - int i, ret, found = 0; - void **curp, *cur, *next; - - if (ht == NULL || fn == NULL) - return 0; - for (i = 0; i <= ht->buckets; i++) { - curp = &ht->ht[i]; - while ( (cur = *curp) != NULL) { - next = *(void **)((char *)cur + ht->ofs); - ret = fn(cur, arg); - if (ret & DNHT_SCAN_DEL) { - found++; - ht->entries--; - *curp = next; - } else { - curp = (void **)((char *)cur + ht->ofs); - } - if (ret & DNHT_SCAN_END) - return found; - } - } - return found; -} - -/* - * Similar to dn_ht_scan(), except that the scan is performed only - * in the bucket 'bucket'. The function returns a correct bucket number if - * the original is invalid. - * If the callback returns DNHT_SCAN_END, the function move the ht->ht[i] - * pointer to the last entry processed. Moreover, the bucket number passed - * by caller is decremented, because usually the caller increment it. - */ -int -dn_ht_scan_bucket(struct dn_ht *ht, int *bucket, int (*fn)(void *, void *), - void *arg) -{ - int i, ret, found = 0; - void **curp, *cur, *next; - - if (ht == NULL || fn == NULL) - return 0; - if (*bucket > ht->buckets) - *bucket = 0; - i = *bucket; - - curp = &ht->ht[i]; - while ( (cur = *curp) != NULL) { - next = *(void **)((char *)cur + ht->ofs); - ret = fn(cur, arg); - if (ret & DNHT_SCAN_DEL) { - found++; - ht->entries--; - *curp = next; - } else { - curp = (void **)((char *)cur + ht->ofs); - } - if (ret & DNHT_SCAN_END) - return found; - } - return found; -} diff --git a/sys/netinet/ipfw/dn_heap.h b/sys/netinet/ipfw/dn_heap.h deleted file mode 100644 index c95473ade3926..0000000000000 --- a/sys/netinet/ipfw/dn_heap.h +++ /dev/null @@ -1,191 +0,0 @@ -/*- - * Copyright (c) 1998-2010 Luigi Rizzo, Universita` di Pisa - * 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. - */ - -/* - * Binary heap and hash tables, header file - * - * $FreeBSD$ - */ - -#ifndef _IP_DN_HEAP_H -#define _IP_DN_HEAP_H - -#define DN_KEY_LT(a,b) ((int64_t)((a)-(b)) < 0) -#define DN_KEY_LEQ(a,b) ((int64_t)((a)-(b)) <= 0) - -/* - * This module implements a binary heap supporting random extraction. - * - * A heap entry contains an uint64_t key and a pointer to object. - * DN_KEY_LT(a,b) returns true if key 'a' is smaller than 'b' - * - * The heap is a struct dn_heap plus a dynamically allocated - * array of dn_heap_entry entries. 'size' represents the size of - * the array, 'elements' count entries in use. The topmost - * element has the smallest key. - * The heap supports ordered insert, and extract from the top. - * To extract an object from the middle of the heap, we the object - * must reserve an 'int32_t' to store the position of the object - * in the heap itself, and the location of this field must be - * passed as an argument to heap_init() -- use -1 if the feature - * is not used. - */ -struct dn_heap_entry { - uint64_t key; /* sorting key, smallest comes first */ - void *object; /* object pointer */ -}; - -struct dn_heap { - int size; /* the size of the array */ - int elements; /* elements in use */ - int ofs; /* offset in the object of heap index */ - struct dn_heap_entry *p; /* array of "size" entries */ -}; - -enum { - HEAP_SCAN_DEL = 1, - HEAP_SCAN_END = 2, -}; - -/* - * heap_init() reinitializes the heap setting the size and the offset - * of the index for random extraction (use -1 if not used). - * The 'elements' counter is set to 0. - * - * SET_HEAP_OFS() indicates where, in the object, is stored the index - * for random extractions from the heap. - * - * heap_free() frees the memory associated to a heap. - * - * heap_insert() adds a key-pointer pair to the heap - * - * HEAP_TOP() returns a pointer to the top element of the heap, - * but makes no checks on its existance (XXX should we change ?) - * - * heap_extract() removes the entry at the top, returing the pointer. - * (the key should have been read before). - * - * heap_scan() invokes a callback on each entry of the heap. - * The callback can return a combination of HEAP_SCAN_DEL and - * HEAP_SCAN_END. HEAP_SCAN_DEL means the current element must - * be removed, and HEAP_SCAN_END means to terminate the scan. - * heap_scan() returns the number of elements removed. - * Because the order is not guaranteed, we should use heap_scan() - * only as a last resort mechanism. - */ -#define HEAP_TOP(h) ((h)->p) -#define SET_HEAP_OFS(h, n) do { (h)->ofs = n; } while (0) -int heap_init(struct dn_heap *h, int size, int ofs); -int heap_insert(struct dn_heap *h, uint64_t key1, void *p); -void heap_extract(struct dn_heap *h, void *obj); -void heap_free(struct dn_heap *h); -int heap_scan(struct dn_heap *, int (*)(void *, uintptr_t), uintptr_t); - -/*------------------------------------------------------ - * This module implements a generic hash table with support for - * running callbacks on the entire table. To avoid allocating - * memory during hash table operations, objects must reserve - * space for a link field. XXX if the heap is moderately full, - * an SLIST suffices, and we can tolerate the cost of a hash - * computation on each removal. - * - * dn_ht_init() initializes the table, setting the number of - * buckets, the offset of the link field, the main callbacks. - * Callbacks are: - * - * hash(key, flags, arg) called to return a bucket index. - * match(obj, key, flags, arg) called to determine if key - * matches the current 'obj' in the heap - * newh(key, flags, arg) optional, used to allocate a new - * object during insertions. - * - * dn_ht_free() frees the heap or unlink elements. - * DNHT_REMOVE unlink elements, 0 frees the heap. - * You need two calls to do both. - * - * dn_ht_find() is the main lookup function, which can also be - * used to insert or delete elements in the hash table. - * The final 'arg' is passed to all callbacks. - * - * dn_ht_scan() is used to invoke a callback on all entries of - * the heap, or possibly on just one bucket. The callback - * is invoked with a pointer to the object, and must return - * one of DNHT_SCAN_DEL or DNHT_SCAN_END to request the - * removal of the object from the heap and the end of the - * scan, respectively. - * - * dn_ht_scan_bucket() is similar to dn_ht_scan(), except that it scans - * only the specific bucket of the table. The bucket is a in-out - * parameter and return a valid bucket number if the original - * is invalid. - * - * A combination of flags can be used to modify the operation - * of the dn_ht_find(), and of the callbacks: - * - * DNHT_KEY_IS_OBJ means the key is the object pointer. - * It is usally of interest for the hash and match functions. - * - * DNHT_MATCH_PTR during a lookup, match pointers instead - * of calling match(). Normally used when removing specific - * entries. Does not imply KEY_IS_OBJ as the latter _is_ used - * by the match function. - * - * DNHT_INSERT insert the element if not found. - * Calls new() to allocates a new object unless - * DNHT_KEY_IS_OBJ is set. - * - * DNHT_UNIQUE only insert if object not found. - * XXX should it imply DNHT_INSERT ? - * - * DNHT_REMOVE remove objects if we find them. - */ -struct dn_ht; /* should be opaque */ - -struct dn_ht *dn_ht_init(struct dn_ht *, int buckets, int ofs, - uint32_t (*hash)(uintptr_t, int, void *), - int (*match)(void *, uintptr_t, int, void *), - void *(*newh)(uintptr_t, int, void *)); -void dn_ht_free(struct dn_ht *, int flags); - -void *dn_ht_find(struct dn_ht *, uintptr_t, int, void *); -int dn_ht_scan(struct dn_ht *, int (*)(void *, void *), void *); -int dn_ht_scan_bucket(struct dn_ht *, int * , int (*)(void *, void *), void *); -int dn_ht_entries(struct dn_ht *); - -enum { /* flags values. - * first two are returned by the scan callback to indicate - * to delete the matching element or to end the scan - */ - DNHT_SCAN_DEL = 0x0001, - DNHT_SCAN_END = 0x0002, - DNHT_KEY_IS_OBJ = 0x0004, /* key is the obj pointer */ - DNHT_MATCH_PTR = 0x0008, /* match by pointer, not match() */ - DNHT_INSERT = 0x0010, /* insert if not found */ - DNHT_UNIQUE = 0x0020, /* report error if already there */ - DNHT_REMOVE = 0x0040, /* remove on find or dn_ht_free */ -}; - -#endif /* _IP_DN_HEAP_H */ diff --git a/sys/netinet/ipfw/dn_sched.h b/sys/netinet/ipfw/dn_sched.h deleted file mode 100644 index ab823fe7c806e..0000000000000 --- a/sys/netinet/ipfw/dn_sched.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2010 Riccardo Panicucci, Luigi Rizzo, Universita` di Pisa - * 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. - */ - -/* - * The API to write a packet scheduling algorithm for dummynet. - * - * $FreeBSD$ - */ - -#ifndef _DN_SCHED_H -#define _DN_SCHED_H - -#define DN_MULTIQUEUE 0x01 -/* - * Descriptor for a scheduling algorithm. - * Contains all function pointers for a given scheduler - * This is typically created when a module is loaded, and stored - * in a global list of schedulers. - */ -struct dn_alg { - uint32_t type; /* the scheduler type */ - const char *name; /* scheduler name */ - uint32_t flags; /* DN_MULTIQUEUE if supports multiple queues */ - - /* - * The following define the size of 3 optional data structures - * that may need to be allocated at runtime, and are appended - * to each of the base data structures: scheduler, sched.inst, - * and queue. We don't have a per-flowset structure. - */ - /* + parameters attached to the template, e.g. - * default queue sizes, weights, quantum size, and so on; - */ - size_t schk_datalen; - - /* + per-instance parameters, such as timestamps, - * containers for queues, etc; - */ - size_t si_datalen; - - size_t q_datalen; /* per-queue parameters (e.g. S,F) */ - - /* - * Methods implemented by the scheduler: - * enqueue enqueue packet 'm' on scheduler 's', queue 'q'. - * q is NULL for !MULTIQUEUE. - * Return 0 on success, 1 on drop (packet consumed anyways). - * Note that q should be interpreted only as a hint - * on the flow that the mbuf belongs to: while a - * scheduler will normally enqueue m into q, it is ok - * to leave q alone and put the mbuf elsewhere. - * This function is called in two cases: - * - when a new packet arrives to the scheduler; - * - when a scheduler is reconfigured. In this case the - * call is issued by the new_queue callback, with a - * non empty queue (q) and m pointing to the first - * mbuf in the queue. For this reason, the function - * should internally check for (m != q->mq.head) - * before calling dn_enqueue(). - * - * dequeue Called when scheduler instance 's' can - * dequeue a packet. Return NULL if none are available. - * XXX what about non work-conserving ? - * - * config called on 'sched X config ...', normally writes - * in the area of size sch_arg - * - * destroy called on 'sched delete', frees everything - * in sch_arg (other parts are handled by more specific - * functions) - * - * new_sched called when a new instance is created, e.g. - * to create the local queue for !MULTIQUEUE, set V or - * copy parameters for WFQ, and so on. - * - * free_sched called when deleting an instance, cleans - * extra data in the per-instance area. - * - * new_fsk called when a flowset is linked to a scheduler, - * e.g. to validate parameters such as weights etc. - * free_fsk when a flowset is unlinked from a scheduler. - * (probably unnecessary) - * - * new_queue called to set the per-queue parameters, - * e.g. S and F, adjust sum of weights in the parent, etc. - * - * The new_queue callback is normally called from when - * creating a new queue. In some cases (such as a - * scheduler change or reconfiguration) it can be called - * with a non empty queue. In this case, the queue - * In case of non empty queue, the new_queue callback could - * need to call the enqueue function. In this case, - * the callback should eventually call enqueue() passing - * as m the first element in the queue. - * - * free_queue actions related to a queue removal, e.g. undo - * all the above. If the queue has data in it, also remove - * from the scheduler. This can e.g. happen during a reconfigure. - */ - int (*enqueue)(struct dn_sch_inst *, struct dn_queue *, - struct mbuf *); - struct mbuf * (*dequeue)(struct dn_sch_inst *); - - int (*config)(struct dn_schk *); - int (*destroy)(struct dn_schk*); - int (*new_sched)(struct dn_sch_inst *); - int (*free_sched)(struct dn_sch_inst *); - int (*new_fsk)(struct dn_fsk *f); - int (*free_fsk)(struct dn_fsk *f); - int (*new_queue)(struct dn_queue *q); - int (*free_queue)(struct dn_queue *q); - - /* run-time fields */ - int ref_count; /* XXX number of instances in the system */ - SLIST_ENTRY(dn_alg) next; /* Next scheduler in the list */ -}; - -/* MSVC does not support initializers so we need this ugly macro */ -#ifdef _WIN32 -#define _SI(fld) -#else -#define _SI(fld) fld -#endif - -/* - * Additionally, dummynet exports some functions and macros - * to be used by schedulers: - */ - -void dn_free_pkts(struct mbuf *mnext); -int dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop); -/* bound a variable between min and max */ -int ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg); - -/* - * Extract the head of a queue, update stats. Must be the very last - * thing done on a dequeue as the queue itself may go away. - */ -static __inline struct mbuf* -dn_dequeue(struct dn_queue *q) -{ - struct mbuf *m = q->mq.head; - if (m == NULL) - return NULL; - q->mq.head = m->m_nextpkt; - - /* Update stats for the queue */ - q->ni.length--; - q->ni.len_bytes -= m->m_pkthdr.len; - if (q->_si) { - q->_si->ni.length--; - q->_si->ni.len_bytes -= m->m_pkthdr.len; - } - if (q->ni.length == 0) /* queue is now idle */ - q->q_time = dn_cfg.curr_time; - return m; -} - -int dn_sched_modevent(module_t mod, int cmd, void *arg); - -#define DECLARE_DNSCHED_MODULE(name, dnsched) \ - static moduledata_t name##_mod = { \ - #name, dn_sched_modevent, dnsched \ - }; \ - DECLARE_MODULE(name, name##_mod, \ - SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); \ - MODULE_DEPEND(name, dummynet, 3, 3, 3); -#endif /* _DN_SCHED_H */ diff --git a/sys/netinet/ipfw/dn_sched_fifo.c b/sys/netinet/ipfw/dn_sched_fifo.c deleted file mode 100644 index 0bb3800a9c2a3..0000000000000 --- a/sys/netinet/ipfw/dn_sched_fifo.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa - * 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. - */ - -/* - * $FreeBSD$ - */ - -#ifdef _KERNEL -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ */ -#include -#include /* ipfw_rule_ref */ -#include /* flow_id */ -#include -#include -#include -#include -#else -#include -#endif - -/* - * This file implements a FIFO scheduler for a single queue. - * The queue is allocated as part of the scheduler instance, - * and there is a single flowset is in the template which stores - * queue size and policy. - * Enqueue and dequeue use the default library functions. - */ -static int -fifo_enqueue(struct dn_sch_inst *si, struct dn_queue *q, struct mbuf *m) -{ - /* XXX if called with q != NULL and m=NULL, this is a - * re-enqueue from an existing scheduler, which we should - * handle. - */ - return dn_enqueue((struct dn_queue *)(si+1), m, 0); -} - -static struct mbuf * -fifo_dequeue(struct dn_sch_inst *si) -{ - return dn_dequeue((struct dn_queue *)(si + 1)); -} - -static int -fifo_new_sched(struct dn_sch_inst *si) -{ - /* This scheduler instance contains the queue */ - struct dn_queue *q = (struct dn_queue *)(si + 1); - - set_oid(&q->ni.oid, DN_QUEUE, sizeof(*q)); - q->_si = si; - q->fs = si->sched->fs; - return 0; -} - -static int -fifo_free_sched(struct dn_sch_inst *si) -{ - struct dn_queue *q = (struct dn_queue *)(si + 1); - dn_free_pkts(q->mq.head); - bzero(q, sizeof(*q)); - return 0; -} - -/* - * FIFO scheduler descriptor - * contains the type of the scheduler, the name, the size of extra - * data structures, and function pointers. - */ -static struct dn_alg fifo_desc = { - _SI( .type = ) DN_SCHED_FIFO, - _SI( .name = ) "FIFO", - _SI( .flags = ) 0, - - _SI( .schk_datalen = ) 0, - _SI( .si_datalen = ) sizeof(struct dn_queue), - _SI( .q_datalen = ) 0, - - _SI( .enqueue = ) fifo_enqueue, - _SI( .dequeue = ) fifo_dequeue, - _SI( .config = ) NULL, - _SI( .destroy = ) NULL, - _SI( .new_sched = ) fifo_new_sched, - _SI( .free_sched = ) fifo_free_sched, - _SI( .new_fsk = ) NULL, - _SI( .free_fsk = ) NULL, - _SI( .new_queue = ) NULL, - _SI( .free_queue = ) NULL, -}; - -DECLARE_DNSCHED_MODULE(dn_fifo, &fifo_desc); diff --git a/sys/netinet/ipfw/dn_sched_prio.c b/sys/netinet/ipfw/dn_sched_prio.c deleted file mode 100644 index 28f60062cfc5f..0000000000000 --- a/sys/netinet/ipfw/dn_sched_prio.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa - * 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. - */ - -/* - * $FreeBSD$ - */ -#ifdef _KERNEL -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ */ -#include -#include /* ipfw_rule_ref */ -#include /* flow_id */ -#include -#include -#include -#include -#else -#include -#endif - -#define DN_SCHED_PRIO 5 //XXX - -#if !defined(_KERNEL) || !defined(__linux__) -#define test_bit(ix, pData) ((*pData) & (1<<(ix))) -#define __set_bit(ix, pData) (*pData) |= (1<<(ix)) -#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix)) -#endif - -#ifdef __MIPSEL__ -#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix)) -#endif - -/* Size of the array of queues pointers. */ -#define BITMAP_T unsigned long -#define MAXPRIO (sizeof(BITMAP_T) * 8) - -/* - * The scheduler instance contains an array of pointers to queues, - * one for each priority, and a bitmap listing backlogged queues. - */ -struct prio_si { - BITMAP_T bitmap; /* array bitmap */ - struct dn_queue *q_array[MAXPRIO]; /* Array of queues pointers */ -}; - -/* - * If a queue with the same priority is already backlogged, use - * that one instead of the queue passed as argument. - */ -static int -prio_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m) -{ - struct prio_si *si = (struct prio_si *)(_si + 1); - int prio = q->fs->fs.par[0]; - - if (test_bit(prio, &si->bitmap) == 0) { - /* No queue with this priority, insert */ - __set_bit(prio, &si->bitmap); - si->q_array[prio] = q; - } else { /* use the existing queue */ - q = si->q_array[prio]; - } - if (dn_enqueue(q, m, 0)) - return 1; - return 0; -} - -/* - * Packets are dequeued only from the highest priority queue. - * The function ffs() return the lowest bit in the bitmap that rapresent - * the array index (-1) which contains the pointer to the highest priority - * queue. - * After the dequeue, if this queue become empty, it is index is removed - * from the bitmap. - * Scheduler is idle if the bitmap is empty - * - * NOTE: highest priority is 0, lowest is sched->max_prio_q - */ -static struct mbuf * -prio_dequeue(struct dn_sch_inst *_si) -{ - struct prio_si *si = (struct prio_si *)(_si + 1); - struct mbuf *m; - struct dn_queue *q; - int prio; - - if (si->bitmap == 0) /* scheduler idle */ - return NULL; - - prio = ffs(si->bitmap) - 1; - - /* Take the highest priority queue in the scheduler */ - q = si->q_array[prio]; - // assert(q) - - m = dn_dequeue(q); - if (q->mq.head == NULL) { - /* Queue is now empty, remove from scheduler - * and mark it - */ - si->q_array[prio] = NULL; - __clear_bit(prio, &si->bitmap); - } - return m; -} - -static int -prio_new_sched(struct dn_sch_inst *_si) -{ - struct prio_si *si = (struct prio_si *)(_si + 1); - - bzero(si->q_array, sizeof(si->q_array)); - si->bitmap = 0; - - return 0; -} - -static int -prio_new_fsk(struct dn_fsk *fs) -{ - /* Check if the prioritiy is between 0 and MAXPRIO-1 */ - ipdn_bound_var(&fs->fs.par[0], 0, 0, MAXPRIO - 1, "PRIO priority"); - return 0; -} - -static int -prio_new_queue(struct dn_queue *q) -{ - struct prio_si *si = (struct prio_si *)(q->_si + 1); - int prio = q->fs->fs.par[0]; - struct dn_queue *oldq; - - q->ni.oid.subtype = DN_SCHED_PRIO; - - if (q->mq.head == NULL) - return 0; - - /* Queue already full, must insert in the scheduler or append - * mbufs to existing queue. This partly duplicates prio_enqueue - */ - if (test_bit(prio, &si->bitmap) == 0) { - /* No queue with this priority, insert */ - __set_bit(prio, &si->bitmap); - si->q_array[prio] = q; - } else if ( (oldq = si->q_array[prio]) != q) { - /* must append to the existing queue. - * can simply append q->mq.head to q2->... - * and add the counters to those of q2 - */ - oldq->mq.tail->m_nextpkt = q->mq.head; - oldq->mq.tail = q->mq.tail; - oldq->ni.length += q->ni.length; - q->ni.length = 0; - oldq->ni.len_bytes += q->ni.len_bytes; - q->ni.len_bytes = 0; - q->mq.tail = q->mq.head = NULL; - } - return 0; -} - -static int -prio_free_queue(struct dn_queue *q) -{ - int prio = q->fs->fs.par[0]; - struct prio_si *si = (struct prio_si *)(q->_si + 1); - - if (si->q_array[prio] == q) { - si->q_array[prio] = NULL; - __clear_bit(prio, &si->bitmap); - } - return 0; -} - - -static struct dn_alg prio_desc = { - _SI( .type = ) DN_SCHED_PRIO, - _SI( .name = ) "PRIO", - _SI( .flags = ) DN_MULTIQUEUE, - - /* we need extra space in the si and the queue */ - _SI( .schk_datalen = ) 0, - _SI( .si_datalen = ) sizeof(struct prio_si), - _SI( .q_datalen = ) 0, - - _SI( .enqueue = ) prio_enqueue, - _SI( .dequeue = ) prio_dequeue, - - _SI( .config = ) NULL, - _SI( .destroy = ) NULL, - _SI( .new_sched = ) prio_new_sched, - _SI( .free_sched = ) NULL, - - _SI( .new_fsk = ) prio_new_fsk, - _SI( .free_fsk = ) NULL, - - _SI( .new_queue = ) prio_new_queue, - _SI( .free_queue = ) prio_free_queue, -}; - - -DECLARE_DNSCHED_MODULE(dn_prio, &prio_desc); diff --git a/sys/netinet/ipfw/dn_sched_qfq.c b/sys/netinet/ipfw/dn_sched_qfq.c deleted file mode 100644 index be7fba31564e8..0000000000000 --- a/sys/netinet/ipfw/dn_sched_qfq.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Copyright (c) 2010 Fabio Checconi, Luigi Rizzo, Paolo Valente - * 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. - */ - -/* - * $FreeBSD$ - */ - -#ifdef _KERNEL -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ */ -#include -#include /* ipfw_rule_ref */ -#include /* flow_id */ -#include -#include -#include -#include -#else -#include -#endif - -#ifdef QFQ_DEBUG -struct qfq_sched; -static void dump_sched(struct qfq_sched *q, const char *msg); -#define NO(x) x -#else -#define NO(x) -#endif -#define DN_SCHED_QFQ 4 // XXX Where? -typedef unsigned long bitmap; - -/* - * bitmaps ops are critical. Some linux versions have __fls - * and the bitmap ops. Some machines have ffs - */ -#if defined(_WIN32) || (defined(__MIPSEL__) && defined(LINUX_24)) -int fls(unsigned int n) -{ - int i = 0; - for (i = 0; n > 0; n >>= 1, i++) - ; - return i; -} -#endif - -#if !defined(_KERNEL) || defined( __FreeBSD__ ) || defined(_WIN32) || (defined(__MIPSEL__) && defined(LINUX_24)) -static inline unsigned long __fls(unsigned long word) -{ - return fls(word) - 1; -} -#endif - -#if !defined(_KERNEL) || !defined(__linux__) -#ifdef QFQ_DEBUG -int test_bit(int ix, bitmap *p) -{ - if (ix < 0 || ix > 31) - D("bad index %d", ix); - return *p & (1< 31) - D("bad index %d", ix); - *p |= (1< 31) - D("bad index %d", ix); - *p &= ~(1<index = 0 - *.__grp->slot_shift - - where MIN_SLOT_SHIFT is derived by difference from the others. - -The max group index corresponds to Lmax/w_min, where -Lmax=1<group mapping. Class weights are - * in the range [1, QFQ_MAX_WEIGHT], we to map each class i to the - * group with the smallest index that can support the L_i / r_i - * configured for the class. - * - * grp->index is the index of the group; and grp->slot_shift - * is the shift for the corresponding (scaled) sigma_i. - * - * When computing the group index, we do (len<i_wsum) -#define IWSUM ((1< 0; -} - -/* Round a precise timestamp to its slotted value. */ -static inline uint64_t qfq_round_down(uint64_t ts, unsigned int shift) -{ - return ts & ~((1ULL << shift) - 1); -} - -/* return the pointer to the group with lowest index in the bitmap */ -static inline struct qfq_group *qfq_ffs(struct qfq_sched *q, - unsigned long bitmap) -{ - int index = ffs(bitmap) - 1; // zero-based - return &q->groups[index]; -} - -/* - * Calculate a flow index, given its weight and maximum packet length. - * index = log_2(maxlen/weight) but we need to apply the scaling. - * This is used only once at flow creation. - */ -static int qfq_calc_index(uint32_t inv_w, unsigned int maxlen) -{ - uint64_t slot_size = (uint64_t)maxlen *inv_w; - unsigned long size_map; - int index = 0; - - size_map = (unsigned long)(slot_size >> QFQ_MIN_SLOT_SHIFT); - if (!size_map) - goto out; - - index = __fls(size_map) + 1; // basically a log_2() - index -= !(slot_size - (1ULL << (index + QFQ_MIN_SLOT_SHIFT - 1))); - - if (index < 0) - index = 0; - -out: - ND("W = %d, L = %d, I = %d\n", ONE_FP/inv_w, maxlen, index); - return index; -} -/*---- end support functions ----*/ - -/*-------- API calls --------------------------------*/ -/* - * Validate and copy parameters from flowset. - */ -static int -qfq_new_queue(struct dn_queue *_q) -{ - struct qfq_sched *q = (struct qfq_sched *)(_q->_si + 1); - struct qfq_class *cl = (struct qfq_class *)_q; - int i; - uint32_t w; /* approximated weight */ - - /* import parameters from the flowset. They should be correct - * already. - */ - w = _q->fs->fs.par[0]; - cl->lmax = _q->fs->fs.par[1]; - if (!w || w > QFQ_MAX_WEIGHT) { - w = 1; - D("rounding weight to 1"); - } - cl->inv_w = ONE_FP/w; - w = ONE_FP/cl->inv_w; - if (q->wsum + w > QFQ_MAX_WSUM) - return EINVAL; - - i = qfq_calc_index(cl->inv_w, cl->lmax); - cl->grp = &q->groups[i]; - q->wsum += w; - // XXX cl->S = q->V; ? - // XXX compute q->i_wsum - return 0; -} - -/* remove an empty queue */ -static int -qfq_free_queue(struct dn_queue *_q) -{ - struct qfq_sched *q = (struct qfq_sched *)(_q->_si + 1); - struct qfq_class *cl = (struct qfq_class *)_q; - if (cl->inv_w) { - q->wsum -= ONE_FP/cl->inv_w; - cl->inv_w = 0; /* reset weight to avoid run twice */ - } - return 0; -} - -/* Calculate a mask to mimic what would be ffs_from(). */ -static inline unsigned long -mask_from(unsigned long bitmap, int from) -{ - return bitmap & ~((1UL << from) - 1); -} - -/* - * The state computation relies on ER=0, IR=1, EB=2, IB=3 - * First compute eligibility comparing grp->S, q->V, - * then check if someone is blocking us and possibly add EB - */ -static inline unsigned int -qfq_calc_state(struct qfq_sched *q, struct qfq_group *grp) -{ - /* if S > V we are not eligible */ - unsigned int state = qfq_gt(grp->S, q->V); - unsigned long mask = mask_from(q->bitmaps[ER], grp->index); - struct qfq_group *next; - - if (mask) { - next = qfq_ffs(q, mask); - if (qfq_gt(grp->F, next->F)) - state |= EB; - } - - return state; -} - -/* - * In principle - * q->bitmaps[dst] |= q->bitmaps[src] & mask; - * q->bitmaps[src] &= ~mask; - * but we should make sure that src != dst - */ -static inline void -qfq_move_groups(struct qfq_sched *q, unsigned long mask, int src, int dst) -{ - q->bitmaps[dst] |= q->bitmaps[src] & mask; - q->bitmaps[src] &= ~mask; -} - -static inline void -qfq_unblock_groups(struct qfq_sched *q, int index, uint64_t old_finish) -{ - unsigned long mask = mask_from(q->bitmaps[ER], index + 1); - struct qfq_group *next; - - if (mask) { - next = qfq_ffs(q, mask); - if (!qfq_gt(next->F, old_finish)) - return; - } - - mask = (1UL << index) - 1; - qfq_move_groups(q, mask, EB, ER); - qfq_move_groups(q, mask, IB, IR); -} - -/* - * perhaps - * - old_V ^= q->V; - old_V >>= QFQ_MIN_SLOT_SHIFT; - if (old_V) { - ... - } - * - */ -static inline void -qfq_make_eligible(struct qfq_sched *q, uint64_t old_V) -{ - unsigned long mask, vslot, old_vslot; - - vslot = q->V >> QFQ_MIN_SLOT_SHIFT; - old_vslot = old_V >> QFQ_MIN_SLOT_SHIFT; - - if (vslot != old_vslot) { - mask = (2UL << (__fls(vslot ^ old_vslot))) - 1; - qfq_move_groups(q, mask, IR, ER); - qfq_move_groups(q, mask, IB, EB); - } -} - -/* - * XXX we should make sure that slot becomes less than 32. - * This is guaranteed by the input values. - * roundedS is always cl->S rounded on grp->slot_shift bits. - */ -static inline void -qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl, uint64_t roundedS) -{ - uint64_t slot = (roundedS - grp->S) >> grp->slot_shift; - unsigned int i = (grp->front + slot) % QFQ_MAX_SLOTS; - - cl->next = grp->slots[i]; - grp->slots[i] = cl; - __set_bit(slot, &grp->full_slots); -} - -/* - * remove the entry from the slot - */ -static inline void -qfq_front_slot_remove(struct qfq_group *grp) -{ - struct qfq_class **h = &grp->slots[grp->front]; - - *h = (*h)->next; - if (!*h) - __clear_bit(0, &grp->full_slots); -} - -/* - * Returns the first full queue in a group. As a side effect, - * adjust the bucket list so the first non-empty bucket is at - * position 0 in full_slots. - */ -static inline struct qfq_class * -qfq_slot_scan(struct qfq_group *grp) -{ - int i; - - ND("grp %d full %x", grp->index, grp->full_slots); - if (!grp->full_slots) - return NULL; - - i = ffs(grp->full_slots) - 1; // zero-based - if (i > 0) { - grp->front = (grp->front + i) % QFQ_MAX_SLOTS; - grp->full_slots >>= i; - } - - return grp->slots[grp->front]; -} - -/* - * adjust the bucket list. When the start time of a group decreases, - * we move the index down (modulo QFQ_MAX_SLOTS) so we don't need to - * move the objects. The mask of occupied slots must be shifted - * because we use ffs() to find the first non-empty slot. - * This covers decreases in the group's start time, but what about - * increases of the start time ? - * Here too we should make sure that i is less than 32 - */ -static inline void -qfq_slot_rotate(struct qfq_sched *q, struct qfq_group *grp, uint64_t roundedS) -{ - unsigned int i = (grp->S - roundedS) >> grp->slot_shift; - - grp->full_slots <<= i; - grp->front = (grp->front - i) % QFQ_MAX_SLOTS; -} - - -static inline void -qfq_update_eligible(struct qfq_sched *q, uint64_t old_V) -{ - bitmap ineligible; - - ineligible = q->bitmaps[IR] | q->bitmaps[IB]; - if (ineligible) { - if (!q->bitmaps[ER]) { - struct qfq_group *grp; - grp = qfq_ffs(q, ineligible); - if (qfq_gt(grp->S, q->V)) - q->V = grp->S; - } - qfq_make_eligible(q, old_V); - } -} - -/* - * Updates the class, returns true if also the group needs to be updated. - */ -static inline int -qfq_update_class(struct qfq_sched *q, struct qfq_group *grp, - struct qfq_class *cl) -{ - - cl->S = cl->F; - if (cl->_q.mq.head == NULL) { - qfq_front_slot_remove(grp); - } else { - unsigned int len; - uint64_t roundedS; - - len = cl->_q.mq.head->m_pkthdr.len; - cl->F = cl->S + (uint64_t)len * cl->inv_w; - roundedS = qfq_round_down(cl->S, grp->slot_shift); - if (roundedS == grp->S) - return 0; - - qfq_front_slot_remove(grp); - qfq_slot_insert(grp, cl, roundedS); - } - return 1; -} - -static struct mbuf * -qfq_dequeue(struct dn_sch_inst *si) -{ - struct qfq_sched *q = (struct qfq_sched *)(si + 1); - struct qfq_group *grp; - struct qfq_class *cl; - struct mbuf *m; - uint64_t old_V; - - NO(q->loops++;) - if (!q->bitmaps[ER]) { - NO(if (q->queued) - dump_sched(q, "start dequeue");) - return NULL; - } - - grp = qfq_ffs(q, q->bitmaps[ER]); - - cl = grp->slots[grp->front]; - /* extract from the first bucket in the bucket list */ - m = dn_dequeue(&cl->_q); - - if (!m) { - D("BUG/* non-workconserving leaf */"); - return NULL; - } - NO(q->queued--;) - old_V = q->V; - q->V += (uint64_t)m->m_pkthdr.len * IWSUM; - ND("m is %p F 0x%llx V now 0x%llx", m, cl->F, q->V); - - if (qfq_update_class(q, grp, cl)) { - uint64_t old_F = grp->F; - cl = qfq_slot_scan(grp); - if (!cl) { /* group gone, remove from ER */ - __clear_bit(grp->index, &q->bitmaps[ER]); - // grp->S = grp->F + 1; // XXX debugging only - } else { - uint64_t roundedS = qfq_round_down(cl->S, grp->slot_shift); - unsigned int s; - - if (grp->S == roundedS) - goto skip_unblock; - grp->S = roundedS; - grp->F = roundedS + (2ULL << grp->slot_shift); - /* remove from ER and put in the new set */ - __clear_bit(grp->index, &q->bitmaps[ER]); - s = qfq_calc_state(q, grp); - __set_bit(grp->index, &q->bitmaps[s]); - } - /* we need to unblock even if the group has gone away */ - qfq_unblock_groups(q, grp->index, old_F); - } - -skip_unblock: - qfq_update_eligible(q, old_V); - NO(if (!q->bitmaps[ER] && q->queued) - dump_sched(q, "end dequeue");) - - return m; -} - -/* - * Assign a reasonable start time for a new flow k in group i. - * Admissible values for \hat(F) are multiples of \sigma_i - * no greater than V+\sigma_i . Larger values mean that - * we had a wraparound so we consider the timestamp to be stale. - * - * If F is not stale and F >= V then we set S = F. - * Otherwise we should assign S = V, but this may violate - * the ordering in ER. So, if we have groups in ER, set S to - * the F_j of the first group j which would be blocking us. - * We are guaranteed not to move S backward because - * otherwise our group i would still be blocked. - */ -static inline void -qfq_update_start(struct qfq_sched *q, struct qfq_class *cl) -{ - unsigned long mask; - uint64_t limit, roundedF; - int slot_shift = cl->grp->slot_shift; - - roundedF = qfq_round_down(cl->F, slot_shift); - limit = qfq_round_down(q->V, slot_shift) + (1UL << slot_shift); - - if (!qfq_gt(cl->F, q->V) || qfq_gt(roundedF, limit)) { - /* timestamp was stale */ - mask = mask_from(q->bitmaps[ER], cl->grp->index); - if (mask) { - struct qfq_group *next = qfq_ffs(q, mask); - if (qfq_gt(roundedF, next->F)) { - cl->S = next->F; - return; - } - } - cl->S = q->V; - } else { /* timestamp is not stale */ - cl->S = cl->F; - } -} - -static int -qfq_enqueue(struct dn_sch_inst *si, struct dn_queue *_q, struct mbuf *m) -{ - struct qfq_sched *q = (struct qfq_sched *)(si + 1); - struct qfq_group *grp; - struct qfq_class *cl = (struct qfq_class *)_q; - uint64_t roundedS; - int s; - - NO(q->loops++;) - DX(4, "len %d flow %p inv_w 0x%x grp %d", m->m_pkthdr.len, - _q, cl->inv_w, cl->grp->index); - /* XXX verify that the packet obeys the parameters */ - if (m != _q->mq.head) { - if (dn_enqueue(_q, m, 0)) /* packet was dropped */ - return 1; - NO(q->queued++;) - if (m != _q->mq.head) - return 0; - } - /* If reach this point, queue q was idle */ - grp = cl->grp; - qfq_update_start(q, cl); /* adjust start time */ - /* compute new finish time and rounded start. */ - cl->F = cl->S + (uint64_t)(m->m_pkthdr.len) * cl->inv_w; - roundedS = qfq_round_down(cl->S, grp->slot_shift); - - /* - * insert cl in the correct bucket. - * If cl->S >= grp->S we don't need to adjust the - * bucket list and simply go to the insertion phase. - * Otherwise grp->S is decreasing, we must make room - * in the bucket list, and also recompute the group state. - * Finally, if there were no flows in this group and nobody - * was in ER make sure to adjust V. - */ - if (grp->full_slots) { - if (!qfq_gt(grp->S, cl->S)) - goto skip_update; - /* create a slot for this cl->S */ - qfq_slot_rotate(q, grp, roundedS); - /* group was surely ineligible, remove */ - __clear_bit(grp->index, &q->bitmaps[IR]); - __clear_bit(grp->index, &q->bitmaps[IB]); - } else if (!q->bitmaps[ER] && qfq_gt(roundedS, q->V)) - q->V = roundedS; - - grp->S = roundedS; - grp->F = roundedS + (2ULL << grp->slot_shift); // i.e. 2\sigma_i - s = qfq_calc_state(q, grp); - __set_bit(grp->index, &q->bitmaps[s]); - ND("new state %d 0x%x", s, q->bitmaps[s]); - ND("S %llx F %llx V %llx", cl->S, cl->F, q->V); -skip_update: - qfq_slot_insert(grp, cl, roundedS); - - return 0; -} - - -#if 0 -static inline void -qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp, - struct qfq_class *cl, struct qfq_class **pprev) -{ - unsigned int i, offset; - uint64_t roundedS; - - roundedS = qfq_round_down(cl->S, grp->slot_shift); - offset = (roundedS - grp->S) >> grp->slot_shift; - i = (grp->front + offset) % QFQ_MAX_SLOTS; - -#ifdef notyet - if (!pprev) { - pprev = &grp->slots[i]; - while (*pprev && *pprev != cl) - pprev = &(*pprev)->next; - } -#endif - - *pprev = cl->next; - if (!grp->slots[i]) - __clear_bit(offset, &grp->full_slots); -} - -/* - * called to forcibly destroy a queue. - * If the queue is not in the front bucket, or if it has - * other queues in the front bucket, we can simply remove - * the queue with no other side effects. - * Otherwise we must propagate the event up. - * XXX description to be completed. - */ -static void -qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl, - struct qfq_class **pprev) -{ - struct qfq_group *grp = &q->groups[cl->index]; - unsigned long mask; - uint64_t roundedS; - int s; - - cl->F = cl->S; // not needed if the class goes away. - qfq_slot_remove(q, grp, cl, pprev); - - if (!grp->full_slots) { - /* nothing left in the group, remove from all sets. - * Do ER last because if we were blocking other groups - * we must unblock them. - */ - __clear_bit(grp->index, &q->bitmaps[IR]); - __clear_bit(grp->index, &q->bitmaps[EB]); - __clear_bit(grp->index, &q->bitmaps[IB]); - - if (test_bit(grp->index, &q->bitmaps[ER]) && - !(q->bitmaps[ER] & ~((1UL << grp->index) - 1))) { - mask = q->bitmaps[ER] & ((1UL << grp->index) - 1); - if (mask) - mask = ~((1UL << __fls(mask)) - 1); - else - mask = ~0UL; - qfq_move_groups(q, mask, EB, ER); - qfq_move_groups(q, mask, IB, IR); - } - __clear_bit(grp->index, &q->bitmaps[ER]); - } else if (!grp->slots[grp->front]) { - cl = qfq_slot_scan(grp); - roundedS = qfq_round_down(cl->S, grp->slot_shift); - if (grp->S != roundedS) { - __clear_bit(grp->index, &q->bitmaps[ER]); - __clear_bit(grp->index, &q->bitmaps[IR]); - __clear_bit(grp->index, &q->bitmaps[EB]); - __clear_bit(grp->index, &q->bitmaps[IB]); - grp->S = roundedS; - grp->F = roundedS + (2ULL << grp->slot_shift); - s = qfq_calc_state(q, grp); - __set_bit(grp->index, &q->bitmaps[s]); - } - } - qfq_update_eligible(q, q->V); -} -#endif - -static int -qfq_new_fsk(struct dn_fsk *f) -{ - ipdn_bound_var(&f->fs.par[0], 1, 1, QFQ_MAX_WEIGHT, "qfq weight"); - ipdn_bound_var(&f->fs.par[1], 1500, 1, 2000, "qfq maxlen"); - ND("weight %d len %d\n", f->fs.par[0], f->fs.par[1]); - return 0; -} - -/* - * initialize a new scheduler instance - */ -static int -qfq_new_sched(struct dn_sch_inst *si) -{ - struct qfq_sched *q = (struct qfq_sched *)(si + 1); - struct qfq_group *grp; - int i; - - for (i = 0; i <= QFQ_MAX_INDEX; i++) { - grp = &q->groups[i]; - grp->index = i; - grp->slot_shift = QFQ_MTU_SHIFT + FRAC_BITS - - (QFQ_MAX_INDEX - i); - } - return 0; -} - -/* - * QFQ scheduler descriptor - */ -static struct dn_alg qfq_desc = { - _SI( .type = ) DN_SCHED_QFQ, - _SI( .name = ) "QFQ", - _SI( .flags = ) DN_MULTIQUEUE, - - _SI( .schk_datalen = ) 0, - _SI( .si_datalen = ) sizeof(struct qfq_sched), - _SI( .q_datalen = ) sizeof(struct qfq_class) - sizeof(struct dn_queue), - - _SI( .enqueue = ) qfq_enqueue, - _SI( .dequeue = ) qfq_dequeue, - - _SI( .config = ) NULL, - _SI( .destroy = ) NULL, - _SI( .new_sched = ) qfq_new_sched, - _SI( .free_sched = ) NULL, - _SI( .new_fsk = ) qfq_new_fsk, - _SI( .free_fsk = ) NULL, - _SI( .new_queue = ) qfq_new_queue, - _SI( .free_queue = ) qfq_free_queue, -}; - -DECLARE_DNSCHED_MODULE(dn_qfq, &qfq_desc); - -#ifdef QFQ_DEBUG -static void -dump_groups(struct qfq_sched *q, uint32_t mask) -{ - int i, j; - - for (i = 0; i < QFQ_MAX_INDEX + 1; i++) { - struct qfq_group *g = &q->groups[i]; - - if (0 == (mask & (1<slots[j]) - D(" bucket %d %p", j, g->slots[j]); - } - D("full_slots 0x%x", g->full_slots); - D(" %2d S 0x%20llx F 0x%llx %c", i, - g->S, g->F, - mask & (1<loops, q->queued, q->V); - D(" ER 0x%08x", q->bitmaps[ER]); - D(" EB 0x%08x", q->bitmaps[EB]); - D(" IR 0x%08x", q->bitmaps[IR]); - D(" IB 0x%08x", q->bitmaps[IB]); - dump_groups(q, 0xffffffff); -}; -#endif /* QFQ_DEBUG */ diff --git a/sys/netinet/ipfw/dn_sched_rr.c b/sys/netinet/ipfw/dn_sched_rr.c deleted file mode 100644 index 1bbd80057c3f4..0000000000000 --- a/sys/netinet/ipfw/dn_sched_rr.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa - * 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. - */ - -/* - * $FreeBSD$ - */ - -#ifdef _KERNEL -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ */ -#include -#include /* ipfw_rule_ref */ -#include /* flow_id */ -#include -#include -#include -#include -#else -#include -#endif - -#define DN_SCHED_RR 3 // XXX Where? - -struct rr_queue { - struct dn_queue q; /* Standard queue */ - int status; /* 1: queue is in the list */ - int credit; /* Number of bytes to transmit */ - int quantum; /* quantum * C */ - struct rr_queue *qnext; /* */ -}; - -/* struct rr_schk contains global config parameters - * and is right after dn_schk - */ -struct rr_schk { - int min_q; /* Min quantum */ - int max_q; /* Max quantum */ - int q_bytes; /* Bytes per quantum */ -}; - -/* per-instance round robin list, right after dn_sch_inst */ -struct rr_si { - struct rr_queue *head, *tail; /* Pointer to current queue */ -}; - -/* Append a queue to the rr list */ -static inline void -rr_append(struct rr_queue *q, struct rr_si *si) -{ - q->status = 1; /* mark as in-rr_list */ - q->credit = q->quantum; /* initialize credit */ - - /* append to the tail */ - if (si->head == NULL) - si->head = q; - else - si->tail->qnext = q; - si->tail = q; /* advance the tail pointer */ - q->qnext = si->head; /* make it circular */ -} - -/* Remove the head queue from circular list. */ -static inline void -rr_remove_head(struct rr_si *si) -{ - if (si->head == NULL) - return; /* empty queue */ - si->head->status = 0; - - if (si->head == si->tail) { - si->head = si->tail = NULL; - return; - } - - si->head = si->head->qnext; - si->tail->qnext = si->head; -} - -/* Remove a queue from circular list. - * XXX see if ti can be merge with remove_queue() - */ -static inline void -remove_queue_q(struct rr_queue *q, struct rr_si *si) -{ - struct rr_queue *prev; - - if (q->status != 1) - return; - if (q == si->head) { - rr_remove_head(si); - return; - } - - for (prev = si->head; prev; prev = prev->qnext) { - if (prev->qnext != q) - continue; - prev->qnext = q->qnext; - if (q == si->tail) - si->tail = prev; - q->status = 0; - break; - } -} - - -static inline void -next_pointer(struct rr_si *si) -{ - if (si->head == NULL) - return; /* empty queue */ - - si->head = si->head->qnext; - si->tail = si->tail->qnext; -} - -static int -rr_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m) -{ - struct rr_si *si; - struct rr_queue *rrq; - - if (m != q->mq.head) { - if (dn_enqueue(q, m, 0)) /* packet was dropped */ - return 1; - if (m != q->mq.head) - return 0; - } - - /* If reach this point, queue q was idle */ - si = (struct rr_si *)(_si + 1); - rrq = (struct rr_queue *)q; - - if (rrq->status == 1) /* Queue is already in the queue list */ - return 0; - - /* Insert the queue in the queue list */ - rr_append(rrq, si); - - return 0; -} - -static struct mbuf * -rr_dequeue(struct dn_sch_inst *_si) -{ - /* Access scheduler instance private data */ - struct rr_si *si = (struct rr_si *)(_si + 1); - struct rr_queue *rrq; - uint64_t len; - - while ( (rrq = si->head) ) { - struct mbuf *m = rrq->q.mq.head; - if ( m == NULL) { - /* empty queue, remove from list */ - rr_remove_head(si); - continue; - } - len = m->m_pkthdr.len; - - if (len > rrq->credit) { - /* Packet too big */ - rrq->credit += rrq->quantum; - /* Try next queue */ - next_pointer(si); - } else { - rrq->credit -= len; - return dn_dequeue(&rrq->q); - } - } - - /* no packet to dequeue*/ - return NULL; -} - -static int -rr_config(struct dn_schk *_schk) -{ - struct rr_schk *schk = (struct rr_schk *)(_schk + 1); - ND("called"); - - /* use reasonable quantums (64..2k bytes, default 1500) */ - schk->min_q = 64; - schk->max_q = 2048; - schk->q_bytes = 1500; /* quantum */ - - return 0; -} - -static int -rr_new_sched(struct dn_sch_inst *_si) -{ - struct rr_si *si = (struct rr_si *)(_si + 1); - - ND("called"); - si->head = si->tail = NULL; - - return 0; -} - -static int -rr_free_sched(struct dn_sch_inst *_si) -{ - ND("called"); - /* Nothing to do? */ - return 0; -} - -static int -rr_new_fsk(struct dn_fsk *fs) -{ - struct rr_schk *schk = (struct rr_schk *)(fs->sched + 1); - /* par[0] is the weight, par[1] is the quantum step */ - ipdn_bound_var(&fs->fs.par[0], 1, - 1, 65536, "RR weight"); - ipdn_bound_var(&fs->fs.par[1], schk->q_bytes, - schk->min_q, schk->max_q, "RR quantum"); - return 0; -} - -static int -rr_new_queue(struct dn_queue *_q) -{ - struct rr_queue *q = (struct rr_queue *)_q; - - _q->ni.oid.subtype = DN_SCHED_RR; - - q->quantum = _q->fs->fs.par[0] * _q->fs->fs.par[1]; - ND("called, q->quantum %d", q->quantum); - q->credit = q->quantum; - q->status = 0; - - if (_q->mq.head != NULL) { - /* Queue NOT empty, insert in the queue list */ - rr_append(q, (struct rr_si *)(_q->_si + 1)); - } - return 0; -} - -static int -rr_free_queue(struct dn_queue *_q) -{ - struct rr_queue *q = (struct rr_queue *)_q; - - ND("called"); - if (q->status == 1) { - struct rr_si *si = (struct rr_si *)(_q->_si + 1); - remove_queue_q(q, si); - } - return 0; -} - -/* - * RR scheduler descriptor - * contains the type of the scheduler, the name, the size of the - * structures and function pointers. - */ -static struct dn_alg rr_desc = { - _SI( .type = ) DN_SCHED_RR, - _SI( .name = ) "RR", - _SI( .flags = ) DN_MULTIQUEUE, - - _SI( .schk_datalen = ) 0, - _SI( .si_datalen = ) sizeof(struct rr_si), - _SI( .q_datalen = ) sizeof(struct rr_queue) - sizeof(struct dn_queue), - - _SI( .enqueue = ) rr_enqueue, - _SI( .dequeue = ) rr_dequeue, - - _SI( .config = ) rr_config, - _SI( .destroy = ) NULL, - _SI( .new_sched = ) rr_new_sched, - _SI( .free_sched = ) rr_free_sched, - _SI( .new_fsk = ) rr_new_fsk, - _SI( .free_fsk = ) NULL, - _SI( .new_queue = ) rr_new_queue, - _SI( .free_queue = ) rr_free_queue, -}; - - -DECLARE_DNSCHED_MODULE(dn_rr, &rr_desc); diff --git a/sys/netinet/ipfw/dn_sched_wf2q.c b/sys/netinet/ipfw/dn_sched_wf2q.c deleted file mode 100644 index 7f16719d37880..0000000000000 --- a/sys/netinet/ipfw/dn_sched_wf2q.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa - * Copyright (c) 2000-2002 Luigi Rizzo, Universita` di Pisa - * 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. - */ - -/* - * $FreeBSD$ - */ - -#ifdef _KERNEL -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ */ -#include -#include /* ipfw_rule_ref */ -#include /* flow_id */ -#include -#include -#include -#include -#else -#include -#endif - -#ifndef MAX64 -#define MAX64(x,y) (( (int64_t) ( (y)-(x) )) > 0 ) ? (y) : (x) -#endif - -/* - * timestamps are computed on 64 bit using fixed point arithmetic. - * LMAX_BITS, WMAX_BITS are the max number of bits for the packet len - * and sum of weights, respectively. FRAC_BITS is the number of - * fractional bits. We want FRAC_BITS >> WMAX_BITS to avoid too large - * errors when computing the inverse, FRAC_BITS < 32 so we can do 1/w - * using an unsigned 32-bit division, and to avoid wraparounds we need - * LMAX_BITS + WMAX_BITS + FRAC_BITS << 64 - * As an example - * FRAC_BITS = 26, LMAX_BITS=14, WMAX_BITS = 19 - */ -#ifndef FRAC_BITS -#define FRAC_BITS 28 /* shift for fixed point arithmetic */ -#define ONE_FP (1UL << FRAC_BITS) -#endif - -/* - * Private information for the scheduler instance: - * sch_heap (key is Finish time) returns the next queue to serve - * ne_heap (key is Start time) stores not-eligible queues - * idle_heap (key=start/finish time) stores idle flows. It must - * support extract-from-middle. - * A flow is only in 1 of the three heaps. - * XXX todo: use a more efficient data structure, e.g. a tree sorted - * by F with min_subtree(S) in each node - */ -struct wf2qp_si { - struct dn_heap sch_heap; /* top extract - key Finish time */ - struct dn_heap ne_heap; /* top extract - key Start time */ - struct dn_heap idle_heap; /* random extract - key Start=Finish time */ - uint64_t V; /* virtual time */ - uint32_t inv_wsum; /* inverse of sum of weights */ - uint32_t wsum; /* sum of weights */ -}; - -struct wf2qp_queue { - struct dn_queue _q; - uint64_t S, F; /* start time, finish time */ - uint32_t inv_w; /* ONE_FP / weight */ - int32_t heap_pos; /* position (index) of struct in heap */ -}; - -/* - * This file implements a WF2Q+ scheduler as it has been in dummynet - * since 2000. - * The scheduler supports per-flow queues and has O(log N) complexity. - * - * WF2Q+ needs to drain entries from the idle heap so that we - * can keep the sum of weights up to date. We can do it whenever - * we get a chance, or periodically, or following some other - * strategy. The function idle_check() drains at most N elements - * from the idle heap. - */ -static void -idle_check(struct wf2qp_si *si, int n, int force) -{ - struct dn_heap *h = &si->idle_heap; - while (n-- > 0 && h->elements > 0 && - (force || DN_KEY_LT(HEAP_TOP(h)->key, si->V))) { - struct dn_queue *q = HEAP_TOP(h)->object; - struct wf2qp_queue *alg_fq = (struct wf2qp_queue *)q; - - heap_extract(h, NULL); - /* XXX to let the flowset delete the queue we should - * mark it as 'unused' by the scheduler. - */ - alg_fq->S = alg_fq->F + 1; /* Mark timestamp as invalid. */ - si->wsum -= q->fs->fs.par[0]; /* adjust sum of weights */ - if (si->wsum > 0) - si->inv_wsum = ONE_FP/si->wsum; - } -} - -static int -wf2qp_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m) -{ - struct dn_fsk *fs = q->fs; - struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); - struct wf2qp_queue *alg_fq; - uint64_t len = m->m_pkthdr.len; - - if (m != q->mq.head) { - if (dn_enqueue(q, m, 0)) /* packet was dropped */ - return 1; - if (m != q->mq.head) /* queue was already busy */ - return 0; - } - - /* If reach this point, queue q was idle */ - alg_fq = (struct wf2qp_queue *)q; - - if (DN_KEY_LT(alg_fq->F, alg_fq->S)) { - /* Fbrand new queue. */ - alg_fq->S = si->V; /* init start time */ - si->wsum += fs->fs.par[0]; /* add weight of new queue. */ - si->inv_wsum = ONE_FP/si->wsum; - } else { /* if it was idle then it was in the idle heap */ - heap_extract(&si->idle_heap, q); - alg_fq->S = MAX64(alg_fq->F, si->V); /* compute new S */ - } - alg_fq->F = alg_fq->S + len * alg_fq->inv_w; - - /* if nothing is backlogged, make sure this flow is eligible */ - if (si->ne_heap.elements == 0 && si->sch_heap.elements == 0) - si->V = MAX64(alg_fq->S, si->V); - - /* - * Look at eligibility. A flow is not eligibile if S>V (when - * this happens, it means that there is some other flow already - * scheduled for the same pipe, so the sch_heap cannot be - * empty). If the flow is not eligible we just store it in the - * ne_heap. Otherwise, we store in the sch_heap. - * Note that for all flows in sch_heap (SCH), S_i <= V, - * and for all flows in ne_heap (NEH), S_i > V. - * So when we need to compute max(V, min(S_i)) forall i in - * SCH+NEH, we only need to look into NEH. - */ - if (DN_KEY_LT(si->V, alg_fq->S)) { - /* S>V means flow Not eligible. */ - if (si->sch_heap.elements == 0) - D("++ ouch! not eligible but empty scheduler!"); - heap_insert(&si->ne_heap, alg_fq->S, q); - } else { - heap_insert(&si->sch_heap, alg_fq->F, q); - } - return 0; -} - -/* XXX invariant: sch > 0 || V >= min(S in neh) */ -static struct mbuf * -wf2qp_dequeue(struct dn_sch_inst *_si) -{ - /* Access scheduler instance private data */ - struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); - struct mbuf *m; - struct dn_queue *q; - struct dn_heap *sch = &si->sch_heap; - struct dn_heap *neh = &si->ne_heap; - struct wf2qp_queue *alg_fq; - - if (sch->elements == 0 && neh->elements == 0) { - /* we have nothing to do. We could kill the idle heap - * altogether and reset V - */ - idle_check(si, 0x7fffffff, 1); - si->V = 0; - si->wsum = 0; /* should be set already */ - return NULL; /* quick return if nothing to do */ - } - idle_check(si, 1, 0); /* drain something from the idle heap */ - - /* make sure at least one element is eligible, bumping V - * and moving entries that have become eligible. - * We need to repeat the first part twice, before and - * after extracting the candidate, or enqueue() will - * find the data structure in a wrong state. - */ - m = NULL; - for(;;) { - /* - * Compute V = max(V, min(S_i)). Remember that all elements - * in sch have by definition S_i <= V so if sch is not empty, - * V is surely the max and we must not update it. Conversely, - * if sch is empty we only need to look at neh. - * We don't need to move the queues, as it will be done at the - * next enqueue - */ - if (sch->elements == 0 && neh->elements > 0) { - si->V = MAX64(si->V, HEAP_TOP(neh)->key); - } - while (neh->elements > 0 && - DN_KEY_LEQ(HEAP_TOP(neh)->key, si->V)) { - q = HEAP_TOP(neh)->object; - alg_fq = (struct wf2qp_queue *)q; - heap_extract(neh, NULL); - heap_insert(sch, alg_fq->F, q); - } - if (m) /* pkt found in previous iteration */ - break; - /* ok we have at least one eligible pkt */ - q = HEAP_TOP(sch)->object; - alg_fq = (struct wf2qp_queue *)q; - m = dn_dequeue(q); - heap_extract(sch, NULL); /* Remove queue from heap. */ - si->V += (uint64_t)(m->m_pkthdr.len) * si->inv_wsum; - alg_fq->S = alg_fq->F; /* Update start time. */ - if (q->mq.head == 0) { /* not backlogged any more. */ - heap_insert(&si->idle_heap, alg_fq->F, q); - } else { /* Still backlogged. */ - /* Update F, store in neh or sch */ - uint64_t len = q->mq.head->m_pkthdr.len; - alg_fq->F += len * alg_fq->inv_w; - if (DN_KEY_LEQ(alg_fq->S, si->V)) { - heap_insert(sch, alg_fq->F, q); - } else { - heap_insert(neh, alg_fq->S, q); - } - } - } - return m; -} - -static int -wf2qp_new_sched(struct dn_sch_inst *_si) -{ - struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); - int ofs = offsetof(struct wf2qp_queue, heap_pos); - - /* all heaps support extract from middle */ - if (heap_init(&si->idle_heap, 16, ofs) || - heap_init(&si->sch_heap, 16, ofs) || - heap_init(&si->ne_heap, 16, ofs)) { - heap_free(&si->ne_heap); - heap_free(&si->sch_heap); - heap_free(&si->idle_heap); - return ENOMEM; - } - return 0; -} - -static int -wf2qp_free_sched(struct dn_sch_inst *_si) -{ - struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); - - heap_free(&si->sch_heap); - heap_free(&si->ne_heap); - heap_free(&si->idle_heap); - - return 0; -} - -static int -wf2qp_new_fsk(struct dn_fsk *fs) -{ - ipdn_bound_var(&fs->fs.par[0], 1, - 1, 100, "WF2Q+ weight"); - return 0; -} - -static int -wf2qp_new_queue(struct dn_queue *_q) -{ - struct wf2qp_queue *q = (struct wf2qp_queue *)_q; - - _q->ni.oid.subtype = DN_SCHED_WF2QP; - q->F = 0; /* not strictly necessary */ - q->S = q->F + 1; /* mark timestamp as invalid. */ - q->inv_w = ONE_FP / _q->fs->fs.par[0]; - if (_q->mq.head != NULL) { - wf2qp_enqueue(_q->_si, _q, _q->mq.head); - } - return 0; -} - -/* - * Called when the infrastructure removes a queue (e.g. flowset - * is reconfigured). Nothing to do if we did not 'own' the queue, - * otherwise remove it from the right heap and adjust the sum - * of weights. - */ -static int -wf2qp_free_queue(struct dn_queue *q) -{ - struct wf2qp_queue *alg_fq = (struct wf2qp_queue *)q; - struct wf2qp_si *si = (struct wf2qp_si *)(q->_si + 1); - - if (alg_fq->S >= alg_fq->F + 1) - return 0; /* nothing to do, not in any heap */ - si->wsum -= q->fs->fs.par[0]; - if (si->wsum > 0) - si->inv_wsum = ONE_FP/si->wsum; - - /* extract from the heap. XXX TODO we may need to adjust V - * to make sure the invariants hold. - */ - if (q->mq.head == NULL) { - heap_extract(&si->idle_heap, q); - } else if (DN_KEY_LT(si->V, alg_fq->S)) { - heap_extract(&si->ne_heap, q); - } else { - heap_extract(&si->sch_heap, q); - } - return 0; -} - -/* - * WF2Q+ scheduler descriptor - * contains the type of the scheduler, the name, the size of the - * structures and function pointers. - */ -static struct dn_alg wf2qp_desc = { - _SI( .type = ) DN_SCHED_WF2QP, - _SI( .name = ) "WF2Q+", - _SI( .flags = ) DN_MULTIQUEUE, - - /* we need extra space in the si and the queue */ - _SI( .schk_datalen = ) 0, - _SI( .si_datalen = ) sizeof(struct wf2qp_si), - _SI( .q_datalen = ) sizeof(struct wf2qp_queue) - - sizeof(struct dn_queue), - - _SI( .enqueue = ) wf2qp_enqueue, - _SI( .dequeue = ) wf2qp_dequeue, - - _SI( .config = ) NULL, - _SI( .destroy = ) NULL, - _SI( .new_sched = ) wf2qp_new_sched, - _SI( .free_sched = ) wf2qp_free_sched, - - _SI( .new_fsk = ) wf2qp_new_fsk, - _SI( .free_fsk = ) NULL, - - _SI( .new_queue = ) wf2qp_new_queue, - _SI( .free_queue = ) wf2qp_free_queue, -}; - - -DECLARE_DNSCHED_MODULE(dn_wf2qp, &wf2qp_desc); diff --git a/sys/netinet/ipfw/dummynet.txt b/sys/netinet/ipfw/dummynet.txt deleted file mode 100644 index e8c97255aeff9..0000000000000 --- a/sys/netinet/ipfw/dummynet.txt +++ /dev/null @@ -1,860 +0,0 @@ -# -# $FreeBSD$ -# - -Notes on the internal structure of dummynet (2010 version) -by Riccardo Panicucci and Luigi Rizzo -Work supported by the EC project ONELAB2 - - -********* -* INDEX * -********* -Implementation of new dummynet - Internal structure - Files -Packet arrival - The reconfiguration routine -dummynet_task() -Configuration - Add a pipe - Add a scheduler - Add a flowset -Listing object -Delete of object - Delete a pipe - Delete a flowset - Delete a scheduler -Compatibility with FreeBSD7.2 and FreeBSD 8 ipfw binary - ip_dummynet_glue.c - ip_fw_glue.c -How to configure dummynet -How to implement a new scheduler - - - -OPEN ISSUES ------------------------------- -20100131 deleting RR causes infinite loop - presumably in the rr_free_queue() call -- seems to hang - forever when deleting a live flow ------------------------------- - -Dummynet is a traffic shaper and network emulator. Packets are -selected by an external filter such as ipfw, and passed to the emulator -with a tag such as "pipe 10" or "queue 5" which tells what to -do with the packet. As an example - - ipfw add queue 5 icmp from 10.0.0.2 to all - -All packets with the same tag belong to a "flowset", or a set -of flows which can be further partitioned according to a mask. -Flowsets are then passed to a scheduler for processing. The -association of flowsets and schedulers is configurable e.g. - - ipfw queue 5 config sched 10 weight 3 flow_mask xxxx - ipfw queue 8 config sched 10 weight 1 ... - ipfw queue 3 config sched 20 weight 1 ... - -"sched 10" represents one or more scheduler instances, -selected through a mask on the 5-tuple itself. - - ipfw sched 20 config type FIFO sched_mask yyy ... - -There are in fact two masks applied to each packet: -+ the "sched_mask" sends packets arriving to a scheduler_id to - one of many instances. -+ the "flow_mask" together with the flowset_id is used to - collect packets into independent flows on each scheduler. - -As an example, we can have - ipfw queue 5 config sched 10 flow_mask src-ip 0x000000ff - ipfw sched 10 config type WF2Q+ sched_mask src-ip 0xffffff00 - -means that sched 10 will have one instance per /24 source subnet, -and within that, each individual source will be a flow. - -Internal structure ------------------ -Dummynet-related data is split into several data structures, -part of them constituting the userland-kernel API, and others -specific to the kernel. -NOTE: for up-to-date details please look at the relevant source - headers (ip_dummynet.h, ip_dn_private.h, dn_sched.h) - -USERLAND-KERNEL API (ip_dummynet.h) - - struct dn_link: - contains data about the physical link such as - bandwith, delay, burst size; - - struct dn_fs: - describes a flowset, i.e. a template for queues. - Main parameters are the scheduler we attach to, a flow_mask, - buckets, queue size, plr, weight, and other scheduler-specific - parameters. - - struct dn_flow - contains information on a flow, including masks and - statistics - - struct dn_sch: - defines a scheduler (and a link attached to it). - Parameters include scheduler type, sched_mask, number of - buckets, and possibly other scheduler-specific parameters, - - struct dn_profile: - fields to simulate a delay profile - - -KERNEL REPRESENTATION (ip_dn_private.h) - - struct mq - a queue of mbufs with head and tail. - - struct dn_queue - individual queue of packets, created by a flowset using - flow_mask and attached to a scheduler instance selected - through sched_mask. - A dn_queue has a pointer to the dn_fsk (which in turn counts - how many queues point to it), a pointer to the - dn_sch_inst it attaches to, and is in a hash table in the - flowset. scheduler instances also should store queues in - their own containers used for scheduling (lists, trees, etc.) - CREATE: done on packet arrivals when a flow matches a flowset. - DELETE: done only when deleting the parent dn_sch_inst - or draining memory. - - struct dn_fsk - includes a dn_fs; a pointer to the dn_schk; a link field - for the list of dn_fsk attached to the same scheduler, - or for the unlinked list; - a refcount for the number of queues pointing to it; - The dn_fsk is in a hash table, fshash. - CREATE: done on configuration commands. - DELETE: on configuration commands. - - struct dn_sch_inst - a scheduler instance, created from a dn_schk applying sched_mask. - Contains a delay line, a reference to the parent, and scheduler- - specific info. Both dn_sch_inst and its delay line can be in the - evheap if they have events to be processed. - CREATE: created from a dn_schk applying sched_mask - DELETE: configuration command delete a scheduler which in turn - sweeps the hash table of instances deleting them - - struct dn_schk - includes dn_sch, dn_link, a pointer to dn_profile, - a hash table of dn_sch_inst, a list of dn_fsk - attached to it. - CREATE: configuration command. If there are flowsets that - refer to this number, they are attached and moved - to the hash table - DELETE: manual, see dn_sch_inst - - - fshash schedhash - +---------------+ sched +--------------+ - | sched-------------------->| NEW_SCHK| - -<----*sch_chain |<-----------------*fsk_list | - |NEW_FSK |<----. | [dn_link] | - +---------------+ | +--------------+ - |qht (hash) | | | siht(hash) | - | [dn_queue] | | | [dn_si] | - | [dn_queue] | | | [dn_si] | - | ... | | | ... | - | +--------+ | | | +---------+ | - | |dn_queue| | | | |dn_si | | - | | fs *----------' | | | | - | | si *---------------------->| | | - | +---------+ | | +---------+ | - +---------------+ +--------------+ - -The following global data structures contain all -schedulers and flowsets. - -- schedhash[x]: contains all scheduler templates in the system. - Looked up only on manual configurations, where flowsets - are attached to matching schedulers. - We have one entry per 'sched X config' command - (plus one for each 'pipe X config'). - -- fshash[x]: contains all flowsets. - We do a lookup on this for each packet. - We have one entry for each 'queue X config' - (plus one for each 'pipe X config'). - -Additionally, a list that contains all unlinked flowset: -- fsu: contains flowset that are not linked with any scheduler. - Flowset are put in this list when they refer to a non - existing scheduler. - We don't need an efficient data structure as we never search - here on a packet arrivals. - -Scheduler instances and the delay lines associated with each scheduler -instance need to be woken up at certain times. Because we have many -such objects, we keep them in a priority heap (system_heap). - -Almost all objects in this implementation are preceded by a structure -(struct dn_id) which makes it easier to identify them. - - -Files ------ -The dummynet code is split in several files. -All kernel code is in sys/netinet/ipfw except ip_dummynet.h -All userland code is in sbin/ipfw. -Files are -- sys/netinet/ip_dummynet.h defines the kernel-userland API -- ip_dn_private.h contains the kernel-specific APIs - and data structures -- dn_sched.h defines the scheduler API -- ip_dummynet.c cointains module glue and sockopt handlers, with all - functions to configure and list objects. -- ip_dn_io.c contains the functions directly related to packet processing, - and run in the critical path. It also contains some functions - exported to the schedulers. -- dn_heap.[ch] implement a binary heap and a generic hash table -- dn_sched_* implement the various scheduler modules - -- dummynet.c is the file used to implement the user side of dummynet. - It contains the function to parsing command line, and functions to - show the output of dummynet objects. -Moreover, there are two new file (ip_dummynet_glue.c and ip_fw_glue.c) that -are used to allow compatibility with the "ipfw" binary from FreeBSD 7.2 and -FreeBSD 8. - -LOCKING -======= -At the moment the entire processing occurs under a single lock -which is expected to be acquired in exclusive mode -DN_BH_WLOCK() / DN_BH_WUNLOCK(). - -In perspective we aim at the following: -- the 'busy' flag, 'pending' list and all structures modified by packet - arrivals and departures are protected by the BH_WLOCK. - This is normally acquired in exclusive mode by the packet processing - functions for short sections of code (exception -- the timer). - If 'busy' is not set, we can do regular packet processing. - If 'busy' is set, no pieces can be accessed. - We must enqueue the packet on 'pending' and return immediately. - -- the 'busy' flag is set/cleared by long sections of code as follows: - UH_WLOCK(); KASSERT(busy == 0); - BH_WLOCK(); busy=1; BH_WUNLOCK(); - ... do processing ... - BH_WLOCK(); busy=0; drain_queue(pending); BH_WUNLOCK(); - UH_WUNLOCK(); - this normally happens when the upper half has something heavy - to do. The prologue and epilogue are not in the critical path. - -- the main containers (fshash, schedhash, ...) are protected by - UH_WLOCK. - -Packet processing -================= -A packet enters dummynet through dummynet_io(). We first lookup -the flowset number in fshash using dn_ht_find(), then find the scheduler -instance using ipdn_si_find(), then possibly identify the correct -queue with ipdn_q_find(). -If successful, we call the scheduler's enqueue function(), and -if needed start I/O on the link calling serve_sched(). -If the packet can be returned immediately, this is done by -leaving *m0 set. Otherwise, the packet is absorbed by dummynet -and we simply return, possibly with some appropriate error code. - -Reconfiguration ---------------- -Reconfiguration is the complex part of the system because we need to -keep track of the various objects and containers. -At the moment we do not use reference counts for objects so all -processing must be done under a lock. - -The main entry points for configuration is the ip_dn_ctl() handler -for the IP_DUMMYNET3 sockopt (others are provided only for backward -compatibility). Modifications to the configuration call do_config(). -The argument is a sequence of blocks each starting with a struct dn_id -which specifies its content. -The first dn_id must contain as obj.id the DN_API_VERSION -The obj.type is DN_CMD_CONFIG (followed by actual objects), -DN_CMD_DELETE (with the correct subtype and list of objects), or -DN_CMD_FLUSH. - -DN_CMD_CONFIG is followed by objects to add/reconfigure. In general, -if an object already exists it is reconfigured, otherwise it is -created in a way that keeps the structure consistent. -We have the following objects in the system, normally numbered with -an identifier N between 1 and 65535. For certain objects we have -"shadow" copies numbered I+NMAX and I+ 2*NMAX which are used to -implement certain backward compatibility features. - -In general we have the following linking - - TRADITIONAL DUMMYNET QUEUES "queue N config ... pipe M ..." - corresponds to a dn_fs object numbered N - - TRADITIONAL DUMMYNET PIPES "pipe N config ..." - dn_fs N+2*NMAX --> dn_sch N+NMAX type FIFO --> dn_link N+NMAX - - GENERIC SCHEDULER "sched N config ... " - [dn_fs N+NMAX] --> dn_sch N --> dn_link N - The flowset N+NMAX is created only if the scheduler is not - of type MULTIQUEUE. - - DELAY PROFILE "pipe N config profile ..." - it is always attached to an existing dn_link N - -Because traditional dummynet pipes actually configure both a -'standalone' instance and one that can be used by queues, -we do the following: - - "pipe N config ..." configures: - dn_sched N type WF2Q+ - dn_sched N+NMAX type FIFO - dn_fs N+2NMAX attached to dn_sched N+NMAX - dn_pipe N - dn_pipe N+NMAX - - "queue N config" configures - dn_fs N - - "sched N config" configures - dn_sched N type as desired - dn_fs N+NMAX attached to dn_sched N - - -dummynet_task() -=============== -The dummynet_task() function is the main dummynet processing function and is -called every tick. This function first calculate the new current time, then -it checks if it is the time to wake up object from the system_heap comparing -the current time and the key of the heap. Two types of object (really the -heap contains pointer to objects) are in the -system_heap: - -- scheduler instance: if a scheduler instance is waked up, the dequeue() - function is called until it has credit. If the dequeue() returns packets, - the scheduler instance is inserted in the heap with a new key depending of - the data that will be send out. If the scheduler instance remains with - some credit, it means that is hasn't other packet to send and so the - instance is no longer inserted in the heap. - - If the scheduler instance extracted from the heap has the DELETE flag set, - the dequeue() is not called and the instance is destroyed now. - -- delay line: when extracting a delay line, the function transmit_event() is - called to send out packet from delay line. - - If the scheduler instance associated with this delay line doesn't exists, - the delay line will be delete now. - -Configuration -============= -To create a pipe, queue or scheduler, the user should type commands like: -"ipfw pipe x config" -"ipfw queue y config pipe x" -"ipfw pipe x config sched " - -The userland side of dummynet will prepare a buffer contains data to pass to -kernel side. -The buffer contains all struct needed to configure an object. In more detail, -to configure a pipe all three structs (dn_link, dn_sch, dn_fs) are needed, -plus the delay profile struct if the pipe has a delay profile. - -If configuring a scheduler only the struct dn_sch is wrote in the buffer, -while if configuring a flowset only the dn_fs struct is wrote. - -The first struct in the buffer contains the type of command request, that is -if it is configuring a pipe, a queue, or a scheduler. Then there are structs -need to configure the object, and finally there is the struct that mark -the end of the buffer. - -To support the insertion of pipe and queue using the old syntax, when adding -a pipe it's necessary to create a FIFO flowset and a FIFO scheduler, which -have a number x + DN_PIPEOFFSET. - -Add a pipe ----------- -A pipe is only a template for a link. -If the pipe already exists, parameters are updated. If a delay profile exists -it is deleted and a new one is created. -If the pipe doesn't exist a new one is created. After the creation, the -flowset unlinked list is scanned to see if there are some flowset that would -be linked with this pipe. If so, these flowset will be of wf2q+ type (for -compatibility) and a new wf2q+ scheduler is created now. - -Add a scheduler ---------------- -If the scheduler already exists, and the type and the mask are the same, the -scheduler is simply reconfigured calling the config_scheduler() scheduler -function with the RECONFIGURE flag active. -If the type or the mask differ, it is necessary to delete the old scheduler -and create a new one. -If the scheduler doesn't exists, a new one is created. If the scheduler has -a mask, the hash table is created to store pointers to scheduler instances. -When a new scheduler is created, it is necessary to scan the unlinked -flowset list to search eventually flowset that would be linked with this -scheduler number. If some are found, flowsets became of the type of this -scheduler and they are configured properly. - -Add a flowset -------------- -Flowset pointers are store in the system in two list. The unlinked flowset list -contains all flowset that aren't linked with a scheduler, the flowset list -contains flowset linked to a scheduler, and so they have a type. -When adding a new flowset, first it is checked if the flowset exists (that is, -it is in the flowset list) and if it doesn't exists a new flowset is created -and added to unlinked flowset list if the scheduler which the flowset would be -linked doesn't exists, or added in the flowset list and configured properly if -the scheduler exists. If the flowset (before to be created) was in the -unlinked flowset list, it is removed and deleted, and then recreated. -If the flowset exists, to allow reconfiguration of this flowset, the -scheduler number and types must match with the one in memory. If this isn't -so, the flowset is deleted and a new one will be created. Really, the flowset -it isn't deleted now, but it is removed from flowset list and it will be -deleted later because there could be some queues that are using it. - -Listing of object -================= -The user can request a list of object present in dummynet through the command -"ipfw [-v] pipe|queue [x] list|show" -The kernel side of dummynet send a buffer to user side that contains all -pipe, all scheduler, all flowset, plus all scheduler instances and all queues. -The dummynet user land will format the output and show only the relevant -information. -The buffer sent start with all pipe from the system. The entire struct dn_link -is passed, except the delay_profile struct that is useless in user space. -After pipes, all flowset are wrote in the buffer. The struct contains -scheduler flowset specific data is linked with the flowset writing the -'obj' id of the extension into the 'alg_fs' pointer. -Then schedulers are wrote. If a scheduler has one or more scheduler instance, -these are linked to the parent scheduler writing the id of the parent in the -'ptr_sched' pointer. If a scheduler instance has queues, there are wrote in -the buffer and linked thorugh the 'obj' and 'sched_inst' pointer. -Finally, flowsets in the unlinked flowset list are write in the buffer, and -then a struct gen in saved in the buffer to mark the last struct in the buffer. - - -Delete of object -================ -An object is usually removed by user through a command like -"ipfw pipe|queue x delete". XXX sched? -ipfw pass to the kernel a struct gen that contains the type and the number -of the object to remove - -Delete of pipe x ----------------- -A pipe can be deleted by the user throught the command 'ipfw pipe x delete'. -To delete a pipe, the pipe is removed from the pipe list, and then deleted. -Also the scheduler associated with this pipe should be deleted. -For compatibility with old dummynet syntax, the associated FIFO scheduler and -FIFO flowset must be deleted. - -Delete of flowset x -------------------- -To remove a flowset, we must be sure that is no loger referenced by any object. -If the flowset to remove is in the unlinked flowset list, there is not any -issue, the flowset can be safely removed calling a free() (the flowset -extension is not yet created if the flowset is in this list). -If the flowset is in the flowset list, first we remove from it so new packet -are discarded when arrive. Next, the flowset is marked as delete. -Now we must check if some queue is using this flowset. -To do this, a counter (active_f) is provided. This counter indicate how many -queues exist using this flowset. -The active_f counter is automatically incremented when a queue is created -and decremented when a queue is deleted. -If the counter is 0, the flowset can be safely deleted, and the delete_alg_fs() -scheduler function is called before deallocate memory. -If the counter is not 0, the flowset remain in memory until the counter become -zero. When a queue is delete (by dn_delete_queue() function) it is checked if -the linked flowset is deleting and if so the counter is decrementing. If the -counter reaches 0, the flowset is deleted. -The deletion of a queue can be done only by the scheduler, or when the scheduler -is destroyed. - -Delete of scheduler x ---------------------- -To delete a scheduler we must be sure that any scheduler instance of this type -are in the system_heap. To do so, a counter (inst_counter) is provided. -This counter is managed by the system: it is incremented every time it is -inserted in the system_heap, and decremented every time it is extracted from it. -To delete the scheduler, first we remove it from the scheduler list, so new -packet are discarded when they arrive, and mark the scheduler as deleting. - -If the counter is 0, we can remove the scheduler safely calling the -really_deletescheduler() function. This function will scan all scheduler -instances and call the delete_scheduler_instance() function that will delete -the instance. When all instance are deleted, the scheduler template is -deleted calling the delete_scheduler_template(). If the delay line associate -with the scheduler is empty, it is deleted now, else it will be deleted when -it will became empy. -If the counter was not 0, we wait for it. Every time the dummynet_task() -function extract a scheduler from the system_heap, the counter is decremented. -If the scheduler has the delete flag enabled the dequeue() is not called and -delete_scheduler_instance() is called to delete the instance. -Obviously this scheduler instance is no loger inserted in the system_heap. -If the counter reaches 0, the delete_scheduler_template() function is called -all memory is released. -NOTE: Flowsets that belong to this scheduler are not deleted, so if a new - scheduler with the same number is inserted will use these flowsets. - To do so, the best approach would be insert these flowset in the - unlinked flowset list, but doing this now will be very expensive. - So flowsets will remain in memory and linked with a scheduler that no - longer exists until a packet belonging to this flowset arrives. When - this packet arrives, the reconfigure() function is called because the - generation number mismatch with one contains in the flowset and so - the flowset will be moved into the flowset unlinked list, or will be - linked with the new scheduler if a new one was created. - - -COMPATIBILITY WITH FREEBSD 7.2 AND FREEBSD 8 'IPFW' BINARY -========================================================== -Dummynet is not compatible with old ipfw binary because internal structs are -changed. Moreover, the old ipfw binary is not compatible with new kernels -because the struct that represents a firewall rule has changed. So, if a user -install a new kernel on a FreeBSD 7.2, the ipfw (and possibly many other -commands) will not work. -New dummynet uses a new socket option: IP_DUMMYNET3, used for both set and get. -The old option can be used to allow compatibility with the 'ipfw' binary of -older version (tested with 7.2 and 8.0) of FreeBSD. -Two file are provided for this purpose: -- ip_dummynet_glue.c translates old dummynet requests to the new ones, -- ip_fw_glue.c converts the rule format between 7.2 and 8 versions. -Let see in detail these two files. - -IP_DUMMYNET_GLUE.C ------------------- -The internal structs of new dummynet are very different from the original. -Because of there are some difference from between dummynet in FreeBSD 7.2 and -dummynet in FreeBSD 8 (the FreeBSD 8 version includes support to pipe delay -profile and burst option), I have to include both header files. I copied -the revision 191715 (for version 7.2) and the revision 196045 (for version 8) -and I appended a number to each struct to mark them. - -The main function of this file is ip_dummynet_compat() that is called by -ip_dn_ctl() when it receive a request of old socket option. - -A global variabile ('is7') store the version of 'ipfw' that FreeBSD is using. -This variable is set every time a request of configuration is done, because -with this request we receive a buffer of which size depending of ipfw version. -Because of in general the first action is a configuration, this variable is -usually set accordly. If the first action is a request of listing of pipes -or queues, the system cannot know the version of ipfw, and we suppose that -version 7.2 is used. If version is wrong, the output can be senseless, but -the application should not crash. - -There are four request for old dummynet: -- IP_DUMMYNET_FLUSH: the flush options have no parameter, so simply the - dummynet_flush() function is called; -- IP_DUMMYNET_DEL: the delete option need to be translate. - It is only necessary to extract the number and the type of the object - (pipe or queue) to delete from the buffer received and build a new struct - gen contains the right parameters, then call the delete_object() function; -- IP_DUMMYNET_CONFIGURE: the configure command receive a buffer depending of - the ipfw version. After the properly extraction of all data, that depends - by the ipfw version used, new structures are filled and then the dummynet - config_link() function is properly called. Note that the 7.2 version does - not support some parameter as burst or delay profile. -- IP_DUMMYNET_GET: The get command should send to the ipfw the correct buffer - depending of its version. There are two function that build the - corrected buffer, ip_dummynet_get7() and ip_dummynet_get8(). These - functions reproduce the buffer exactly as 'ipfw' expect. The only difference - is that the weight parameter for a queue is no loger sent by dummynet and so - it is set to 0. - Moreover, because of the internal structure has changed, the bucket size - of a queue could not be correct, because now all flowset share the hash - table. - If the version of ipfw is wrong, the output could be senseless or truncated, - but the application should not crash. - -IP_FW_GLUE.C ------------- -The ipfw binary also is used to add rules to FreeBSD firewall. Because of the -struct ip_fw is changed from FreeBsd 7.2 to FreeBSD 8, it is necessary -to write some glue code to allow use ipfw from FreeBSD 7.2 with the kernel -provided with FreeBSD 8. -This file contains two functions to convert a rule from FreeBSD 7.2 format to -FreeBSD 8 format, and viceversa. -The conversion should be done when a rule passes from userspace to kernel space -and viceversa. -I have to modify the ip_fw2.c file to manage these two case, and added a -variable (is7) to store the ipfw version used, using an approach like the -previous file: -- when a new rule is added (option IP_FW_ADD) the is7 variable is set if the - size of the rule received corrispond to FreeBSD 7.2 ipfw version. If so, the - rule is converted to version 8 calling the function convert_rule_to_8(). - Moreover, after the insertion of the rule, the rule is now reconverted to - version 7 because the ipfw binary will print it. -- when the user request a list of rules (option IP_FW_GET) the is7 variable - should be set correctly because we suppose that a configure command was done, - else we suppose that the FreeBSD version is 8. The function ipfw_getrules() - in ip_fw2.c file return all rules, eventually converted to version 7 (if - the is7 is set) to the ipfw binary. -The conversion of a rule is quite simple. The only difference between the -two structures (struct ip_fw) is that in the new there is a new field -(uint32_t id). So, I copy the entire rule in a buffer and the copy the rule in -the right position in the new (or old) struct. The size of commands are not -changed, and the copy is done into a cicle. - -How to configure dummynet -========================= -It is possible to configure dummynet through two main commands: -'ipfw pipe' and 'ipfw queue'. -To allow compatibility with old version, it is possible configure dummynet -using the old command syntax. Doing so, obviously, it is only possible to -configure a FIFO scheduler or a wf2q+ scheduler. -A new command, 'ipfw pipe x config sched ' is supported to add a new -scheduler to the system. - -- ipfw pipe x config ... - create a new pipe with the link parameters - create a new scheduler fifo (x + offset) - create a new flowset fifo (x + offset) - the mask is eventually stored in the FIFO scheduler - -- ipfw queue y config pipe x ... - create a new flowset y linked to sched x. - The type of flowset depends by the specified scheduler. - If the scheduler does not exist, this flowset is inserted in a special - list and will be not active. - If pipe x exists and sched does not exist, a new wf2q+ scheduler is - created and the flowset will be linked to this new scheduler (this is - done for compatibility with old syntax). - -- ipfw pipe x config sched ... - create a new scheduler x of type . - Search into the flowset unlinked list if there are some flowset that - should be linked with this new scheduler. - -- ipfw pipe x delete - delete the pipe x - delete the scheduler fifo (x + offset) - delete the scheduler x - delete the flowset fifo (x + offset) - -- ipfw queue x delete - delete the flowset x - -- ipfw sched x delete ///XXX - delete the scheduler x - -Follow now some examples to how configure dummynet: -- Ex1: - ipfw pipe 10 config bw 1M delay 15 // create a pipe with band and delay - A FIFO flowset and scheduler is - also created - ipfw queue 5 config pipe 10 weight 56 // create a flowset. This flowset - will be of wf2q+ because a pipe 10 - exists. Moreover, the wf2q+ - scheduler is created now. -- Ex2: - ipfw queue 5 config pipe 10 weight 56 // Create a flowset. Scheduler 10 - does not exist, so this flowset - is inserted in the unlinked - flowset list. - ipfw pipe 10 config bw... // Create a pipe, a FIFO flowset and scheduler. - Because of a flowset with 'pipe 10' exists, - a wf2q+ scheduler is created now and that - flowset is linked with this sceduler. - -- Ex3: - ipfw pipe 10 config bw... // Create a pipe, a FIFO flowset and scheduler. - ipfw pipe 10 config sched rr // Create a scheduler of type RR, linked to - pipe 10 - ipfw queue 5 config pipe 10 weight 56 // Create a flowset 5. This flowset - will belong to scheduler 10 and - it is of type RR - -- Ex4: - ipfw pipe 10 config sched rr // Create a scheduler of type RR, linked to - pipe 10 (not exist yet) - ipfw pipe 10 config bw... // Create a pipe, a FIFO flowset and scheduler. - ipfw queue 5 config pipe 10 weight 56 // Create a flowset 5.This flowset - will belong to scheduler 10 and - it is of type RR - ipfw pipe 10 config sched wf2q+ // Modify the type of scheduler 10. It - becomes a wf2q+ scheduler. - When a new packet of flowset 5 arrives, - the flowset 5 becomes to wf2q+ type. - -How to implement a new scheduler -================================ -In dummynet, a scheduler algorithm is represented by two main structs, some -functions and other minor structs. -- A struct dn_sch_xyz (where xyz is the 'type' of scheduler algorithm - implemented) contains data relative to scheduler, as global parameter that - are common to all instances of the scheduler -- A struct dn_sch_inst_xyz contains data relative to a single scheduler - instance, as local status variable depending for example by flows that - are linked with the scheduler, and so on. -To add a scheduler to dummynet, the user should type a command like: -'ipfw pipe x config sched [mask ... ...]' -This command creates a new struct dn_sch_xyz of type , and -store the optional parameter in that struct. - -The parameter mask determines how many scheduler instance of this -scheduler may exist. For example, it is possible to divide traffic -depending on the source port (or destination, or ip address...), -so that every scheduler instance act as an independent scheduler. -If the mask is not set, all traffic goes to the same instance. - -When a packet arrives to a scheduler, the system search the corrected -scheduler instance, and if it does not exist it is created now (the -struct dn_sch_inst_xyz is allocated by the system, and the scheduler -fills the field correctly). It is a task of the scheduler to create -the struct that contains all queues for a scheduler instance. -Dummynet provides some function to create an hash table to store -queues, but the schedule algorithm can choice the own struct. - -To link a flow to a scheduler, the user should type a command like: -'ipfw queue z config pipe x [mask... ...]' - -This command creates a new 'dn_fs' struct that will be inserted -in the system. If the scheduler x exists, this flowset will be -linked to that scheduler and the flowset type become the same as -the scheduler type. At this point, the function create_alg_fs_xyz() -is called to allow store eventually parameter for the flowset that -depend by scheduler (for example the 'weight' parameter for a wf2q+ -scheduler, or some priority...). A parameter mask can be used for -a flowset. If the mask parameter is set, the scheduler instance can -separate packet according to its flow id (src and dst ip, ports...) -and assign it to a separate queue. This is done by the scheduler, -so it can ignore the mask if it wants. - -See now the two main structs: -struct dn_sch_xyz { - struct gen g; /* important the name g */ - /* global params */ -}; -struct dn_sch_inst_xyz { - struct gen g; /* important the name g */ - /* params of the instance */ -}; -It is important to embed the struct gen as first parameter. The struct gen -contains some values that the scheduler instance must fill (the 'type' of -scheduler, the 'len' of the struct...) -The function create_scheduler_xyz() should be implemented to initialize global -parameters in the first struct, and if memory allocation is done it is -mandatory to implement the delete_scheduler_template() function to free that -memory. -The function create_scheduler_instance_xyz() must be implemented even if the -scheduler instance does not use extra parameters. In this function the struct -gen fields must be filled with corrected infos. The -delete_scheduler_instance_xyz() function must bu implemented if the instance -has allocated some memory in the previous function. - -To store data belonging to a flowset the follow struct is used: -struct alg_fs_xyz { - struct gen g; - /* fill correctly the gen struct - g.subtype = DN_XYZ; - g.len = sizeof(struct alg_fs_xyz) - ... - */ - /* params for the flow */ -}; -The create_alg_fs_xyz() function is mandatory, because it must fill the struct -gen, but the delete_alg_fs_xyz() is mandatory only if the previous function -has allocated some memory. - -A struct dn_queue contains packets belonging to a queue and some statistical -data. The scheduler could have to store data in this struct, so it must define -a dn_queue_xyz struct: -struct dn_queue_xyz { - struct dn_queue q; - /* parameter for a queue */ -} - -All structures are allocated by the system. To do so, the scheduler must -set the size of its structs in the scheduler descriptor: -scheduler_size: sizeof(dn_sch_xyz) -scheduler_i_size: sizeof(dn_sch_inst_xyz) -flowset_size: sizeof(alg_fs_xyz) -queue_size: sizeof(dn_queue_xyz); -The scheduler_size could be 0, but other struct must have at least a struct gen. - - -After the definition of structs, it is necessary to implement the -scheduler functions. - -- int (*config_scheduler)(char *command, void *sch, int reconfigure); - Configure a scheduler, or reconfigure if 'reconfigure' == 1. - This function performs additional allocation and initialization of global - parameter for this scheduler. - If memory is allocated here, the delete_scheduler_template() function - should be implemented to remove this memory. -- int (*delete_scheduler_template)(void* sch); - Delete a scheduler template. This function is mandatory if the scheduler - uses extra data respect the struct dn_sch. -- int (*create_scheduler_instance)(void *s); - Create a new scheduler instance. The system allocate the necessary memory - and the schedulet can access it using the 's' pointer. - The scheduler instance stores all queues, and to do this can use the - hash table provided by the system. -- int (*delete_scheduler_instance)(void *s); - Delete a scheduler instance. It is important to free memory allocated - by create_scheduler_instance() function. The memory allocated by system - is freed by the system itself. The struct contains all queue also has - to be deleted. -- int (*enqueue)(void *s, struct gen *f, struct mbuf *m, - struct ipfw_flow_id *id); - Called when a packet arrives. The packet 'm' belongs to the scheduler - instance 's', has a flowset 'f' and the flowid 'id' has already been - masked. The enqueue() must call dn_queue_packet(q, m) function to really - enqueue packet in the queue q. The queue 'q' is chosen by the scheduler - and if it does not exist should be created calling the dn_create_queue() - function. If the schedule want to drop the packet, it must call the - dn_drop_packet() function and then return 1. -- struct mbuf * (*dequeue)(void *s); - Called when the timer expires (or when a packet arrives and the scheduler - instance is idle). - This function is called when at least a packet can be send out. The - scheduler choices the packet and returns it; if no packet are in the - schedulerinstance, the function must return NULL. - Before return a packet, it is important to call the function - dn_return_packet() to update some statistic of the queue and update the - queue counters. -- int (*drain_queue)(void *s, int flag); - The system request to scheduler to delete all queues that is not using - to free memory. The flag parameter indicate if a queue must be deleted - even if it is active. - -- int (*create_alg_fs)(char *command, struct gen *g, int reconfigure); - It is called when a flowset is linked with a scheduler. This is done - when the scheduler is defined, so we can know the type of flowset. - The function initialize the flowset paramenter parsing the command - line. The parameter will be stored in the g struct that have the right - size allocated by the system. If the reconfigure flag is set, it means - that the flowset is reconfiguring -- int (*delete_alg_fs)(struct gen *f); - It is called when a flowset is deleting. Must remove the memory allocate - by the create_alg_fs() function. - -- int (*create_queue_alg)(struct dn_queue *q, struct gen *f); - Called when a queue is created. The function should link the queue - to the struct used by the scheduler instance to store all queues. -- int (*delete_queue_alg)(struct dn_queue *q); - Called when a queue is deleting. The function should remove extra data - and update the struct contains all queues in the scheduler instance. - -The struct scheduler represent the scheduler descriptor that is passed to -dummynet when a scheduler module is loaded. -This struct contains the type of scheduler, the length of all structs and -all function pointers. -If a function is not implemented should be initialize to NULL. Some functions -are mandatory, other are mandatory if some memory should be freed. -Mandatory functions: -- create_scheduler_instance() -- enqueue() -- dequeue() -- create_alg_fs() -- drain_queue() -Optional functions: -- config_scheduler() -- create_queue_alg() -Mandatory functions if the corresponding create...() has allocated memory: -- delete_scheduler_template() -- delete_scheduler_instance() -- delete_alg_fs() -- delete_queue_alg() - diff --git a/sys/netinet/ipfw/ip_dn_glue.c b/sys/netinet/ipfw/ip_dn_glue.c deleted file mode 100644 index 9fc6b23f662e5..0000000000000 --- a/sys/netinet/ipfw/ip_dn_glue.c +++ /dev/null @@ -1,845 +0,0 @@ -/*- - * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa - * 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. - */ - -/* - * $FreeBSD$ - * - * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8 - */ - -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ -#include -#include /* ip_output(), IP_FORWARDING */ -#include -#include -#include -#include -#include -#include - -/* FREEBSD7.2 ip_dummynet.h r191715*/ - -struct dn_heap_entry7 { - int64_t key; /* sorting key. Topmost element is smallest one */ - void *object; /* object pointer */ -}; - -struct dn_heap7 { - int size; - int elements; - int offset; /* XXX if > 0 this is the offset of direct ptr to obj */ - struct dn_heap_entry7 *p; /* really an array of "size" entries */ -}; - -/* Common to 7.2 and 8 */ -struct dn_flow_set { - SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ - - u_short fs_nr ; /* flow_set number */ - u_short flags_fs; -#define DNOLD_HAVE_FLOW_MASK 0x0001 -#define DNOLD_IS_RED 0x0002 -#define DNOLD_IS_GENTLE_RED 0x0004 -#define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ -#define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ -#define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ -#define DNOLD_IS_PIPE 0x4000 -#define DNOLD_IS_QUEUE 0x8000 - - struct dn_pipe7 *pipe ; /* pointer to parent pipe */ - u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ - - int weight ; /* WFQ queue weight */ - int qsize ; /* queue size in slots or bytes */ - int plr ; /* pkt loss rate (2^31-1 means 100%) */ - - struct ipfw_flow_id flow_mask ; - - /* hash table of queues onto this flow_set */ - int rq_size ; /* number of slots */ - int rq_elements ; /* active elements */ - struct dn_flow_queue7 **rq; /* array of rq_size entries */ - - u_int32_t last_expired ; /* do not expire too frequently */ - int backlogged ; /* #active queues for this flowset */ - - /* RED parameters */ -#define SCALE_RED 16 -#define SCALE(x) ( (x) << SCALE_RED ) -#define SCALE_VAL(x) ( (x) >> SCALE_RED ) -#define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED ) - int w_q ; /* queue weight (scaled) */ - int max_th ; /* maximum threshold for queue (scaled) */ - int min_th ; /* minimum threshold for queue (scaled) */ - int max_p ; /* maximum value for p_b (scaled) */ - u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ - u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ - u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ - u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ - u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ - u_int lookup_depth ; /* depth of lookup table */ - int lookup_step ; /* granularity inside the lookup table */ - int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ - int avg_pkt_size ; /* medium packet size */ - int max_pkt_size ; /* max packet size */ -}; -SLIST_HEAD(dn_flow_set_head, dn_flow_set); - -#define DN_IS_PIPE 0x4000 -#define DN_IS_QUEUE 0x8000 -struct dn_flow_queue7 { - struct dn_flow_queue7 *next ; - struct ipfw_flow_id id ; - - struct mbuf *head, *tail ; /* queue of packets */ - u_int len ; - u_int len_bytes ; - - u_long numbytes; - - u_int64_t tot_pkts ; /* statistics counters */ - u_int64_t tot_bytes ; - u_int32_t drops ; - - int hash_slot ; /* debugging/diagnostic */ - - /* RED parameters */ - int avg ; /* average queue length est. (scaled) */ - int count ; /* arrivals since last RED drop */ - int random ; /* random value (scaled) */ - u_int32_t q_time; /* start of queue idle time */ - - /* WF2Q+ support */ - struct dn_flow_set *fs ; /* parent flow set */ - int heap_pos ; /* position (index) of struct in heap */ - int64_t sched_time ; /* current time when queue enters ready_heap */ - - int64_t S,F ; /* start time, finish time */ -}; - -struct dn_pipe7 { /* a pipe */ - SLIST_ENTRY(dn_pipe7) next; /* linked list in a hash slot */ - - int pipe_nr ; /* number */ - int bandwidth; /* really, bytes/tick. */ - int delay ; /* really, ticks */ - - struct mbuf *head, *tail ; /* packets in delay line */ - - /* WF2Q+ */ - struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ - struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ - struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ - - int64_t V ; /* virtual time */ - int sum; /* sum of weights of all active sessions */ - - int numbytes; - - int64_t sched_time ; /* time pipe was scheduled in ready_heap */ - - /* - * When the tx clock come from an interface (if_name[0] != '\0'), its name - * is stored below, whereas the ifp is filled when the rule is configured. - */ - char if_name[IFNAMSIZ]; - struct ifnet *ifp ; - int ready ; /* set if ifp != NULL and we got a signal from it */ - - struct dn_flow_set fs ; /* used with fixed-rate flows */ -}; -SLIST_HEAD(dn_pipe_head7, dn_pipe7); - - -/* FREEBSD8 ip_dummynet.h r196045 */ -struct dn_flow_queue8 { - struct dn_flow_queue8 *next ; - struct ipfw_flow_id id ; - - struct mbuf *head, *tail ; /* queue of packets */ - u_int len ; - u_int len_bytes ; - - uint64_t numbytes ; /* credit for transmission (dynamic queues) */ - int64_t extra_bits; /* extra bits simulating unavailable channel */ - - u_int64_t tot_pkts ; /* statistics counters */ - u_int64_t tot_bytes ; - u_int32_t drops ; - - int hash_slot ; /* debugging/diagnostic */ - - /* RED parameters */ - int avg ; /* average queue length est. (scaled) */ - int count ; /* arrivals since last RED drop */ - int random ; /* random value (scaled) */ - int64_t idle_time; /* start of queue idle time */ - - /* WF2Q+ support */ - struct dn_flow_set *fs ; /* parent flow set */ - int heap_pos ; /* position (index) of struct in heap */ - int64_t sched_time ; /* current time when queue enters ready_heap */ - - int64_t S,F ; /* start time, finish time */ -}; - -struct dn_pipe8 { /* a pipe */ - SLIST_ENTRY(dn_pipe8) next; /* linked list in a hash slot */ - - int pipe_nr ; /* number */ - int bandwidth; /* really, bytes/tick. */ - int delay ; /* really, ticks */ - - struct mbuf *head, *tail ; /* packets in delay line */ - - /* WF2Q+ */ - struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ - struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ - struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ - - int64_t V ; /* virtual time */ - int sum; /* sum of weights of all active sessions */ - - /* Same as in dn_flow_queue, numbytes can become large */ - int64_t numbytes; /* bits I can transmit (more or less). */ - uint64_t burst; /* burst size, scaled: bits * hz */ - - int64_t sched_time ; /* time pipe was scheduled in ready_heap */ - int64_t idle_time; /* start of pipe idle time */ - - char if_name[IFNAMSIZ]; - struct ifnet *ifp ; - int ready ; /* set if ifp != NULL and we got a signal from it */ - - struct dn_flow_set fs ; /* used with fixed-rate flows */ - - /* fields to simulate a delay profile */ -#define ED_MAX_NAME_LEN 32 - char name[ED_MAX_NAME_LEN]; - int loss_level; - int samples_no; - int *samples; -}; - -#define ED_MAX_SAMPLES_NO 1024 -struct dn_pipe_max8 { - struct dn_pipe8 pipe; - int samples[ED_MAX_SAMPLES_NO]; -}; -SLIST_HEAD(dn_pipe_head8, dn_pipe8); - -/* - * Changes from 7.2 to 8: - * dn_pipe: - * numbytes from int to int64_t - * add burst (int64_t) - * add idle_time (int64_t) - * add profile - * add struct dn_pipe_max - * add flag DN_HAS_PROFILE - * - * dn_flow_queue - * numbytes from u_long to int64_t - * add extra_bits (int64_t) - * q_time from u_int32_t to int64_t and name idle_time - * - * dn_flow_set unchanged - * - */ - -/* NOTE:XXX copied from dummynet.c */ -#define O_NEXT(p, len) ((void *)((char *)p + len)) -static void -oid_fill(struct dn_id *oid, int len, int type, uintptr_t id) -{ - oid->len = len; - oid->type = type; - oid->subtype = 0; - oid->id = id; -} -/* make room in the buffer and move the pointer forward */ -static void * -o_next(struct dn_id **o, int len, int type) -{ - struct dn_id *ret = *o; - oid_fill(ret, len, type, 0); - *o = O_NEXT(*o, len); - return ret; -} - - -static size_t pipesize7 = sizeof(struct dn_pipe7); -static size_t pipesize8 = sizeof(struct dn_pipe8); -static size_t pipesizemax8 = sizeof(struct dn_pipe_max8); - -/* Indicate 'ipfw' version - * 1: from FreeBSD 7.2 - * 0: from FreeBSD 8 - * -1: unknow (for now is unused) - * - * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives - * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknow, - * it is suppose to be the FreeBSD 8 version. - */ -static int is7 = 0; - -static int -convertflags2new(int src) -{ - int dst = 0; - - if (src & DNOLD_HAVE_FLOW_MASK) - dst |= DN_HAVE_MASK; - if (src & DNOLD_QSIZE_IS_BYTES) - dst |= DN_QSIZE_BYTES; - if (src & DNOLD_NOERROR) - dst |= DN_NOERROR; - if (src & DNOLD_IS_RED) - dst |= DN_IS_RED; - if (src & DNOLD_IS_GENTLE_RED) - dst |= DN_IS_GENTLE_RED; - if (src & DNOLD_HAS_PROFILE) - dst |= DN_HAS_PROFILE; - - return dst; -} - -static int -convertflags2old(int src) -{ - int dst = 0; - - if (src & DN_HAVE_MASK) - dst |= DNOLD_HAVE_FLOW_MASK; - if (src & DN_IS_RED) - dst |= DNOLD_IS_RED; - if (src & DN_IS_GENTLE_RED) - dst |= DNOLD_IS_GENTLE_RED; - if (src & DN_NOERROR) - dst |= DNOLD_NOERROR; - if (src & DN_HAS_PROFILE) - dst |= DNOLD_HAS_PROFILE; - if (src & DN_QSIZE_BYTES) - dst |= DNOLD_QSIZE_IS_BYTES; - - return dst; -} - -static int -dn_compat_del(void *v) -{ - struct dn_pipe7 *p = (struct dn_pipe7 *) v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *) v; - struct { - struct dn_id oid; - uintptr_t a[1]; /* add more if we want a list */ - } cmd; - - /* XXX DN_API_VERSION ??? */ - oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); - - if (is7) { - if (p->pipe_nr == 0 && p->fs.fs_nr == 0) - return EINVAL; - if (p->pipe_nr != 0 && p->fs.fs_nr != 0) - return EINVAL; - } else { - if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0) - return EINVAL; - if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0) - return EINVAL; - } - - if (p->pipe_nr != 0) { /* pipe x delete */ - cmd.a[0] = p->pipe_nr; - cmd.oid.subtype = DN_LINK; - } else { /* queue x delete */ - cmd.oid.subtype = DN_FS; - cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr; - } - - return do_config(&cmd, cmd.oid.len); -} - -static int -dn_compat_config_queue(struct dn_fs *fs, void* v) -{ - struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - struct dn_flow_set *f; - - if (is7) - f = &p7->fs; - else - f = &p8->fs; - - fs->fs_nr = f->fs_nr; - fs->sched_nr = f->parent_nr; - fs->flow_mask = f->flow_mask; - fs->buckets = f->rq_size; - fs->qsize = f->qsize; - fs->plr = f->plr; - fs->par[0] = f->weight; - fs->flags = convertflags2new(f->flags_fs); - if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { - fs->w_q = f->w_q; - fs->max_th = f->max_th; - fs->min_th = f->min_th; - fs->max_p = f->max_p; - } - - return 0; -} - -static int -dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p, - struct dn_fs *fs, void* v) -{ - struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - int i = p7->pipe_nr; - - sch->sched_nr = i; - sch->oid.subtype = 0; - p->link_nr = i; - fs->fs_nr = i + 2*DN_MAX_ID; - fs->sched_nr = i + DN_MAX_ID; - - /* Common to 7 and 8 */ - p->bandwidth = p7->bandwidth; - p->delay = p7->delay; - if (!is7) { - /* FreeBSD 8 has burst */ - p->burst = p8->burst; - } - - /* fill the fifo flowset */ - dn_compat_config_queue(fs, v); - fs->fs_nr = i + 2*DN_MAX_ID; - fs->sched_nr = i + DN_MAX_ID; - - /* Move scheduler related parameter from fs to sch */ - sch->buckets = fs->buckets; /*XXX*/ - fs->buckets = 0; - if (fs->flags & DN_HAVE_MASK) { - sch->flags |= DN_HAVE_MASK; - fs->flags &= ~DN_HAVE_MASK; - sch->sched_mask = fs->flow_mask; - bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id)); - } - - return 0; -} - -static int -dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p, - void *v) -{ - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - - p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]); - - pf->link_nr = p->link_nr; - pf->loss_level = p8->loss_level; -// pf->bandwidth = p->bandwidth; //XXX bandwidth redundant? - pf->samples_no = p8->samples_no; - strncpy(pf->name, p8->name,sizeof(pf->name)); - bcopy(p8->samples, pf->samples, sizeof(pf->samples)); - - return 0; -} - -/* - * If p->pipe_nr != 0 the command is 'pipe x config', so need to create - * the three main struct, else only a flowset is created - */ -static int -dn_compat_configure(void *v) -{ - struct dn_id *buf = NULL, *base; - struct dn_sch *sch = NULL; - struct dn_link *p = NULL; - struct dn_fs *fs = NULL; - struct dn_profile *pf = NULL; - int lmax; - int error; - - struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - - int i; /* number of object to configure */ - - lmax = sizeof(struct dn_id); /* command header */ - lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + - sizeof(struct dn_fs) + sizeof(struct dn_profile); - - base = buf = malloc(lmax, M_DUMMYNET, M_WAIT|M_ZERO); - o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); - base->id = DN_API_VERSION; - - /* pipe_nr is the same in p7 and p8 */ - i = p7->pipe_nr; - if (i != 0) { /* pipe config */ - sch = o_next(&buf, sizeof(*sch), DN_SCH); - p = o_next(&buf, sizeof(*p), DN_LINK); - fs = o_next(&buf, sizeof(*fs), DN_FS); - - error = dn_compat_config_pipe(sch, p, fs, v); - if (error) { - free(buf, M_DUMMYNET); - return error; - } - if (!is7 && p8->samples_no > 0) { - /* Add profiles*/ - pf = o_next(&buf, sizeof(*pf), DN_PROFILE); - error = dn_compat_config_profile(pf, p, v); - if (error) { - free(buf, M_DUMMYNET); - return error; - } - } - } else { /* queue config */ - fs = o_next(&buf, sizeof(*fs), DN_FS); - error = dn_compat_config_queue(fs, v); - if (error) { - free(buf, M_DUMMYNET); - return error; - } - } - error = do_config(base, (char *)buf - (char *)base); - - if (buf) - free(buf, M_DUMMYNET); - return error; -} - -int -dn_compat_calc_size(void) -{ - int need = 0; - /* XXX use FreeBSD 8 struct size */ - /* NOTE: - * - half scheduler: schk_count/2 - * - all flowset: fsk_count - * - all flowset queues: queue_count - * - all pipe queue: si_count - */ - need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2; - need += dn_cfg.fsk_count * sizeof(struct dn_flow_set); - need += dn_cfg.si_count * sizeof(struct dn_flow_queue8); - need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8); - - return need; -} - -int -dn_c_copy_q (void *_ni, void *arg) -{ - struct copy_args *a = arg; - struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start; - struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start; - struct dn_flow *ni = (struct dn_flow *)_ni; - int size = 0; - - /* XXX hash slot not set */ - /* No difference between 7.2/8 */ - fq7->len = ni->length; - fq7->len_bytes = ni->len_bytes; - fq7->id = ni->fid; - - if (is7) { - size = sizeof(struct dn_flow_queue7); - fq7->tot_pkts = ni->tot_pkts; - fq7->tot_bytes = ni->tot_bytes; - fq7->drops = ni->drops; - } else { - size = sizeof(struct dn_flow_queue8); - fq8->tot_pkts = ni->tot_pkts; - fq8->tot_bytes = ni->tot_bytes; - fq8->drops = ni->drops; - } - - *a->start += size; - return 0; -} - -int -dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq) -{ - struct dn_link *l = &s->link; - struct dn_fsk *f = s->fs; - - struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start; - struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start; - struct dn_flow_set *fs; - int size = 0; - - if (is7) { - fs = &pipe7->fs; - size = sizeof(struct dn_pipe7); - } else { - fs = &pipe8->fs; - size = sizeof(struct dn_pipe8); - } - - /* These 4 field are the same in pipe7 and pipe8 */ - pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE; - pipe7->bandwidth = l->bandwidth; - pipe7->delay = l->delay * 1000 / hz; - pipe7->pipe_nr = l->link_nr - DN_MAX_ID; - - if (!is7) { - if (s->profile) { - struct dn_profile *pf = s->profile; - strncpy(pipe8->name, pf->name, sizeof(pf->name)); - pipe8->loss_level = pf->loss_level; - pipe8->samples_no = pf->samples_no; - } - pipe8->burst = div64(l->burst , 8 * hz); - } - - fs->flow_mask = s->sch.sched_mask; - fs->rq_size = s->sch.buckets ? s->sch.buckets : 1; - - fs->parent_nr = l->link_nr - DN_MAX_ID; - fs->qsize = f->fs.qsize; - fs->plr = f->fs.plr; - fs->w_q = f->fs.w_q; - fs->max_th = f->max_th; - fs->min_th = f->min_th; - fs->max_p = f->fs.max_p; - fs->rq_elements = nq; - - fs->flags_fs = convertflags2old(f->fs.flags); - - *a->start += size; - return 0; -} - - -int -dn_compat_copy_pipe(struct copy_args *a, void *_o) -{ - int have = a->end - *a->start; - int need = 0; - int pipe_size = sizeof(struct dn_pipe8); - int queue_size = sizeof(struct dn_flow_queue8); - int n_queue = 0; /* number of queues */ - - struct dn_schk *s = (struct dn_schk *)_o; - /* calculate needed space: - * - struct dn_pipe - * - if there are instances, dn_queue * n_instances - */ - n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) : - (s->siht ? 1 : 0)); - need = pipe_size + queue_size * n_queue; - if (have < need) { - D("have %d < need %d", have, need); - return 1; - } - /* copy pipe */ - dn_c_copy_pipe(s, a, n_queue); - - /* copy queues */ - if (s->sch.flags & DN_HAVE_MASK) - dn_ht_scan(s->siht, dn_c_copy_q, a); - else if (s->siht) - dn_c_copy_q(s->siht, a); - return 0; -} - -int -dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) -{ - struct dn_flow_set *fs = (struct dn_flow_set *)*a->start; - - fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; - fs->fs_nr = f->fs.fs_nr; - fs->qsize = f->fs.qsize; - fs->plr = f->fs.plr; - fs->w_q = f->fs.w_q; - fs->max_th = f->max_th; - fs->min_th = f->min_th; - fs->max_p = f->fs.max_p; - fs->flow_mask = f->fs.flow_mask; - fs->rq_elements = nq; - fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1); - fs->parent_nr = f->fs.sched_nr; - fs->weight = f->fs.par[0]; - - fs->flags_fs = convertflags2old(f->fs.flags); - *a->start += sizeof(struct dn_flow_set); - return 0; -} - -int -dn_compat_copy_queue(struct copy_args *a, void *_o) -{ - int have = a->end - *a->start; - int need = 0; - int fs_size = sizeof(struct dn_flow_set); - int queue_size = sizeof(struct dn_flow_queue8); - - struct dn_fsk *fs = (struct dn_fsk *)_o; - int n_queue = 0; /* number of queues */ - - n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) : - (fs->qht ? 1 : 0)); - - need = fs_size + queue_size * n_queue; - if (have < need) { - D("have < need"); - return 1; - } - - /* copy flowset */ - dn_c_copy_fs(fs, a, n_queue); - - /* copy queues */ - if (fs->fs.flags & DN_HAVE_MASK) - dn_ht_scan(fs->qht, dn_c_copy_q, a); - else if (fs->qht) - dn_c_copy_q(fs->qht, a); - - return 0; -} - -int -copy_data_helper_compat(void *_o, void *_arg) -{ - struct copy_args *a = _arg; - - if (a->type == DN_COMPAT_PIPE) { - struct dn_schk *s = _o; - if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) { - return 0; /* not old type */ - } - /* copy pipe parameters, and if instance exists, copy - * other parameters and eventually queues. - */ - if(dn_compat_copy_pipe(a, _o)) - return DNHT_SCAN_END; - } else if (a->type == DN_COMPAT_QUEUE) { - struct dn_fsk *fs = _o; - if (fs->fs.fs_nr >= DN_MAX_ID) - return 0; - if (dn_compat_copy_queue(a, _o)) - return DNHT_SCAN_END; - } - return 0; -} - -/* Main function to manage old requests */ -int -ip_dummynet_compat(struct sockopt *sopt) -{ - int error=0; - void *v = NULL; - struct dn_id oid; - - /* Lenght of data, used to found ipfw version... */ - int len = sopt->sopt_valsize; - - /* len can be 0 if command was dummynet_flush */ - if (len == pipesize7) { - D("setting compatibility with FreeBSD 7.2"); - is7 = 1; - } - else if (len == pipesize8 || len == pipesizemax8) { - D("setting compatibility with FreeBSD 8"); - is7 = 0; - } - - switch (sopt->sopt_name) { - default: - printf("dummynet: -- unknown option %d", sopt->sopt_name); - error = EINVAL; - break; - - case IP_DUMMYNET_FLUSH: - oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); - do_config(&oid, oid.len); - break; - - case IP_DUMMYNET_DEL: - v = malloc(len, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, v, len, len); - if (error) - break; - error = dn_compat_del(v); - free(v, M_TEMP); - break; - - case IP_DUMMYNET_CONFIGURE: - v = malloc(len, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, v, len, len); - if (error) - break; - error = dn_compat_configure(v); - free(v, M_TEMP); - break; - - case IP_DUMMYNET_GET: { - void *buf; - int ret; - int original_size = sopt->sopt_valsize; - int size; - - ret = dummynet_get(sopt, &buf); - if (ret) - return 0;//XXX ? - size = sopt->sopt_valsize; - sopt->sopt_valsize = original_size; - D("size=%d, buf=%p", size, buf); - ret = sooptcopyout(sopt, buf, size); - if (ret) - printf(" %s ERROR sooptcopyout\n", __FUNCTION__); - if (buf) - free(buf, M_DUMMYNET); - } - } - - return error; -} - - diff --git a/sys/netinet/ipfw/ip_dn_io.c b/sys/netinet/ipfw/ip_dn_io.c deleted file mode 100644 index becd85e4636ce..0000000000000 --- a/sys/netinet/ipfw/ip_dn_io.c +++ /dev/null @@ -1,858 +0,0 @@ -/*- - * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa - * 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. - */ - -/* - * Dummynet portions related to packet handling. - */ -#include -__FBSDID("$FreeBSD$"); - -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ -#include -#include - -#include -#include /* ip_len, ip_off */ -#include /* ip_output(), IP_FORWARDING */ -#include -#include -#include -#include -#include -#include - -#include /* various ether_* routines */ - -#include /* for ip6_input, ip6_output prototypes */ -#include - -/* - * We keep a private variable for the simulation time, but we could - * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) - * instead of dn_cfg.curr_time - */ - -struct dn_parms dn_cfg; -//VNET_DEFINE(struct dn_parms, _base_dn_cfg); - -static long tick_last; /* Last tick duration (usec). */ -static long tick_delta; /* Last vs standard tick diff (usec). */ -static long tick_delta_sum; /* Accumulated tick difference (usec).*/ -static long tick_adjustment; /* Tick adjustments done. */ -static long tick_lost; /* Lost(coalesced) ticks number. */ -/* Adjusted vs non-adjusted curr_time difference (ticks). */ -static long tick_diff; - -static unsigned long io_pkt; -static unsigned long io_pkt_fast; -static unsigned long io_pkt_drop; - -/* - * We use a heap to store entities for which we have pending timer events. - * The heap is checked at every tick and all entities with expired events - * are extracted. - */ - -MALLOC_DEFINE(M_DUMMYNET, "dummynet", "dummynet heap"); - -extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); - -#ifdef SYSCTL_NODE - -/* - * Because of the way the SYSBEGIN/SYSEND macros work on other - * platforms, there should not be functions between them. - * So keep the handlers outside the block. - */ -static int -sysctl_hash_size(SYSCTL_HANDLER_ARGS) -{ - int error, value; - - value = dn_cfg.hash_size; - error = sysctl_handle_int(oidp, &value, 0, req); - if (error != 0 || req->newptr == NULL) - return (error); - if (value < 16 || value > 65536) - return (EINVAL); - dn_cfg.hash_size = value; - return (0); -} - -static int -sysctl_limits(SYSCTL_HANDLER_ARGS) -{ - int error; - long value; - - if (arg2 != 0) - value = dn_cfg.slot_limit; - else - value = dn_cfg.byte_limit; - error = sysctl_handle_long(oidp, &value, 0, req); - - if (error != 0 || req->newptr == NULL) - return (error); - if (arg2 != 0) { - if (value < 1) - return (EINVAL); - dn_cfg.slot_limit = value; - } else { - if (value < 1500) - return (EINVAL); - dn_cfg.byte_limit = value; - } - return (0); -} - -SYSBEGIN(f4) - -SYSCTL_DECL(_net_inet); -SYSCTL_DECL(_net_inet_ip); -static SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet"); - -/* wrapper to pass dn_cfg fields to SYSCTL_* */ -//#define DC(x) (&(VNET_NAME(_base_dn_cfg).x)) -#define DC(x) (&(dn_cfg.x)) -/* parameters */ - - -SYSCTL_PROC(_net_inet_ip_dummynet, OID_AUTO, hash_size, - CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_hash_size, - "I", "Default hash table size"); - - -SYSCTL_PROC(_net_inet_ip_dummynet, OID_AUTO, pipe_slot_limit, - CTLTYPE_LONG | CTLFLAG_RW, 0, 1, sysctl_limits, - "L", "Upper limit in slots for pipe queue."); -SYSCTL_PROC(_net_inet_ip_dummynet, OID_AUTO, pipe_byte_limit, - CTLTYPE_LONG | CTLFLAG_RW, 0, 0, sysctl_limits, - "L", "Upper limit in bytes for pipe queue."); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, io_fast, - CTLFLAG_RW, DC(io_fast), 0, "Enable fast dummynet io."); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug, - CTLFLAG_RW, DC(debug), 0, "Dummynet debug level"); - -/* RED parameters */ -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_lookup_depth, - CTLFLAG_RD, DC(red_lookup_depth), 0, "Depth of RED lookup table"); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_avg_pkt_size, - CTLFLAG_RD, DC(red_avg_pkt_size), 0, "RED Medium packet size"); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_max_pkt_size, - CTLFLAG_RD, DC(red_max_pkt_size), 0, "RED Max packet size"); - -/* time adjustment */ -SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta, - CTLFLAG_RD, &tick_delta, 0, "Last vs standard tick difference (usec)."); -SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta_sum, - CTLFLAG_RD, &tick_delta_sum, 0, "Accumulated tick difference (usec)."); -SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_adjustment, - CTLFLAG_RD, &tick_adjustment, 0, "Tick adjustments done."); -SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_diff, - CTLFLAG_RD, &tick_diff, 0, - "Adjusted vs non-adjusted curr_time difference (ticks)."); -SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_lost, - CTLFLAG_RD, &tick_lost, 0, - "Number of ticks coalesced by dummynet taskqueue."); - -/* Drain parameters */ -SYSCTL_UINT(_net_inet_ip_dummynet, OID_AUTO, expire, - CTLFLAG_RW, DC(expire), 0, "Expire empty queues/pipes"); -SYSCTL_UINT(_net_inet_ip_dummynet, OID_AUTO, expire_cycle, - CTLFLAG_RD, DC(expire_cycle), 0, "Expire cycle for queues/pipes"); - -/* statistics */ -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, schk_count, - CTLFLAG_RD, DC(schk_count), 0, "Number of schedulers"); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, si_count, - CTLFLAG_RD, DC(si_count), 0, "Number of scheduler instances"); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, fsk_count, - CTLFLAG_RD, DC(fsk_count), 0, "Number of flowsets"); -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, queue_count, - CTLFLAG_RD, DC(queue_count), 0, "Number of queues"); -SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt, - CTLFLAG_RD, &io_pkt, 0, - "Number of packets passed to dummynet."); -SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_fast, - CTLFLAG_RD, &io_pkt_fast, 0, - "Number of packets bypassed dummynet scheduler."); -SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_drop, - CTLFLAG_RD, &io_pkt_drop, 0, - "Number of packets dropped by dummynet."); -#undef DC -SYSEND - -#endif - -static void dummynet_send(struct mbuf *); - -/* - * Packets processed by dummynet have an mbuf tag associated with - * them that carries their dummynet state. - * Outside dummynet, only the 'rule' field is relevant, and it must - * be at the beginning of the structure. - */ -struct dn_pkt_tag { - struct ipfw_rule_ref rule; /* matching rule */ - - /* second part, dummynet specific */ - int dn_dir; /* action when packet comes out.*/ - /* see ip_fw_private.h */ - uint64_t output_time; /* when the pkt is due for delivery*/ - struct ifnet *ifp; /* interface, for ip_output */ - struct _ip6dn_args ip6opt; /* XXX ipv6 options */ -}; - -/* - * Return the mbuf tag holding the dummynet state (it should - * be the first one on the list). - */ -static struct dn_pkt_tag * -dn_tag_get(struct mbuf *m) -{ - struct m_tag *mtag = m_tag_first(m); - KASSERT(mtag != NULL && - mtag->m_tag_cookie == MTAG_ABI_COMPAT && - mtag->m_tag_id == PACKET_TAG_DUMMYNET, - ("packet on dummynet queue w/o dummynet tag!")); - return (struct dn_pkt_tag *)(mtag+1); -} - -static inline void -mq_append(struct mq *q, struct mbuf *m) -{ - if (q->head == NULL) - q->head = m; - else - q->tail->m_nextpkt = m; - q->tail = m; - m->m_nextpkt = NULL; -} - -/* - * Dispose a list of packet. Use a functions so if we need to do - * more work, this is a central point to do it. - */ -void dn_free_pkts(struct mbuf *mnext) -{ - struct mbuf *m; - - while ((m = mnext) != NULL) { - mnext = m->m_nextpkt; - FREE_PKT(m); - } -} - -static int -red_drops (struct dn_queue *q, int len) -{ - /* - * RED algorithm - * - * RED calculates the average queue size (avg) using a low-pass filter - * with an exponential weighted (w_q) moving average: - * avg <- (1-w_q) * avg + w_q * q_size - * where q_size is the queue length (measured in bytes or * packets). - * - * If q_size == 0, we compute the idle time for the link, and set - * avg = (1 - w_q)^(idle/s) - * where s is the time needed for transmitting a medium-sized packet. - * - * Now, if avg < min_th the packet is enqueued. - * If avg > max_th the packet is dropped. Otherwise, the packet is - * dropped with probability P function of avg. - */ - - struct dn_fsk *fs = q->fs; - int64_t p_b = 0; - - /* Queue in bytes or packets? */ - uint32_t q_size = (fs->fs.flags & DN_QSIZE_BYTES) ? - q->ni.len_bytes : q->ni.length; - - /* Average queue size estimation. */ - if (q_size != 0) { - /* Queue is not empty, avg <- avg + (q_size - avg) * w_q */ - int diff = SCALE(q_size) - q->avg; - int64_t v = SCALE_MUL((int64_t)diff, (int64_t)fs->w_q); - - q->avg += (int)v; - } else { - /* - * Queue is empty, find for how long the queue has been - * empty and use a lookup table for computing - * (1 - * w_q)^(idle_time/s) where s is the time to send a - * (small) packet. - * XXX check wraps... - */ - if (q->avg) { - u_int t = div64((dn_cfg.curr_time - q->q_time), fs->lookup_step); - - q->avg = (t < fs->lookup_depth) ? - SCALE_MUL(q->avg, fs->w_q_lookup[t]) : 0; - } - } - - /* Should i drop? */ - if (q->avg < fs->min_th) { - q->count = -1; - return (0); /* accept packet */ - } - if (q->avg >= fs->max_th) { /* average queue >= max threshold */ - if (fs->fs.flags & DN_IS_GENTLE_RED) { - /* - * According to Gentle-RED, if avg is greater than - * max_th the packet is dropped with a probability - * p_b = c_3 * avg - c_4 - * where c_3 = (1 - max_p) / max_th - * c_4 = 1 - 2 * max_p - */ - p_b = SCALE_MUL((int64_t)fs->c_3, (int64_t)q->avg) - - fs->c_4; - } else { - q->count = -1; - return (1); - } - } else if (q->avg > fs->min_th) { - /* - * We compute p_b using the linear dropping function - * p_b = c_1 * avg - c_2 - * where c_1 = max_p / (max_th - min_th) - * c_2 = max_p * min_th / (max_th - min_th) - */ - p_b = SCALE_MUL((int64_t)fs->c_1, (int64_t)q->avg) - fs->c_2; - } - - if (fs->fs.flags & DN_QSIZE_BYTES) - p_b = div64((p_b * len) , fs->max_pkt_size); - if (++q->count == 0) - q->random = random() & 0xffff; - else { - /* - * q->count counts packets arrived since last drop, so a greater - * value of q->count means a greater packet drop probability. - */ - if (SCALE_MUL(p_b, SCALE((int64_t)q->count)) > q->random) { - q->count = 0; - /* After a drop we calculate a new random value. */ - q->random = random() & 0xffff; - return (1); /* drop */ - } - } - /* End of RED algorithm. */ - - return (0); /* accept */ - -} - -/* - * Enqueue a packet in q, subject to space and queue management policy - * (whose parameters are in q->fs). - * Update stats for the queue and the scheduler. - * Return 0 on success, 1 on drop. The packet is consumed anyways. - */ -int -dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) -{ - struct dn_fs *f; - struct dn_flow *ni; /* stats for scheduler instance */ - uint64_t len; - - if (q->fs == NULL || q->_si == NULL) { - printf("%s fs %p si %p, dropping\n", - __FUNCTION__, q->fs, q->_si); - FREE_PKT(m); - return 1; - } - f = &(q->fs->fs); - ni = &q->_si->ni; - len = m->m_pkthdr.len; - /* Update statistics, then check reasons to drop pkt. */ - q->ni.tot_bytes += len; - q->ni.tot_pkts++; - ni->tot_bytes += len; - ni->tot_pkts++; - if (drop) - goto drop; - if (f->plr && random() < f->plr) - goto drop; - if (f->flags & DN_IS_RED && red_drops(q, m->m_pkthdr.len)) - goto drop; - if (f->flags & DN_QSIZE_BYTES) { - if (q->ni.len_bytes > f->qsize) - goto drop; - } else if (q->ni.length >= f->qsize) { - goto drop; - } - mq_append(&q->mq, m); - q->ni.length++; - q->ni.len_bytes += len; - ni->length++; - ni->len_bytes += len; - return 0; - -drop: - io_pkt_drop++; - q->ni.drops++; - ni->drops++; - FREE_PKT(m); - return 1; -} - -/* - * Fetch packets from the delay line which are due now. If there are - * leftover packets, reinsert the delay line in the heap. - * Runs under scheduler lock. - */ -static void -transmit_event(struct mq *q, struct delay_line *dline, uint64_t now) -{ - struct mbuf *m; - struct dn_pkt_tag *pkt = NULL; - - dline->oid.subtype = 0; /* not in heap */ - while ((m = dline->mq.head) != NULL) { - pkt = dn_tag_get(m); - if (!DN_KEY_LEQ(pkt->output_time, now)) - break; - dline->mq.head = m->m_nextpkt; - mq_append(q, m); - } - if (m != NULL) { - dline->oid.subtype = 1; /* in heap */ - heap_insert(&dn_cfg.evheap, pkt->output_time, dline); - } -} - -/* - * Convert the additional MAC overheads/delays into an equivalent - * number of bits for the given data rate. The samples are - * in milliseconds so we need to divide by 1000. - */ -static uint64_t -extra_bits(struct mbuf *m, struct dn_schk *s) -{ - int index; - uint64_t bits; - struct dn_profile *pf = s->profile; - - if (!pf || pf->samples_no == 0) - return 0; - index = random() % pf->samples_no; - bits = div64((uint64_t)pf->samples[index] * s->link.bandwidth, 1000); - if (index >= pf->loss_level) { - struct dn_pkt_tag *dt = dn_tag_get(m); - if (dt) - dt->dn_dir = DIR_DROP; - } - return bits; -} - -/* - * Send traffic from a scheduler instance due by 'now'. - * Return a pointer to the head of the queue. - */ -static struct mbuf * -serve_sched(struct mq *q, struct dn_sch_inst *si, uint64_t now) -{ - struct mq def_q; - struct dn_schk *s = si->sched; - struct mbuf *m = NULL; - int delay_line_idle = (si->dline.mq.head == NULL); - int done, bw; - - if (q == NULL) { - q = &def_q; - q->head = NULL; - } - - bw = s->link.bandwidth; - si->kflags &= ~DN_ACTIVE; - - if (bw > 0) - si->credit += (now - si->sched_time) * bw; - else - si->credit = 0; - si->sched_time = now; - done = 0; - while (si->credit >= 0 && (m = s->fp->dequeue(si)) != NULL) { - uint64_t len_scaled; - - done++; - len_scaled = (bw == 0) ? 0 : hz * - (m->m_pkthdr.len * 8 + extra_bits(m, s)); - si->credit -= len_scaled; - /* Move packet in the delay line */ - dn_tag_get(m)->output_time = dn_cfg.curr_time + s->link.delay ; - mq_append(&si->dline.mq, m); - } - - /* - * If credit >= 0 the instance is idle, mark time. - * Otherwise put back in the heap, and adjust the output - * time of the last inserted packet, m, which was too early. - */ - if (si->credit >= 0) { - si->idle_time = now; - } else { - uint64_t t; - KASSERT (bw > 0, ("bw=0 and credit<0 ?")); - t = div64(bw - 1 - si->credit, bw); - if (m) - dn_tag_get(m)->output_time += t; - si->kflags |= DN_ACTIVE; - heap_insert(&dn_cfg.evheap, now + t, si); - } - if (delay_line_idle && done) - transmit_event(q, &si->dline, now); - return q->head; -} - -/* - * The timer handler for dummynet. Time is computed in ticks, but - * but the code is tolerant to the actual rate at which this is called. - * Once complete, the function reschedules itself for the next tick. - */ -void -dummynet_task(void *context, int pending) -{ - struct timeval t; - struct mq q = { NULL, NULL }; /* queue to accumulate results */ - - CURVNET_SET((struct vnet *)context); - - DN_BH_WLOCK(); - - /* Update number of lost(coalesced) ticks. */ - tick_lost += pending - 1; - - getmicrouptime(&t); - /* Last tick duration (usec). */ - tick_last = (t.tv_sec - dn_cfg.prev_t.tv_sec) * 1000000 + - (t.tv_usec - dn_cfg.prev_t.tv_usec); - /* Last tick vs standard tick difference (usec). */ - tick_delta = (tick_last * hz - 1000000) / hz; - /* Accumulated tick difference (usec). */ - tick_delta_sum += tick_delta; - - dn_cfg.prev_t = t; - - /* - * Adjust curr_time if the accumulated tick difference is - * greater than the 'standard' tick. Since curr_time should - * be monotonically increasing, we do positive adjustments - * as required, and throttle curr_time in case of negative - * adjustment. - */ - dn_cfg.curr_time++; - if (tick_delta_sum - tick >= 0) { - int diff = tick_delta_sum / tick; - - dn_cfg.curr_time += diff; - tick_diff += diff; - tick_delta_sum %= tick; - tick_adjustment++; - } else if (tick_delta_sum + tick <= 0) { - dn_cfg.curr_time--; - tick_diff--; - tick_delta_sum += tick; - tick_adjustment++; - } - - /* serve pending events, accumulate in q */ - for (;;) { - struct dn_id *p; /* generic parameter to handler */ - - if (dn_cfg.evheap.elements == 0 || - DN_KEY_LT(dn_cfg.curr_time, HEAP_TOP(&dn_cfg.evheap)->key)) - break; - p = HEAP_TOP(&dn_cfg.evheap)->object; - heap_extract(&dn_cfg.evheap, NULL); - - if (p->type == DN_SCH_I) { - serve_sched(&q, (struct dn_sch_inst *)p, dn_cfg.curr_time); - } else { /* extracted a delay line */ - transmit_event(&q, (struct delay_line *)p, dn_cfg.curr_time); - } - } - if (dn_cfg.expire && ++dn_cfg.expire_cycle >= dn_cfg.expire) { - dn_cfg.expire_cycle = 0; - dn_drain_scheduler(); - dn_drain_queue(); - } - - DN_BH_WUNLOCK(); - dn_reschedule(); - if (q.head != NULL) - dummynet_send(q.head); - CURVNET_RESTORE(); -} - -/* - * forward a chain of packets to the proper destination. - * This runs outside the dummynet lock. - */ -static void -dummynet_send(struct mbuf *m) -{ - struct mbuf *n; - - for (; m != NULL; m = n) { - struct ifnet *ifp = NULL; /* gcc 3.4.6 complains */ - struct m_tag *tag; - int dst; - - n = m->m_nextpkt; - m->m_nextpkt = NULL; - tag = m_tag_first(m); - if (tag == NULL) { /* should not happen */ - dst = DIR_DROP; - } else { - struct dn_pkt_tag *pkt = dn_tag_get(m); - /* extract the dummynet info, rename the tag - * to carry reinject info. - */ - dst = pkt->dn_dir; - ifp = pkt->ifp; - tag->m_tag_cookie = MTAG_IPFW_RULE; - tag->m_tag_id = 0; - } - - switch (dst) { - case DIR_OUT: - SET_HOST_IPLEN(mtod(m, struct ip *)); - ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); - break ; - - case DIR_IN : - /* put header in network format for ip_input() */ - //SET_NET_IPLEN(mtod(m, struct ip *)); - netisr_dispatch(NETISR_IP, m); - break; - -#ifdef INET6 - case DIR_IN | PROTO_IPV6: - netisr_dispatch(NETISR_IPV6, m); - break; - - case DIR_OUT | PROTO_IPV6: - ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL); - break; -#endif - - case DIR_FWD | PROTO_IFB: /* DN_TO_IFB_FWD: */ - if (bridge_dn_p != NULL) - ((*bridge_dn_p)(m, ifp)); - else - printf("dummynet: if_bridge not loaded\n"); - - break; - - case DIR_IN | PROTO_LAYER2: /* DN_TO_ETH_DEMUX: */ - /* - * The Ethernet code assumes the Ethernet header is - * contiguous in the first mbuf header. - * Insure this is true. - */ - if (m->m_len < ETHER_HDR_LEN && - (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) { - printf("dummynet/ether: pullup failed, " - "dropping packet\n"); - break; - } - ether_demux(m->m_pkthdr.rcvif, m); - break; - - case DIR_OUT | PROTO_LAYER2: /* N_TO_ETH_OUT: */ - ether_output_frame(ifp, m); - break; - - case DIR_DROP: - /* drop the packet after some time */ - FREE_PKT(m); - break; - - default: - printf("dummynet: bad switch %d!\n", dst); - FREE_PKT(m); - break; - } - } -} - -static inline int -tag_mbuf(struct mbuf *m, int dir, struct ip_fw_args *fwa) -{ - struct dn_pkt_tag *dt; - struct m_tag *mtag; - - mtag = m_tag_get(PACKET_TAG_DUMMYNET, - sizeof(*dt), M_NOWAIT | M_ZERO); - if (mtag == NULL) - return 1; /* Cannot allocate packet header. */ - m_tag_prepend(m, mtag); /* Attach to mbuf chain. */ - dt = (struct dn_pkt_tag *)(mtag + 1); - dt->rule = fwa->rule; - dt->rule.info &= IPFW_ONEPASS; /* only keep this info */ - dt->dn_dir = dir; - dt->ifp = fwa->oif; - /* dt->output tame is updated as we move through */ - dt->output_time = dn_cfg.curr_time; - return 0; -} - - -/* - * dummynet hook for packets. - * We use the argument to locate the flowset fs and the sched_set sch - * associated to it. The we apply flow_mask and sched_mask to - * determine the queue and scheduler instances. - * - * dir where shall we send the packet after dummynet. - * *m0 the mbuf with the packet - * ifp the 'ifp' parameter from the caller. - * NULL in ip_input, destination interface in ip_output, - */ -int -dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa) -{ - struct mbuf *m = *m0; - struct dn_fsk *fs = NULL; - struct dn_sch_inst *si; - struct dn_queue *q = NULL; /* default */ - - int fs_id = (fwa->rule.info & IPFW_INFO_MASK) + - ((fwa->rule.info & IPFW_IS_PIPE) ? 2*DN_MAX_ID : 0); - DN_BH_WLOCK(); - io_pkt++; - /* we could actually tag outside the lock, but who cares... */ - if (tag_mbuf(m, dir, fwa)) - goto dropit; - if (dn_cfg.busy) { - /* if the upper half is busy doing something expensive, - * lets queue the packet and move forward - */ - mq_append(&dn_cfg.pending, m); - m = *m0 = NULL; /* consumed */ - goto done; /* already active, nothing to do */ - } - /* XXX locate_flowset could be optimised with a direct ref. */ - fs = dn_ht_find(dn_cfg.fshash, fs_id, 0, NULL); - if (fs == NULL) - goto dropit; /* This queue/pipe does not exist! */ - if (fs->sched == NULL) /* should not happen */ - goto dropit; - /* find scheduler instance, possibly applying sched_mask */ - si = ipdn_si_find(fs->sched, &(fwa->f_id)); - if (si == NULL) - goto dropit; - /* - * If the scheduler supports multiple queues, find the right one - * (otherwise it will be ignored by enqueue). - */ - if (fs->sched->fp->flags & DN_MULTIQUEUE) { - q = ipdn_q_find(fs, si, &(fwa->f_id)); - if (q == NULL) - goto dropit; - } - if (fs->sched->fp->enqueue(si, q, m)) { - /* packet was dropped by enqueue() */ - m = *m0 = NULL; - goto dropit; - } - - if (si->kflags & DN_ACTIVE) { - m = *m0 = NULL; /* consumed */ - goto done; /* already active, nothing to do */ - } - - /* compute the initial allowance */ - if (si->idle_time < dn_cfg.curr_time) { - /* Do this only on the first packet on an idle pipe */ - struct dn_link *p = &fs->sched->link; - - si->sched_time = dn_cfg.curr_time; - si->credit = dn_cfg.io_fast ? p->bandwidth : 0; - if (p->burst) { - uint64_t burst = (dn_cfg.curr_time - si->idle_time) * p->bandwidth; - if (burst > p->burst) - burst = p->burst; - si->credit += burst; - } - } - /* pass through scheduler and delay line */ - m = serve_sched(NULL, si, dn_cfg.curr_time); - - /* optimization -- pass it back to ipfw for immediate send */ - /* XXX Don't call dummynet_send() if scheduler return the packet - * just enqueued. This avoid a lock order reversal. - * - */ - if (/*dn_cfg.io_fast &&*/ m == *m0 && (dir & PROTO_LAYER2) == 0 ) { - /* fast io, rename the tag * to carry reinject info. */ - struct m_tag *tag = m_tag_first(m); - - tag->m_tag_cookie = MTAG_IPFW_RULE; - tag->m_tag_id = 0; - io_pkt_fast++; - if (m->m_nextpkt != NULL) { - printf("dummynet: fast io: pkt chain detected!\n"); - m->m_nextpkt = NULL; - } - m = NULL; - } else { - *m0 = NULL; - } -done: - DN_BH_WUNLOCK(); - if (m) - dummynet_send(m); - return 0; - -dropit: - io_pkt_drop++; - DN_BH_WUNLOCK(); - if (m) - FREE_PKT(m); - *m0 = NULL; - return (fs && (fs->fs.flags & DN_NOERROR)) ? 0 : ENOBUFS; -} diff --git a/sys/netinet/ipfw/ip_dn_private.h b/sys/netinet/ipfw/ip_dn_private.h deleted file mode 100644 index 159ddc9ab8b29..0000000000000 --- a/sys/netinet/ipfw/ip_dn_private.h +++ /dev/null @@ -1,403 +0,0 @@ -/*- - * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa - * 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. - */ - -/* - * internal dummynet APIs. - * - * $FreeBSD$ - */ - -#ifndef _IP_DN_PRIVATE_H -#define _IP_DN_PRIVATE_H - -/* debugging support - * use ND() to remove debugging, D() to print a line, - * DX(level, ...) to print above a certain level - * If you redefine D() you are expected to redefine all. - */ -#ifndef D -#define ND(fmt, ...) do {} while (0) -#define D1(fmt, ...) do {} while (0) -#define D(fmt, ...) printf("%-10s " fmt "\n", \ - __FUNCTION__, ## __VA_ARGS__) -#define DX(lev, fmt, ...) do { \ - if (dn_cfg.debug > lev) D(fmt, ## __VA_ARGS__); } while (0) -#endif - -MALLOC_DECLARE(M_DUMMYNET); - -#ifndef __linux__ -#define div64(a, b) ((int64_t)(a) / (int64_t)(b)) -#endif - -#define DN_LOCK_INIT() do { \ - mtx_init(&dn_cfg.uh_mtx, "dn_uh", NULL, MTX_DEF); \ - mtx_init(&dn_cfg.bh_mtx, "dn_bh", NULL, MTX_DEF); \ - } while (0) -#define DN_LOCK_DESTROY() do { \ - mtx_destroy(&dn_cfg.uh_mtx); \ - mtx_destroy(&dn_cfg.bh_mtx); \ - } while (0) -#if 0 /* not used yet */ -#define DN_UH_RLOCK() mtx_lock(&dn_cfg.uh_mtx) -#define DN_UH_RUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) -#define DN_UH_WLOCK() mtx_lock(&dn_cfg.uh_mtx) -#define DN_UH_WUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) -#define DN_UH_LOCK_ASSERT() mtx_assert(&dn_cfg.uh_mtx, MA_OWNED) -#endif - -#define DN_BH_RLOCK() mtx_lock(&dn_cfg.uh_mtx) -#define DN_BH_RUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) -#define DN_BH_WLOCK() mtx_lock(&dn_cfg.uh_mtx) -#define DN_BH_WUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) -#define DN_BH_LOCK_ASSERT() mtx_assert(&dn_cfg.uh_mtx, MA_OWNED) - -SLIST_HEAD(dn_schk_head, dn_schk); -SLIST_HEAD(dn_sch_inst_head, dn_sch_inst); -SLIST_HEAD(dn_fsk_head, dn_fsk); -SLIST_HEAD(dn_queue_head, dn_queue); -SLIST_HEAD(dn_alg_head, dn_alg); - -struct mq { /* a basic queue of packets*/ - struct mbuf *head, *tail; -}; - -static inline void -set_oid(struct dn_id *o, int type, int len) -{ - o->type = type; - o->len = len; - o->subtype = 0; -}; - -/* - * configuration and global data for a dummynet instance - * - * When a configuration is modified from userland, 'id' is incremented - * so we can use the value to check for stale pointers. - */ -struct dn_parms { - uint32_t id; /* configuration version */ - - /* defaults (sysctl-accessible) */ - int red_lookup_depth; - int red_avg_pkt_size; - int red_max_pkt_size; - int hash_size; - int max_hash_size; - long byte_limit; /* max queue sizes */ - long slot_limit; - - int io_fast; - int debug; - - /* timekeeping */ - struct timeval prev_t; /* last time dummynet_tick ran */ - struct dn_heap evheap; /* scheduled events */ - - /* counters of objects -- used for reporting space */ - int schk_count; - int si_count; - int fsk_count; - int queue_count; - - /* ticks and other stuff */ - uint64_t curr_time; - /* flowsets and schedulers are in hash tables, with 'hash_size' - * buckets. fshash is looked up at every packet arrival - * so better be generous if we expect many entries. - */ - struct dn_ht *fshash; - struct dn_ht *schedhash; - /* list of flowsets without a scheduler -- use sch_chain */ - struct dn_fsk_head fsu; /* list of unlinked flowsets */ - struct dn_alg_head schedlist; /* list of algorithms */ - - /* Store the fs/sch to scan when draining. The value is the - * bucket number of the hash table. Expire can be disabled - * with net.inet.ip.dummynet.expire=0, or it happens every - * expire ticks. - **/ - int drain_fs; - int drain_sch; - uint32_t expire; - uint32_t expire_cycle; /* tick count */ - - int init_done; - - /* if the upper half is busy doing something long, - * can set the busy flag and we will enqueue packets in - * a queue for later processing. - */ - int busy; - struct mq pending; - -#ifdef _KERNEL - /* - * This file is normally used in the kernel, unless we do - * some userland tests, in which case we do not need a mtx. - * uh_mtx arbitrates between system calls and also - * protects fshash, schedhash and fsunlinked. - * These structures are readonly for the lower half. - * bh_mtx protects all other structures which may be - * modified upon packet arrivals - */ -#if defined( __linux__ ) || defined( _WIN32 ) - spinlock_t uh_mtx; - spinlock_t bh_mtx; -#else - struct mtx uh_mtx; - struct mtx bh_mtx; -#endif - -#endif /* _KERNEL */ -}; - -/* - * Delay line, contains all packets on output from a link. - * Every scheduler instance has one. - */ -struct delay_line { - struct dn_id oid; - struct dn_sch_inst *si; - struct mq mq; -}; - -/* - * The kernel side of a flowset. It is linked in a hash table - * of flowsets, and in a list of children of their parent scheduler. - * qht is either the queue or (if HAVE_MASK) a hash table queues. - * Note that the mask to use is the (flow_mask|sched_mask), which - * changes as we attach/detach schedulers. So we store it here. - * - * XXX If we want to add scheduler-specific parameters, we need to - * put them in external storage because the scheduler may not be - * available when the fsk is created. - */ -struct dn_fsk { /* kernel side of a flowset */ - struct dn_fs fs; - SLIST_ENTRY(dn_fsk) fsk_next; /* hash chain for fshash */ - - struct ipfw_flow_id fsk_mask; - - /* qht is a hash table of queues, or just a single queue - * a bit in fs.flags tells us which one - */ - struct dn_ht *qht; - struct dn_schk *sched; /* Sched we are linked to */ - SLIST_ENTRY(dn_fsk) sch_chain; /* list of fsk attached to sched */ - - /* bucket index used by drain routine to drain queues for this - * flowset - */ - int drain_bucket; - /* Parameter realted to RED / GRED */ - /* original values are in dn_fs*/ - int w_q ; /* queue weight (scaled) */ - int max_th ; /* maximum threshold for queue (scaled) */ - int min_th ; /* minimum threshold for queue (scaled) */ - int max_p ; /* maximum value for p_b (scaled) */ - - u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ - u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ - u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ - u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ - u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ - u_int lookup_depth ; /* depth of lookup table */ - int lookup_step ; /* granularity inside the lookup table */ - int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ - int avg_pkt_size ; /* medium packet size */ - int max_pkt_size ; /* max packet size */ -}; - -/* - * A queue is created as a child of a flowset unless it belongs to - * a !MULTIQUEUE scheduler. It is normally in a hash table in the - * flowset. fs always points to the parent flowset. - * si normally points to the sch_inst, unless the flowset has been - * detached from the scheduler -- in this case si == NULL and we - * should not enqueue. - */ -struct dn_queue { - struct dn_flow ni; /* oid, flow_id, stats */ - struct mq mq; /* packets queue */ - struct dn_sch_inst *_si; /* owner scheduler instance */ - SLIST_ENTRY(dn_queue) q_next; /* hash chain list for qht */ - struct dn_fsk *fs; /* parent flowset. */ - - /* RED parameters */ - int avg; /* average queue length est. (scaled) */ - int count; /* arrivals since last RED drop */ - int random; /* random value (scaled) */ - uint64_t q_time; /* start of queue idle time */ - -}; - -/* - * The kernel side of a scheduler. Contains the userland config, - * a link, pointer to extra config arguments from command line, - * kernel flags, and a pointer to the scheduler methods. - * It is stored in a hash table, and holds a list of all - * flowsets and scheduler instances. - * XXX sch must be at the beginning, see schk_hash(). - */ -struct dn_schk { - struct dn_sch sch; - struct dn_alg *fp; /* Pointer to scheduler functions */ - struct dn_link link; /* The link, embedded */ - struct dn_profile *profile; /* delay profile, if any */ - struct dn_id *cfg; /* extra config arguments */ - - SLIST_ENTRY(dn_schk) schk_next; /* hash chain for schedhash */ - - struct dn_fsk_head fsk_list; /* all fsk linked to me */ - struct dn_fsk *fs; /* Flowset for !MULTIQUEUE */ - - /* bucket index used by the drain routine to drain the scheduler - * instance for this flowset. - */ - int drain_bucket; - - /* Hash table of all instances (through sch.sched_mask) - * or single instance if no mask. Always valid. - */ - struct dn_ht *siht; -}; - - -/* - * Scheduler instance. - * Contains variables and all queues relative to a this instance. - * This struct is created a runtime. - */ -struct dn_sch_inst { - struct dn_flow ni; /* oid, flowid and stats */ - SLIST_ENTRY(dn_sch_inst) si_next; /* hash chain for siht */ - struct delay_line dline; - struct dn_schk *sched; /* the template */ - int kflags; /* DN_ACTIVE */ - - int64_t credit; /* bits I can transmit (more or less). */ - uint64_t sched_time; /* time link was scheduled in ready_heap */ - uint64_t idle_time; /* start of scheduler instance idle time */ - - /* q_count is the number of queues that this instance is using. - * The counter is incremented or decremented when - * a reference from the queue is created or deleted. - * It is used to make sure that a scheduler instance can be safely - * deleted by the drain routine. See notes below. - */ - int q_count; - -}; - -/* - * NOTE about object drain. - * The system will automatically (XXX check when) drain queues and - * scheduler instances when they are idle. - * A queue is idle when it has no packets; an instance is idle when - * it is not in the evheap heap, and the corresponding delay line is empty. - * A queue can be safely deleted when it is idle because of the scheduler - * function xxx_free_queue() will remove any references to it. - * An instance can be only deleted when no queues reference it. To be sure - * of that, a counter (q_count) stores the number of queues that are pointing - * to the instance. - * - * XXX - * Order of scan: - * - take all flowset in a bucket for the flowset hash table - * - take all queues in a bucket for the flowset - * - increment the queue bucket - * - scan next flowset bucket - * Nothing is done if a bucket contains no entries. - * - * The same schema is used for sceduler instances - */ - - -/* kernel-side flags. Linux has DN_DELETE in fcntl.h - */ -enum { - /* 1 and 2 are reserved for the SCAN flags */ - DN_DESTROY = 0x0004, /* destroy */ - DN_DELETE_FS = 0x0008, /* destroy flowset */ - DN_DETACH = 0x0010, - DN_ACTIVE = 0x0020, /* object is in evheap */ - DN_F_DLINE = 0x0040, /* object is a delay line */ - DN_DEL_SAFE = 0x0080, /* delete a queue only if no longer needed - * by scheduler */ - DN_QHT_IS_Q = 0x0100, /* in flowset, qht is a single queue */ -}; - -extern struct dn_parms dn_cfg; -//VNET_DECLARE(struct dn_parms, _base_dn_cfg); -//#define dn_cfg VNET(_base_dn_cfg) - -int dummynet_io(struct mbuf **, int , struct ip_fw_args *); -void dummynet_task(void *context, int pending); -void dn_reschedule(void); - -struct dn_queue *ipdn_q_find(struct dn_fsk *, struct dn_sch_inst *, - struct ipfw_flow_id *); -struct dn_sch_inst *ipdn_si_find(struct dn_schk *, struct ipfw_flow_id *); - -/* - * copy_range is a template for requests for ranges of pipes/queues/scheds. - * The number of ranges is variable and can be derived by o.len. - * As a default, we use a small number of entries so that the struct - * fits easily on the stack and is sufficient for most common requests. - */ -#define DEFAULT_RANGES 5 -struct copy_range { - struct dn_id o; - uint32_t r[ 2 * DEFAULT_RANGES ]; -}; - -struct copy_args { - char **start; - char *end; - int flags; - int type; - struct copy_range *extra; /* extra filtering */ -}; - -struct sockopt; -int ip_dummynet_compat(struct sockopt *sopt); -int dummynet_get(struct sockopt *sopt, void **compat); -int dn_c_copy_q (void *_ni, void *arg); -int dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq); -int dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq); -int dn_compat_copy_queue(struct copy_args *a, void *_o); -int dn_compat_copy_pipe(struct copy_args *a, void *_o); -int copy_data_helper_compat(void *_o, void *_arg); -int dn_compat_calc_size(void); -int do_config(void *p, int l); - -/* function to drain idle object */ -void dn_drain_scheduler(void); -void dn_drain_queue(void); - -#endif /* _IP_DN_PRIVATE_H */ diff --git a/sys/netinet/ipfw/ip_dummynet.c b/sys/netinet/ipfw/ip_dummynet.c deleted file mode 100644 index e1c7a08fb16e9..0000000000000 --- a/sys/netinet/ipfw/ip_dummynet.c +++ /dev/null @@ -1,2314 +0,0 @@ -/*- - * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa - * Portions Copyright (c) 2000 Akamba Corp. - * All rights reserved - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -/* - * Configuration and internal object management for dummynet. - */ - -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ -#include -#include /* ip_output(), IP_FORWARDING */ -#include -#include -#include -#include -#include -#include - -/* which objects to copy */ -#define DN_C_LINK 0x01 -#define DN_C_SCH 0x02 -#define DN_C_FLOW 0x04 -#define DN_C_FS 0x08 -#define DN_C_QUEUE 0x10 - -/* we use this argument in case of a schk_new */ -struct schk_new_arg { - struct dn_alg *fp; - struct dn_sch *sch; -}; - -/*---- callout hooks. ----*/ -static struct callout dn_timeout; -static struct task dn_task; -static struct taskqueue *dn_tq = NULL; - -static void -dummynet(void *arg) -{ - - (void)arg; /* UNUSED */ - taskqueue_enqueue(dn_tq, &dn_task); -} - -void -dn_reschedule(void) -{ - callout_reset(&dn_timeout, 1, dummynet, NULL); -} -/*----- end of callout hooks -----*/ - -/* Return a scheduler descriptor given the type or name. */ -static struct dn_alg * -find_sched_type(int type, char *name) -{ - struct dn_alg *d; - - SLIST_FOREACH(d, &dn_cfg.schedlist, next) { - if (d->type == type || (name && !strcasecmp(d->name, name))) - return d; - } - return NULL; /* not found */ -} - -int -ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg) -{ - int oldv = *v; - const char *op = NULL; - if (dflt < lo) - dflt = lo; - if (dflt > hi) - dflt = hi; - if (oldv < lo) { - *v = dflt; - op = "Bump"; - } else if (oldv > hi) { - *v = hi; - op = "Clamp"; - } else - return *v; - if (op && msg) - printf("%s %s to %d (was %d)\n", op, msg, *v, oldv); - return *v; -} - -/*---- flow_id mask, hash and compare functions ---*/ -/* - * The flow_id includes the 5-tuple, the queue/pipe number - * which we store in the extra area in host order, - * and for ipv6 also the flow_id6. - * XXX see if we want the tos byte (can store in 'flags') - */ -static struct ipfw_flow_id * -flow_id_mask(struct ipfw_flow_id *mask, struct ipfw_flow_id *id) -{ - int is_v6 = IS_IP6_FLOW_ID(id); - - id->dst_port &= mask->dst_port; - id->src_port &= mask->src_port; - id->proto &= mask->proto; - id->extra &= mask->extra; - if (is_v6) { - APPLY_MASK(&id->dst_ip6, &mask->dst_ip6); - APPLY_MASK(&id->src_ip6, &mask->src_ip6); - id->flow_id6 &= mask->flow_id6; - } else { - id->dst_ip &= mask->dst_ip; - id->src_ip &= mask->src_ip; - } - return id; -} - -/* computes an OR of two masks, result in dst and also returned */ -static struct ipfw_flow_id * -flow_id_or(struct ipfw_flow_id *src, struct ipfw_flow_id *dst) -{ - int is_v6 = IS_IP6_FLOW_ID(dst); - - dst->dst_port |= src->dst_port; - dst->src_port |= src->src_port; - dst->proto |= src->proto; - dst->extra |= src->extra; - if (is_v6) { -#define OR_MASK(_d, _s) \ - (_d)->__u6_addr.__u6_addr32[0] |= (_s)->__u6_addr.__u6_addr32[0]; \ - (_d)->__u6_addr.__u6_addr32[1] |= (_s)->__u6_addr.__u6_addr32[1]; \ - (_d)->__u6_addr.__u6_addr32[2] |= (_s)->__u6_addr.__u6_addr32[2]; \ - (_d)->__u6_addr.__u6_addr32[3] |= (_s)->__u6_addr.__u6_addr32[3]; - OR_MASK(&dst->dst_ip6, &src->dst_ip6); - OR_MASK(&dst->src_ip6, &src->src_ip6); -#undef OR_MASK - dst->flow_id6 |= src->flow_id6; - } else { - dst->dst_ip |= src->dst_ip; - dst->src_ip |= src->src_ip; - } - return dst; -} - -static int -nonzero_mask(struct ipfw_flow_id *m) -{ - if (m->dst_port || m->src_port || m->proto || m->extra) - return 1; - if (IS_IP6_FLOW_ID(m)) { - return - m->dst_ip6.__u6_addr.__u6_addr32[0] || - m->dst_ip6.__u6_addr.__u6_addr32[1] || - m->dst_ip6.__u6_addr.__u6_addr32[2] || - m->dst_ip6.__u6_addr.__u6_addr32[3] || - m->src_ip6.__u6_addr.__u6_addr32[0] || - m->src_ip6.__u6_addr.__u6_addr32[1] || - m->src_ip6.__u6_addr.__u6_addr32[2] || - m->src_ip6.__u6_addr.__u6_addr32[3] || - m->flow_id6; - } else { - return m->dst_ip || m->src_ip; - } -} - -/* XXX we may want a better hash function */ -static uint32_t -flow_id_hash(struct ipfw_flow_id *id) -{ - uint32_t i; - - if (IS_IP6_FLOW_ID(id)) { - uint32_t *d = (uint32_t *)&id->dst_ip6; - uint32_t *s = (uint32_t *)&id->src_ip6; - i = (d[0] ) ^ (d[1]) ^ - (d[2] ) ^ (d[3]) ^ - (d[0] >> 15) ^ (d[1] >> 15) ^ - (d[2] >> 15) ^ (d[3] >> 15) ^ - (s[0] << 1) ^ (s[1] << 1) ^ - (s[2] << 1) ^ (s[3] << 1) ^ - (s[0] << 16) ^ (s[1] << 16) ^ - (s[2] << 16) ^ (s[3] << 16) ^ - (id->dst_port << 1) ^ (id->src_port) ^ - (id->extra) ^ - (id->proto ) ^ (id->flow_id6); - } else { - i = (id->dst_ip) ^ (id->dst_ip >> 15) ^ - (id->src_ip << 1) ^ (id->src_ip >> 16) ^ - (id->extra) ^ - (id->dst_port << 1) ^ (id->src_port) ^ (id->proto); - } - return i; -} - -/* Like bcmp, returns 0 if ids match, 1 otherwise. */ -static int -flow_id_cmp(struct ipfw_flow_id *id1, struct ipfw_flow_id *id2) -{ - int is_v6 = IS_IP6_FLOW_ID(id1); - - if (!is_v6) { - if (IS_IP6_FLOW_ID(id2)) - return 1; /* different address families */ - - return (id1->dst_ip == id2->dst_ip && - id1->src_ip == id2->src_ip && - id1->dst_port == id2->dst_port && - id1->src_port == id2->src_port && - id1->proto == id2->proto && - id1->extra == id2->extra) ? 0 : 1; - } - /* the ipv6 case */ - return ( - !bcmp(&id1->dst_ip6,&id2->dst_ip6, sizeof(id1->dst_ip6)) && - !bcmp(&id1->src_ip6,&id2->src_ip6, sizeof(id1->src_ip6)) && - id1->dst_port == id2->dst_port && - id1->src_port == id2->src_port && - id1->proto == id2->proto && - id1->extra == id2->extra && - id1->flow_id6 == id2->flow_id6) ? 0 : 1; -} -/*--------- end of flow-id mask, hash and compare ---------*/ - -/*--- support functions for the qht hashtable ---- - * Entries are hashed by flow-id - */ -static uint32_t -q_hash(uintptr_t key, int flags, void *arg) -{ - /* compute the hash slot from the flow id */ - struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ? - &((struct dn_queue *)key)->ni.fid : - (struct ipfw_flow_id *)key; - - return flow_id_hash(id); -} - -static int -q_match(void *obj, uintptr_t key, int flags, void *arg) -{ - struct dn_queue *o = (struct dn_queue *)obj; - struct ipfw_flow_id *id2; - - if (flags & DNHT_KEY_IS_OBJ) { - /* compare pointers */ - id2 = &((struct dn_queue *)key)->ni.fid; - } else { - id2 = (struct ipfw_flow_id *)key; - } - return (0 == flow_id_cmp(&o->ni.fid, id2)); -} - -/* - * create a new queue instance for the given 'key'. - */ -static void * -q_new(uintptr_t key, int flags, void *arg) -{ - struct dn_queue *q, *template = arg; - struct dn_fsk *fs = template->fs; - int size = sizeof(*q) + fs->sched->fp->q_datalen; - - q = malloc(size, M_DUMMYNET, M_NOWAIT | M_ZERO); - if (q == NULL) { - D("no memory for new queue"); - return NULL; - } - - set_oid(&q->ni.oid, DN_QUEUE, size); - if (fs->fs.flags & DN_QHT_HASH) - q->ni.fid = *(struct ipfw_flow_id *)key; - q->fs = fs; - q->_si = template->_si; - q->_si->q_count++; - - if (fs->sched->fp->new_queue) - fs->sched->fp->new_queue(q); - dn_cfg.queue_count++; - return q; -} - -/* - * Notify schedulers that a queue is going away. - * If (flags & DN_DESTROY), also free the packets. - * The version for callbacks is called q_delete_cb(). - */ -static void -dn_delete_queue(struct dn_queue *q, int flags) -{ - struct dn_fsk *fs = q->fs; - - // D("fs %p si %p\n", fs, q->_si); - /* notify the parent scheduler that the queue is going away */ - if (fs && fs->sched->fp->free_queue) - fs->sched->fp->free_queue(q); - q->_si->q_count--; - q->_si = NULL; - if (flags & DN_DESTROY) { - if (q->mq.head) - dn_free_pkts(q->mq.head); - bzero(q, sizeof(*q)); // safety - free(q, M_DUMMYNET); - dn_cfg.queue_count--; - } -} - -static int -q_delete_cb(void *q, void *arg) -{ - int flags = (int)(uintptr_t)arg; - dn_delete_queue(q, flags); - return (flags & DN_DESTROY) ? DNHT_SCAN_DEL : 0; -} - -/* - * calls dn_delete_queue/q_delete_cb on all queues, - * which notifies the parent scheduler and possibly drains packets. - * flags & DN_DESTROY: drains queues and destroy qht; - */ -static void -qht_delete(struct dn_fsk *fs, int flags) -{ - ND("fs %d start flags %d qht %p", - fs->fs.fs_nr, flags, fs->qht); - if (!fs->qht) - return; - if (fs->fs.flags & DN_QHT_HASH) { - dn_ht_scan(fs->qht, q_delete_cb, (void *)(uintptr_t)flags); - if (flags & DN_DESTROY) { - dn_ht_free(fs->qht, 0); - fs->qht = NULL; - } - } else { - dn_delete_queue((struct dn_queue *)(fs->qht), flags); - if (flags & DN_DESTROY) - fs->qht = NULL; - } -} - -/* - * Find and possibly create the queue for a MULTIQUEUE scheduler. - * We never call it for !MULTIQUEUE (the queue is in the sch_inst). - */ -struct dn_queue * -ipdn_q_find(struct dn_fsk *fs, struct dn_sch_inst *si, - struct ipfw_flow_id *id) -{ - struct dn_queue template; - - template._si = si; - template.fs = fs; - - if (fs->fs.flags & DN_QHT_HASH) { - struct ipfw_flow_id masked_id; - if (fs->qht == NULL) { - fs->qht = dn_ht_init(NULL, fs->fs.buckets, - offsetof(struct dn_queue, q_next), - q_hash, q_match, q_new); - if (fs->qht == NULL) - return NULL; - } - masked_id = *id; - flow_id_mask(&fs->fsk_mask, &masked_id); - return dn_ht_find(fs->qht, (uintptr_t)&masked_id, - DNHT_INSERT, &template); - } else { - if (fs->qht == NULL) - fs->qht = q_new(0, 0, &template); - return (struct dn_queue *)fs->qht; - } -} -/*--- end of queue hash table ---*/ - -/*--- support functions for the sch_inst hashtable ---- - * - * These are hashed by flow-id - */ -static uint32_t -si_hash(uintptr_t key, int flags, void *arg) -{ - /* compute the hash slot from the flow id */ - struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ? - &((struct dn_sch_inst *)key)->ni.fid : - (struct ipfw_flow_id *)key; - - return flow_id_hash(id); -} - -static int -si_match(void *obj, uintptr_t key, int flags, void *arg) -{ - struct dn_sch_inst *o = obj; - struct ipfw_flow_id *id2; - - id2 = (flags & DNHT_KEY_IS_OBJ) ? - &((struct dn_sch_inst *)key)->ni.fid : - (struct ipfw_flow_id *)key; - return flow_id_cmp(&o->ni.fid, id2) == 0; -} - -/* - * create a new instance for the given 'key' - * Allocate memory for instance, delay line and scheduler private data. - */ -static void * -si_new(uintptr_t key, int flags, void *arg) -{ - struct dn_schk *s = arg; - struct dn_sch_inst *si; - int l = sizeof(*si) + s->fp->si_datalen; - - si = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO); - if (si == NULL) - goto error; - - /* Set length only for the part passed up to userland. */ - set_oid(&si->ni.oid, DN_SCH_I, sizeof(struct dn_flow)); - set_oid(&(si->dline.oid), DN_DELAY_LINE, - sizeof(struct delay_line)); - /* mark si and dline as outside the event queue */ - si->ni.oid.id = si->dline.oid.id = -1; - - si->sched = s; - si->dline.si = si; - - if (s->fp->new_sched && s->fp->new_sched(si)) { - D("new_sched error"); - goto error; - } - if (s->sch.flags & DN_HAVE_MASK) - si->ni.fid = *(struct ipfw_flow_id *)key; - - dn_cfg.si_count++; - return si; - -error: - if (si) { - bzero(si, sizeof(*si)); // safety - free(si, M_DUMMYNET); - } - return NULL; -} - -/* - * Callback from siht to delete all scheduler instances. Remove - * si and delay line from the system heap, destroy all queues. - * We assume that all flowset have been notified and do not - * point to us anymore. - */ -static int -si_destroy(void *_si, void *arg) -{ - struct dn_sch_inst *si = _si; - struct dn_schk *s = si->sched; - struct delay_line *dl = &si->dline; - - if (dl->oid.subtype) /* remove delay line from event heap */ - heap_extract(&dn_cfg.evheap, dl); - dn_free_pkts(dl->mq.head); /* drain delay line */ - if (si->kflags & DN_ACTIVE) /* remove si from event heap */ - heap_extract(&dn_cfg.evheap, si); - if (s->fp->free_sched) - s->fp->free_sched(si); - bzero(si, sizeof(*si)); /* safety */ - free(si, M_DUMMYNET); - dn_cfg.si_count--; - return DNHT_SCAN_DEL; -} - -/* - * Find the scheduler instance for this packet. If we need to apply - * a mask, do on a local copy of the flow_id to preserve the original. - * Assume siht is always initialized if we have a mask. - */ -struct dn_sch_inst * -ipdn_si_find(struct dn_schk *s, struct ipfw_flow_id *id) -{ - - if (s->sch.flags & DN_HAVE_MASK) { - struct ipfw_flow_id id_t = *id; - flow_id_mask(&s->sch.sched_mask, &id_t); - return dn_ht_find(s->siht, (uintptr_t)&id_t, - DNHT_INSERT, s); - } - if (!s->siht) - s->siht = si_new(0, 0, s); - return (struct dn_sch_inst *)s->siht; -} - -/* callback to flush credit for the scheduler instance */ -static int -si_reset_credit(void *_si, void *arg) -{ - struct dn_sch_inst *si = _si; - struct dn_link *p = &si->sched->link; - - si->credit = p->burst + (dn_cfg.io_fast ? p->bandwidth : 0); - return 0; -} - -static void -schk_reset_credit(struct dn_schk *s) -{ - if (s->sch.flags & DN_HAVE_MASK) - dn_ht_scan(s->siht, si_reset_credit, NULL); - else if (s->siht) - si_reset_credit(s->siht, NULL); -} -/*---- end of sch_inst hashtable ---------------------*/ - -/*------------------------------------------------------- - * flowset hash (fshash) support. Entries are hashed by fs_nr. - * New allocations are put in the fsunlinked list, from which - * they are removed when they point to a specific scheduler. - */ -static uint32_t -fsk_hash(uintptr_t key, int flags, void *arg) -{ - uint32_t i = !(flags & DNHT_KEY_IS_OBJ) ? key : - ((struct dn_fsk *)key)->fs.fs_nr; - - return ( (i>>8)^(i>>4)^i ); -} - -static int -fsk_match(void *obj, uintptr_t key, int flags, void *arg) -{ - struct dn_fsk *fs = obj; - int i = !(flags & DNHT_KEY_IS_OBJ) ? key : - ((struct dn_fsk *)key)->fs.fs_nr; - - return (fs->fs.fs_nr == i); -} - -static void * -fsk_new(uintptr_t key, int flags, void *arg) -{ - struct dn_fsk *fs; - - fs = malloc(sizeof(*fs), M_DUMMYNET, M_NOWAIT | M_ZERO); - if (fs) { - set_oid(&fs->fs.oid, DN_FS, sizeof(fs->fs)); - dn_cfg.fsk_count++; - fs->drain_bucket = 0; - SLIST_INSERT_HEAD(&dn_cfg.fsu, fs, sch_chain); - } - return fs; -} - -/* - * detach flowset from its current scheduler. Flags as follows: - * DN_DETACH removes from the fsk_list - * DN_DESTROY deletes individual queues - * DN_DELETE_FS destroys the flowset (otherwise goes in unlinked). - */ -static void -fsk_detach(struct dn_fsk *fs, int flags) -{ - if (flags & DN_DELETE_FS) - flags |= DN_DESTROY; - ND("fs %d from sched %d flags %s %s %s", - fs->fs.fs_nr, fs->fs.sched_nr, - (flags & DN_DELETE_FS) ? "DEL_FS":"", - (flags & DN_DESTROY) ? "DEL":"", - (flags & DN_DETACH) ? "DET":""); - if (flags & DN_DETACH) { /* detach from the list */ - struct dn_fsk_head *h; - h = fs->sched ? &fs->sched->fsk_list : &dn_cfg.fsu; - SLIST_REMOVE(h, fs, dn_fsk, sch_chain); - } - /* Free the RED parameters, they will be recomputed on - * subsequent attach if needed. - */ - if (fs->w_q_lookup) - free(fs->w_q_lookup, M_DUMMYNET); - fs->w_q_lookup = NULL; - qht_delete(fs, flags); - if (fs->sched && fs->sched->fp->free_fsk) - fs->sched->fp->free_fsk(fs); - fs->sched = NULL; - if (flags & DN_DELETE_FS) { - bzero(fs, sizeof(fs)); /* safety */ - free(fs, M_DUMMYNET); - dn_cfg.fsk_count--; - } else { - SLIST_INSERT_HEAD(&dn_cfg.fsu, fs, sch_chain); - } -} - -/* - * Detach or destroy all flowsets in a list. - * flags specifies what to do: - * DN_DESTROY: flush all queues - * DN_DELETE_FS: DN_DESTROY + destroy flowset - * DN_DELETE_FS implies DN_DESTROY - */ -static void -fsk_detach_list(struct dn_fsk_head *h, int flags) -{ - struct dn_fsk *fs; - int n = 0; /* only for stats */ - - ND("head %p flags %x", h, flags); - while ((fs = SLIST_FIRST(h))) { - SLIST_REMOVE_HEAD(h, sch_chain); - n++; - fsk_detach(fs, flags); - } - ND("done %d flowsets", n); -} - -/* - * called on 'queue X delete' -- removes the flowset from fshash, - * deletes all queues for the flowset, and removes the flowset. - */ -static int -delete_fs(int i, int locked) -{ - struct dn_fsk *fs; - int err = 0; - - if (!locked) - DN_BH_WLOCK(); - fs = dn_ht_find(dn_cfg.fshash, i, DNHT_REMOVE, NULL); - ND("fs %d found %p", i, fs); - if (fs) { - fsk_detach(fs, DN_DETACH | DN_DELETE_FS); - err = 0; - } else - err = EINVAL; - if (!locked) - DN_BH_WUNLOCK(); - return err; -} - -/*----- end of flowset hashtable support -------------*/ - -/*------------------------------------------------------------ - * Scheduler hash. When searching by index we pass sched_nr, - * otherwise we pass struct dn_sch * which is the first field in - * struct dn_schk so we can cast between the two. We use this trick - * because in the create phase (but it should be fixed). - */ -static uint32_t -schk_hash(uintptr_t key, int flags, void *_arg) -{ - uint32_t i = !(flags & DNHT_KEY_IS_OBJ) ? key : - ((struct dn_schk *)key)->sch.sched_nr; - return ( (i>>8)^(i>>4)^i ); -} - -static int -schk_match(void *obj, uintptr_t key, int flags, void *_arg) -{ - struct dn_schk *s = (struct dn_schk *)obj; - int i = !(flags & DNHT_KEY_IS_OBJ) ? key : - ((struct dn_schk *)key)->sch.sched_nr; - return (s->sch.sched_nr == i); -} - -/* - * Create the entry and intialize with the sched hash if needed. - * Leave s->fp unset so we can tell whether a dn_ht_find() returns - * a new object or a previously existing one. - */ -static void * -schk_new(uintptr_t key, int flags, void *arg) -{ - struct schk_new_arg *a = arg; - struct dn_schk *s; - int l = sizeof(*s) +a->fp->schk_datalen; - - s = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO); - if (s == NULL) - return NULL; - set_oid(&s->link.oid, DN_LINK, sizeof(s->link)); - s->sch = *a->sch; // copy initial values - s->link.link_nr = s->sch.sched_nr; - SLIST_INIT(&s->fsk_list); - /* initialize the hash table or create the single instance */ - s->fp = a->fp; /* si_new needs this */ - s->drain_bucket = 0; - if (s->sch.flags & DN_HAVE_MASK) { - s->siht = dn_ht_init(NULL, s->sch.buckets, - offsetof(struct dn_sch_inst, si_next), - si_hash, si_match, si_new); - if (s->siht == NULL) { - free(s, M_DUMMYNET); - return NULL; - } - } - s->fp = NULL; /* mark as a new scheduler */ - dn_cfg.schk_count++; - return s; -} - -/* - * Callback for sched delete. Notify all attached flowsets to - * detach from the scheduler, destroy the internal flowset, and - * all instances. The scheduler goes away too. - * arg is 0 (only detach flowsets and destroy instances) - * DN_DESTROY (detach & delete queues, delete schk) - * or DN_DELETE_FS (delete queues and flowsets, delete schk) - */ -static int -schk_delete_cb(void *obj, void *arg) -{ - struct dn_schk *s = obj; -#if 0 - int a = (int)arg; - ND("sched %d arg %s%s", - s->sch.sched_nr, - a&DN_DESTROY ? "DEL ":"", - a&DN_DELETE_FS ? "DEL_FS":""); -#endif - fsk_detach_list(&s->fsk_list, arg ? DN_DESTROY : 0); - /* no more flowset pointing to us now */ - if (s->sch.flags & DN_HAVE_MASK) { - dn_ht_scan(s->siht, si_destroy, NULL); - dn_ht_free(s->siht, 0); - } else if (s->siht) - si_destroy(s->siht, NULL); - if (s->profile) { - free(s->profile, M_DUMMYNET); - s->profile = NULL; - } - s->siht = NULL; - if (s->fp->destroy) - s->fp->destroy(s); - bzero(s, sizeof(*s)); // safety - free(obj, M_DUMMYNET); - dn_cfg.schk_count--; - return DNHT_SCAN_DEL; -} - -/* - * called on a 'sched X delete' command. Deletes a single scheduler. - * This is done by removing from the schedhash, unlinking all - * flowsets and deleting their traffic. - */ -static int -delete_schk(int i) -{ - struct dn_schk *s; - - s = dn_ht_find(dn_cfg.schedhash, i, DNHT_REMOVE, NULL); - ND("%d %p", i, s); - if (!s) - return EINVAL; - delete_fs(i + DN_MAX_ID, 1); /* first delete internal fs */ - /* then detach flowsets, delete traffic */ - schk_delete_cb(s, (void*)(uintptr_t)DN_DESTROY); - return 0; -} -/*--- end of schk hashtable support ---*/ - -static int -copy_obj(char **start, char *end, void *_o, const char *msg, int i) -{ - struct dn_id *o = _o; - int have = end - *start; - - if (have < o->len || o->len == 0 || o->type == 0) { - D("(WARN) type %d %s %d have %d need %d", - o->type, msg, i, have, o->len); - return 1; - } - ND("type %d %s %d len %d", o->type, msg, i, o->len); - bcopy(_o, *start, o->len); - if (o->type == DN_LINK) { - /* Adjust burst parameter for link */ - struct dn_link *l = (struct dn_link *)*start; - l->burst = div64(l->burst, 8 * hz); - l->delay = l->delay * 1000 / hz; - } else if (o->type == DN_SCH) { - /* Set id->id to the number of instances */ - struct dn_schk *s = _o; - struct dn_id *id = (struct dn_id *)(*start); - id->id = (s->sch.flags & DN_HAVE_MASK) ? - dn_ht_entries(s->siht) : (s->siht ? 1 : 0); - } - *start += o->len; - return 0; -} - -/* Specific function to copy a queue. - * Copies only the user-visible part of a queue (which is in - * a struct dn_flow), and sets len accordingly. - */ -static int -copy_obj_q(char **start, char *end, void *_o, const char *msg, int i) -{ - struct dn_id *o = _o; - int have = end - *start; - int len = sizeof(struct dn_flow); /* see above comment */ - - if (have < len || o->len == 0 || o->type != DN_QUEUE) { - D("ERROR type %d %s %d have %d need %d", - o->type, msg, i, have, len); - return 1; - } - ND("type %d %s %d len %d", o->type, msg, i, len); - bcopy(_o, *start, len); - ((struct dn_id*)(*start))->len = len; - *start += len; - return 0; -} - -static int -copy_q_cb(void *obj, void *arg) -{ - struct dn_queue *q = obj; - struct copy_args *a = arg; - struct dn_flow *ni = (struct dn_flow *)(*a->start); - if (copy_obj_q(a->start, a->end, &q->ni, "queue", -1)) - return DNHT_SCAN_END; - ni->oid.type = DN_FLOW; /* override the DN_QUEUE */ - ni->oid.id = si_hash((uintptr_t)&ni->fid, 0, NULL); - return 0; -} - -static int -copy_q(struct copy_args *a, struct dn_fsk *fs, int flags) -{ - if (!fs->qht) - return 0; - if (fs->fs.flags & DN_QHT_HASH) - dn_ht_scan(fs->qht, copy_q_cb, a); - else - copy_q_cb(fs->qht, a); - return 0; -} - -/* - * This routine only copies the initial part of a profile ? XXX - */ -static int -copy_profile(struct copy_args *a, struct dn_profile *p) -{ - int have = a->end - *a->start; - /* XXX here we check for max length */ - int profile_len = sizeof(struct dn_profile) - - ED_MAX_SAMPLES_NO*sizeof(int); - - if (p == NULL) - return 0; - if (have < profile_len) { - D("error have %d need %d", have, profile_len); - return 1; - } - bcopy(p, *a->start, profile_len); - ((struct dn_id *)(*a->start))->len = profile_len; - *a->start += profile_len; - return 0; -} - -static int -copy_flowset(struct copy_args *a, struct dn_fsk *fs, int flags) -{ - struct dn_fs *ufs = (struct dn_fs *)(*a->start); - if (!fs) - return 0; - ND("flowset %d", fs->fs.fs_nr); - if (copy_obj(a->start, a->end, &fs->fs, "flowset", fs->fs.fs_nr)) - return DNHT_SCAN_END; - ufs->oid.id = (fs->fs.flags & DN_QHT_HASH) ? - dn_ht_entries(fs->qht) : (fs->qht ? 1 : 0); - if (flags) { /* copy queues */ - copy_q(a, fs, 0); - } - return 0; -} - -static int -copy_si_cb(void *obj, void *arg) -{ - struct dn_sch_inst *si = obj; - struct copy_args *a = arg; - struct dn_flow *ni = (struct dn_flow *)(*a->start); - if (copy_obj(a->start, a->end, &si->ni, "inst", - si->sched->sch.sched_nr)) - return DNHT_SCAN_END; - ni->oid.type = DN_FLOW; /* override the DN_SCH_I */ - ni->oid.id = si_hash((uintptr_t)si, DNHT_KEY_IS_OBJ, NULL); - return 0; -} - -static int -copy_si(struct copy_args *a, struct dn_schk *s, int flags) -{ - if (s->sch.flags & DN_HAVE_MASK) - dn_ht_scan(s->siht, copy_si_cb, a); - else if (s->siht) - copy_si_cb(s->siht, a); - return 0; -} - -/* - * compute a list of children of a scheduler and copy up - */ -static int -copy_fsk_list(struct copy_args *a, struct dn_schk *s, int flags) -{ - struct dn_fsk *fs; - struct dn_id *o; - uint32_t *p; - - int n = 0, space = sizeof(*o); - SLIST_FOREACH(fs, &s->fsk_list, sch_chain) { - if (fs->fs.fs_nr < DN_MAX_ID) - n++; - } - space += n * sizeof(uint32_t); - DX(3, "sched %d has %d flowsets", s->sch.sched_nr, n); - if (a->end - *(a->start) < space) - return DNHT_SCAN_END; - o = (struct dn_id *)(*(a->start)); - o->len = space; - *a->start += o->len; - o->type = DN_TEXT; - p = (uint32_t *)(o+1); - SLIST_FOREACH(fs, &s->fsk_list, sch_chain) - if (fs->fs.fs_nr < DN_MAX_ID) - *p++ = fs->fs.fs_nr; - return 0; -} - -static int -copy_data_helper(void *_o, void *_arg) -{ - struct copy_args *a = _arg; - uint32_t *r = a->extra->r; /* start of first range */ - uint32_t *lim; /* first invalid pointer */ - int n; - - lim = (uint32_t *)((char *)(a->extra) + a->extra->o.len); - - if (a->type == DN_LINK || a->type == DN_SCH) { - /* pipe|sched show, we receive a dn_schk */ - struct dn_schk *s = _o; - - n = s->sch.sched_nr; - if (a->type == DN_SCH && n >= DN_MAX_ID) - return 0; /* not a scheduler */ - if (a->type == DN_LINK && n <= DN_MAX_ID) - return 0; /* not a pipe */ - - /* see if the object is within one of our ranges */ - for (;r < lim; r += 2) { - if (n < r[0] || n > r[1]) - continue; - /* Found a valid entry, copy and we are done */ - if (a->flags & DN_C_LINK) { - if (copy_obj(a->start, a->end, - &s->link, "link", n)) - return DNHT_SCAN_END; - if (copy_profile(a, s->profile)) - return DNHT_SCAN_END; - if (copy_flowset(a, s->fs, 0)) - return DNHT_SCAN_END; - } - if (a->flags & DN_C_SCH) { - if (copy_obj(a->start, a->end, - &s->sch, "sched", n)) - return DNHT_SCAN_END; - /* list all attached flowsets */ - if (copy_fsk_list(a, s, 0)) - return DNHT_SCAN_END; - } - if (a->flags & DN_C_FLOW) - copy_si(a, s, 0); - break; - } - } else if (a->type == DN_FS) { - /* queue show, skip internal flowsets */ - struct dn_fsk *fs = _o; - - n = fs->fs.fs_nr; - if (n >= DN_MAX_ID) - return 0; - /* see if the object is within one of our ranges */ - for (;r < lim; r += 2) { - if (n < r[0] || n > r[1]) - continue; - if (copy_flowset(a, fs, 0)) - return DNHT_SCAN_END; - copy_q(a, fs, 0); - break; /* we are done */ - } - } - return 0; -} - -static inline struct dn_schk * -locate_scheduler(int i) -{ - return dn_ht_find(dn_cfg.schedhash, i, 0, NULL); -} - -/* - * red parameters are in fixed point arithmetic. - */ -static int -config_red(struct dn_fsk *fs) -{ - int64_t s, idle, weight, w0; - int t, i; - - fs->w_q = fs->fs.w_q; - fs->max_p = fs->fs.max_p; - ND("called"); - /* Doing stuff that was in userland */ - i = fs->sched->link.bandwidth; - s = (i <= 0) ? 0 : - hz * dn_cfg.red_avg_pkt_size * 8 * SCALE(1) / i; - - idle = div64((s * 3) , fs->w_q); /* s, fs->w_q scaled; idle not scaled */ - fs->lookup_step = div64(idle , dn_cfg.red_lookup_depth); - /* fs->lookup_step not scaled, */ - if (!fs->lookup_step) - fs->lookup_step = 1; - w0 = weight = SCALE(1) - fs->w_q; //fs->w_q scaled - - for (t = fs->lookup_step; t > 1; --t) - weight = SCALE_MUL(weight, w0); - fs->lookup_weight = (int)(weight); // scaled - - /* Now doing stuff that was in kerneland */ - fs->min_th = SCALE(fs->fs.min_th); - fs->max_th = SCALE(fs->fs.max_th); - - fs->c_1 = fs->max_p / (fs->fs.max_th - fs->fs.min_th); - fs->c_2 = SCALE_MUL(fs->c_1, SCALE(fs->fs.min_th)); - - if (fs->fs.flags & DN_IS_GENTLE_RED) { - fs->c_3 = (SCALE(1) - fs->max_p) / fs->fs.max_th; - fs->c_4 = SCALE(1) - 2 * fs->max_p; - } - - /* If the lookup table already exist, free and create it again. */ - if (fs->w_q_lookup) { - free(fs->w_q_lookup, M_DUMMYNET); - fs->w_q_lookup = NULL; - } - if (dn_cfg.red_lookup_depth == 0) { - printf("\ndummynet: net.inet.ip.dummynet.red_lookup_depth" - "must be > 0\n"); - fs->fs.flags &= ~DN_IS_RED; - fs->fs.flags &= ~DN_IS_GENTLE_RED; - return (EINVAL); - } - fs->lookup_depth = dn_cfg.red_lookup_depth; - fs->w_q_lookup = (u_int *)malloc(fs->lookup_depth * sizeof(int), - M_DUMMYNET, M_NOWAIT); - if (fs->w_q_lookup == NULL) { - printf("dummynet: sorry, cannot allocate red lookup table\n"); - fs->fs.flags &= ~DN_IS_RED; - fs->fs.flags &= ~DN_IS_GENTLE_RED; - return(ENOSPC); - } - - /* Fill the lookup table with (1 - w_q)^x */ - fs->w_q_lookup[0] = SCALE(1) - fs->w_q; - - for (i = 1; i < fs->lookup_depth; i++) - fs->w_q_lookup[i] = - SCALE_MUL(fs->w_q_lookup[i - 1], fs->lookup_weight); - - if (dn_cfg.red_avg_pkt_size < 1) - dn_cfg.red_avg_pkt_size = 512; - fs->avg_pkt_size = dn_cfg.red_avg_pkt_size; - if (dn_cfg.red_max_pkt_size < 1) - dn_cfg.red_max_pkt_size = 1500; - fs->max_pkt_size = dn_cfg.red_max_pkt_size; - ND("exit"); - return 0; -} - -/* Scan all flowset attached to this scheduler and update red */ -static void -update_red(struct dn_schk *s) -{ - struct dn_fsk *fs; - SLIST_FOREACH(fs, &s->fsk_list, sch_chain) { - if (fs && (fs->fs.flags & DN_IS_RED)) - config_red(fs); - } -} - -/* attach flowset to scheduler s, possibly requeue */ -static void -fsk_attach(struct dn_fsk *fs, struct dn_schk *s) -{ - ND("remove fs %d from fsunlinked, link to sched %d", - fs->fs.fs_nr, s->sch.sched_nr); - SLIST_REMOVE(&dn_cfg.fsu, fs, dn_fsk, sch_chain); - fs->sched = s; - SLIST_INSERT_HEAD(&s->fsk_list, fs, sch_chain); - if (s->fp->new_fsk) - s->fp->new_fsk(fs); - /* XXX compute fsk_mask */ - fs->fsk_mask = fs->fs.flow_mask; - if (fs->sched->sch.flags & DN_HAVE_MASK) - flow_id_or(&fs->sched->sch.sched_mask, &fs->fsk_mask); - if (fs->qht) { - /* - * we must drain qht according to the old - * type, and reinsert according to the new one. - * The requeue is complex -- in general we need to - * reclassify every single packet. - * For the time being, let's hope qht is never set - * when we reach this point. - */ - D("XXX TODO requeue from fs %d to sch %d", - fs->fs.fs_nr, s->sch.sched_nr); - fs->qht = NULL; - } - /* set the new type for qht */ - if (nonzero_mask(&fs->fsk_mask)) - fs->fs.flags |= DN_QHT_HASH; - else - fs->fs.flags &= ~DN_QHT_HASH; - - /* XXX config_red() can fail... */ - if (fs->fs.flags & DN_IS_RED) - config_red(fs); -} - -/* update all flowsets which may refer to this scheduler */ -static void -update_fs(struct dn_schk *s) -{ - struct dn_fsk *fs, *tmp; - - SLIST_FOREACH_SAFE(fs, &dn_cfg.fsu, sch_chain, tmp) { - if (s->sch.sched_nr != fs->fs.sched_nr) { - D("fs %d for sch %d not %d still unlinked", - fs->fs.fs_nr, fs->fs.sched_nr, - s->sch.sched_nr); - continue; - } - fsk_attach(fs, s); - } -} - -/* - * Configuration -- to preserve backward compatibility we use - * the following scheme (N is 65536) - * NUMBER SCHED LINK FLOWSET - * 1 .. N-1 (1)WFQ (2)WFQ (3)queue - * N+1 .. 2N-1 (4)FIFO (5)FIFO (6)FIFO for sched 1..N-1 - * 2N+1 .. 3N-1 -- -- (7)FIFO for sched N+1..2N-1 - * - * "pipe i config" configures #1, #2 and #3 - * "sched i config" configures #1 and possibly #6 - * "queue i config" configures #3 - * #1 is configured with 'pipe i config' or 'sched i config' - * #2 is configured with 'pipe i config', and created if not - * existing with 'sched i config' - * #3 is configured with 'queue i config' - * #4 is automatically configured after #1, can only be FIFO - * #5 is automatically configured after #2 - * #6 is automatically created when #1 is !MULTIQUEUE, - * and can be updated. - * #7 is automatically configured after #2 - */ - -/* - * configure a link (and its FIFO instance) - */ -static int -config_link(struct dn_link *p, struct dn_id *arg) -{ - int i; - - if (p->oid.len != sizeof(*p)) { - D("invalid pipe len %d", p->oid.len); - return EINVAL; - } - i = p->link_nr; - if (i <= 0 || i >= DN_MAX_ID) - return EINVAL; - /* - * The config program passes parameters as follows: - * bw = bits/second (0 means no limits), - * delay = ms, must be translated into ticks. - * qsize = slots/bytes - * burst ??? - */ - p->delay = (p->delay * hz) / 1000; - /* Scale burst size: bytes -> bits * hz */ - p->burst *= 8 * hz; - - DN_BH_WLOCK(); - /* do it twice, base link and FIFO link */ - for (; i < 2*DN_MAX_ID; i += DN_MAX_ID) { - struct dn_schk *s = locate_scheduler(i); - if (s == NULL) { - DN_BH_WUNLOCK(); - D("sched %d not found", i); - return EINVAL; - } - /* remove profile if exists */ - if (s->profile) { - free(s->profile, M_DUMMYNET); - s->profile = NULL; - } - /* copy all parameters */ - s->link.oid = p->oid; - s->link.link_nr = i; - s->link.delay = p->delay; - if (s->link.bandwidth != p->bandwidth) { - /* XXX bandwidth changes, need to update red params */ - s->link.bandwidth = p->bandwidth; - update_red(s); - } - s->link.burst = p->burst; - schk_reset_credit(s); - } - dn_cfg.id++; - DN_BH_WUNLOCK(); - return 0; -} - -/* - * configure a flowset. Can be called from inside with locked=1, - */ -static struct dn_fsk * -config_fs(struct dn_fs *nfs, struct dn_id *arg, int locked) -{ - int i; - struct dn_fsk *fs; - - if (nfs->oid.len != sizeof(*nfs)) { - D("invalid flowset len %d", nfs->oid.len); - return NULL; - } - i = nfs->fs_nr; - if (i <= 0 || i >= 3*DN_MAX_ID) - return NULL; - ND("flowset %d", i); - /* XXX other sanity checks */ - if (nfs->flags & DN_QSIZE_BYTES) { - ipdn_bound_var(&nfs->qsize, 16384, - 1500, dn_cfg.byte_limit, NULL); // "queue byte size"); - } else { - ipdn_bound_var(&nfs->qsize, 50, - 1, dn_cfg.slot_limit, NULL); // "queue slot size"); - } - if (nfs->flags & DN_HAVE_MASK) { - /* make sure we have some buckets */ - ipdn_bound_var((int *)&nfs->buckets, dn_cfg.hash_size, - 1, dn_cfg.max_hash_size, "flowset buckets"); - } else { - nfs->buckets = 1; /* we only need 1 */ - } - if (!locked) - DN_BH_WLOCK(); - do { /* exit with break when done */ - struct dn_schk *s; - int flags = nfs->sched_nr ? DNHT_INSERT : 0; - int j; - int oldc = dn_cfg.fsk_count; - fs = dn_ht_find(dn_cfg.fshash, i, flags, NULL); - if (fs == NULL) { - D("missing sched for flowset %d", i); - break; - } - /* grab some defaults from the existing one */ - if (nfs->sched_nr == 0) /* reuse */ - nfs->sched_nr = fs->fs.sched_nr; - for (j = 0; j < sizeof(nfs->par)/sizeof(nfs->par[0]); j++) { - if (nfs->par[j] == -1) /* reuse */ - nfs->par[j] = fs->fs.par[j]; - } - if (bcmp(&fs->fs, nfs, sizeof(*nfs)) == 0) { - ND("flowset %d unchanged", i); - break; /* no change, nothing to do */ - } - if (oldc != dn_cfg.fsk_count) /* new item */ - dn_cfg.id++; - s = locate_scheduler(nfs->sched_nr); - /* detach from old scheduler if needed, preserving - * queues if we need to reattach. Then update the - * configuration, and possibly attach to the new sched. - */ - DX(2, "fs %d changed sched %d@%p to %d@%p", - fs->fs.fs_nr, - fs->fs.sched_nr, fs->sched, nfs->sched_nr, s); - if (fs->sched) { - int flags = s ? DN_DETACH : (DN_DETACH | DN_DESTROY); - flags |= DN_DESTROY; /* XXX temporary */ - fsk_detach(fs, flags); - } - fs->fs = *nfs; /* copy configuration */ - if (s != NULL) - fsk_attach(fs, s); - } while (0); - if (!locked) - DN_BH_WUNLOCK(); - return fs; -} - -/* - * config/reconfig a scheduler and its FIFO variant. - * For !MULTIQUEUE schedulers, also set up the flowset. - * - * On reconfigurations (detected because s->fp is set), - * detach existing flowsets preserving traffic, preserve link, - * and delete the old scheduler creating a new one. - */ -static int -config_sched(struct dn_sch *_nsch, struct dn_id *arg) -{ - struct dn_schk *s; - struct schk_new_arg a; /* argument for schk_new */ - int i; - struct dn_link p; /* copy of oldlink */ - struct dn_profile *pf = NULL; /* copy of old link profile */ - /* Used to preserv mask parameter */ - struct ipfw_flow_id new_mask; - int new_buckets = 0; - int new_flags = 0; - int pipe_cmd; - int err = ENOMEM; - - a.sch = _nsch; - if (a.sch->oid.len != sizeof(*a.sch)) { - D("bad sched len %d", a.sch->oid.len); - return EINVAL; - } - i = a.sch->sched_nr; - if (i <= 0 || i >= DN_MAX_ID) - return EINVAL; - /* make sure we have some buckets */ - if (a.sch->flags & DN_HAVE_MASK) - ipdn_bound_var((int *)&a.sch->buckets, dn_cfg.hash_size, - 1, dn_cfg.max_hash_size, "sched buckets"); - /* XXX other sanity checks */ - bzero(&p, sizeof(p)); - - pipe_cmd = a.sch->flags & DN_PIPE_CMD; - a.sch->flags &= ~DN_PIPE_CMD; //XXX do it even if is not set? - if (pipe_cmd) { - /* Copy mask parameter */ - new_mask = a.sch->sched_mask; - new_buckets = a.sch->buckets; - new_flags = a.sch->flags; - } - DN_BH_WLOCK(); -again: /* run twice, for wfq and fifo */ - /* - * lookup the type. If not supplied, use the previous one - * or default to WF2Q+. Otherwise, return an error. - */ - dn_cfg.id++; - a.fp = find_sched_type(a.sch->oid.subtype, a.sch->name); - if (a.fp != NULL) { - /* found. Lookup or create entry */ - s = dn_ht_find(dn_cfg.schedhash, i, DNHT_INSERT, &a); - } else if (a.sch->oid.subtype == 0 && !a.sch->name[0]) { - /* No type. search existing s* or retry with WF2Q+ */ - s = dn_ht_find(dn_cfg.schedhash, i, 0, &a); - if (s != NULL) { - a.fp = s->fp; - /* Scheduler exists, skip to FIFO scheduler - * if command was pipe config... - */ - if (pipe_cmd) - goto next; - } else { - /* New scheduler, create a wf2q+ with no mask - * if command was pipe config... - */ - if (pipe_cmd) { - /* clear mask parameter */ - bzero(&a.sch->sched_mask, sizeof(new_mask)); - a.sch->buckets = 0; - a.sch->flags &= ~DN_HAVE_MASK; - } - a.sch->oid.subtype = DN_SCHED_WF2QP; - goto again; - } - } else { - D("invalid scheduler type %d %s", - a.sch->oid.subtype, a.sch->name); - err = EINVAL; - goto error; - } - /* normalize name and subtype */ - a.sch->oid.subtype = a.fp->type; - bzero(a.sch->name, sizeof(a.sch->name)); - strlcpy(a.sch->name, a.fp->name, sizeof(a.sch->name)); - if (s == NULL) { - D("cannot allocate scheduler %d", i); - goto error; - } - /* restore existing link if any */ - if (p.link_nr) { - s->link = p; - if (!pf || pf->link_nr != p.link_nr) { /* no saved value */ - s->profile = NULL; /* XXX maybe not needed */ - } else { - s->profile = malloc(sizeof(struct dn_profile), - M_DUMMYNET, M_NOWAIT | M_ZERO); - if (s->profile == NULL) { - D("cannot allocate profile"); - goto error; //XXX - } - bcopy(pf, s->profile, sizeof(*pf)); - } - } - p.link_nr = 0; - if (s->fp == NULL) { - DX(2, "sched %d new type %s", i, a.fp->name); - } else if (s->fp != a.fp || - bcmp(a.sch, &s->sch, sizeof(*a.sch)) ) { - /* already existing. */ - DX(2, "sched %d type changed from %s to %s", - i, s->fp->name, a.fp->name); - DX(4, " type/sub %d/%d -> %d/%d", - s->sch.oid.type, s->sch.oid.subtype, - a.sch->oid.type, a.sch->oid.subtype); - if (s->link.link_nr == 0) - D("XXX WARNING link 0 for sched %d", i); - p = s->link; /* preserve link */ - if (s->profile) {/* preserve profile */ - if (!pf) - pf = malloc(sizeof(*pf), - M_DUMMYNET, M_NOWAIT | M_ZERO); - if (pf) /* XXX should issue a warning otherwise */ - bcopy(s->profile, pf, sizeof(*pf)); - } - /* remove from the hash */ - dn_ht_find(dn_cfg.schedhash, i, DNHT_REMOVE, NULL); - /* Detach flowsets, preserve queues. */ - // schk_delete_cb(s, NULL); - // XXX temporarily, kill queues - schk_delete_cb(s, (void *)DN_DESTROY); - goto again; - } else { - DX(4, "sched %d unchanged type %s", i, a.fp->name); - } - /* complete initialization */ - s->sch = *a.sch; - s->fp = a.fp; - s->cfg = arg; - // XXX schk_reset_credit(s); - /* create the internal flowset if needed, - * trying to reuse existing ones if available - */ - if (!(s->fp->flags & DN_MULTIQUEUE) && !s->fs) { - s->fs = dn_ht_find(dn_cfg.fshash, i, 0, NULL); - if (!s->fs) { - struct dn_fs fs; - bzero(&fs, sizeof(fs)); - set_oid(&fs.oid, DN_FS, sizeof(fs)); - fs.fs_nr = i + DN_MAX_ID; - fs.sched_nr = i; - s->fs = config_fs(&fs, NULL, 1 /* locked */); - } - if (!s->fs) { - schk_delete_cb(s, (void *)DN_DESTROY); - D("error creating internal fs for %d", i); - goto error; - } - } - /* call init function after the flowset is created */ - if (s->fp->config) - s->fp->config(s); - update_fs(s); -next: - if (i < DN_MAX_ID) { /* now configure the FIFO instance */ - i += DN_MAX_ID; - if (pipe_cmd) { - /* Restore mask parameter for FIFO */ - a.sch->sched_mask = new_mask; - a.sch->buckets = new_buckets; - a.sch->flags = new_flags; - } else { - /* sched config shouldn't modify the FIFO scheduler */ - if (dn_ht_find(dn_cfg.schedhash, i, 0, &a) != NULL) { - /* FIFO already exist, don't touch it */ - err = 0; /* and this is not an error */ - goto error; - } - } - a.sch->sched_nr = i; - a.sch->oid.subtype = DN_SCHED_FIFO; - bzero(a.sch->name, sizeof(a.sch->name)); - goto again; - } - err = 0; -error: - DN_BH_WUNLOCK(); - if (pf) - free(pf, M_DUMMYNET); - return err; -} - -/* - * attach a profile to a link - */ -static int -config_profile(struct dn_profile *pf, struct dn_id *arg) -{ - struct dn_schk *s; - int i, olen, err = 0; - - if (pf->oid.len < sizeof(*pf)) { - D("short profile len %d", pf->oid.len); - return EINVAL; - } - i = pf->link_nr; - if (i <= 0 || i >= DN_MAX_ID) - return EINVAL; - /* XXX other sanity checks */ - DN_BH_WLOCK(); - for (; i < 2*DN_MAX_ID; i += DN_MAX_ID) { - s = locate_scheduler(i); - - if (s == NULL) { - err = EINVAL; - break; - } - dn_cfg.id++; - /* - * If we had a profile and the new one does not fit, - * or it is deleted, then we need to free memory. - */ - if (s->profile && (pf->samples_no == 0 || - s->profile->oid.len < pf->oid.len)) { - free(s->profile, M_DUMMYNET); - s->profile = NULL; - } - if (pf->samples_no == 0) - continue; - /* - * new profile, possibly allocate memory - * and copy data. - */ - if (s->profile == NULL) - s->profile = malloc(pf->oid.len, - M_DUMMYNET, M_NOWAIT | M_ZERO); - if (s->profile == NULL) { - D("no memory for profile %d", i); - err = ENOMEM; - break; - } - /* preserve larger length XXX double check */ - olen = s->profile->oid.len; - if (olen < pf->oid.len) - olen = pf->oid.len; - bcopy(pf, s->profile, pf->oid.len); - s->profile->oid.len = olen; - } - DN_BH_WUNLOCK(); - return err; -} - -/* - * Delete all objects: - */ -static void -dummynet_flush(void) -{ - - /* delete all schedulers and related links/queues/flowsets */ - dn_ht_scan(dn_cfg.schedhash, schk_delete_cb, - (void *)(uintptr_t)DN_DELETE_FS); - /* delete all remaining (unlinked) flowsets */ - DX(4, "still %d unlinked fs", dn_cfg.fsk_count); - dn_ht_free(dn_cfg.fshash, DNHT_REMOVE); - fsk_detach_list(&dn_cfg.fsu, DN_DELETE_FS); - /* Reinitialize system heap... */ - heap_init(&dn_cfg.evheap, 16, offsetof(struct dn_id, id)); -} - -/* - * Main handler for configuration. We are guaranteed to be called - * with an oid which is at least a dn_id. - * - the first object is the command (config, delete, flush, ...) - * - config_link must be issued after the corresponding config_sched - * - parameters (DN_TXT) for an object must preceed the object - * processed on a config_sched. - */ -int -do_config(void *p, int l) -{ - struct dn_id *next, *o; - int err = 0, err2 = 0; - struct dn_id *arg = NULL; - uintptr_t *a; - - o = p; - if (o->id != DN_API_VERSION) { - D("invalid api version got %d need %d", - o->id, DN_API_VERSION); - return EINVAL; - } - for (; l >= sizeof(*o); o = next) { - struct dn_id *prev = arg; - if (o->len < sizeof(*o) || l < o->len) { - D("bad len o->len %d len %d", o->len, l); - err = EINVAL; - break; - } - l -= o->len; - next = (struct dn_id *)((char *)o + o->len); - err = 0; - switch (o->type) { - default: - D("cmd %d not implemented", o->type); - break; - -#ifdef EMULATE_SYSCTL - /* sysctl emulation. - * if we recognize the command, jump to the correct - * handler and return - */ - case DN_SYSCTL_SET: - err = kesysctl_emu_set(p, l); - return err; -#endif - - case DN_CMD_CONFIG: /* simply a header */ - break; - - case DN_CMD_DELETE: - /* the argument is in the first uintptr_t after o */ - a = (uintptr_t *)(o+1); - if (o->len < sizeof(*o) + sizeof(*a)) { - err = EINVAL; - break; - } - switch (o->subtype) { - case DN_LINK: - /* delete base and derived schedulers */ - DN_BH_WLOCK(); - err = delete_schk(*a); - err2 = delete_schk(*a + DN_MAX_ID); - DN_BH_WUNLOCK(); - if (!err) - err = err2; - break; - - default: - D("invalid delete type %d", - o->subtype); - err = EINVAL; - break; - - case DN_FS: - err = (*a <1 || *a >= DN_MAX_ID) ? - EINVAL : delete_fs(*a, 0) ; - break; - } - break; - - case DN_CMD_FLUSH: - DN_BH_WLOCK(); - dummynet_flush(); - DN_BH_WUNLOCK(); - break; - case DN_TEXT: /* store argument the next block */ - prev = NULL; - arg = o; - break; - case DN_LINK: - err = config_link((struct dn_link *)o, arg); - break; - case DN_PROFILE: - err = config_profile((struct dn_profile *)o, arg); - break; - case DN_SCH: - err = config_sched((struct dn_sch *)o, arg); - break; - case DN_FS: - err = (NULL==config_fs((struct dn_fs *)o, arg, 0)); - break; - } - if (prev) - arg = NULL; - if (err != 0) - break; - } - return err; -} - -static int -compute_space(struct dn_id *cmd, struct copy_args *a) -{ - int x = 0, need = 0; - int profile_size = sizeof(struct dn_profile) - - ED_MAX_SAMPLES_NO*sizeof(int); - - /* NOTE about compute space: - * NP = dn_cfg.schk_count - * NSI = dn_cfg.si_count - * NF = dn_cfg.fsk_count - * NQ = dn_cfg.queue_count - * - ipfw pipe show - * (NP/2)*(dn_link + dn_sch + dn_id + dn_fs) only half scheduler - * link, scheduler template, flowset - * integrated in scheduler and header - * for flowset list - * (NSI)*(dn_flow) all scheduler instance (includes - * the queue instance) - * - ipfw sched show - * (NP/2)*(dn_link + dn_sch + dn_id + dn_fs) only half scheduler - * link, scheduler template, flowset - * integrated in scheduler and header - * for flowset list - * (NSI * dn_flow) all scheduler instances - * (NF * sizeof(uint_32)) space for flowset list linked to scheduler - * (NQ * dn_queue) all queue [XXXfor now not listed] - * - ipfw queue show - * (NF * dn_fs) all flowset - * (NQ * dn_queue) all queues - */ - switch (cmd->subtype) { - default: - return -1; - /* XXX where do LINK and SCH differ ? */ - /* 'ipfw sched show' could list all queues associated to - * a scheduler. This feature for now is disabled - */ - case DN_LINK: /* pipe show */ - x = DN_C_LINK | DN_C_SCH | DN_C_FLOW; - need += dn_cfg.schk_count * - (sizeof(struct dn_fs) + profile_size) / 2; - need += dn_cfg.fsk_count * sizeof(uint32_t); - break; - case DN_SCH: /* sched show */ - need += dn_cfg.schk_count * - (sizeof(struct dn_fs) + profile_size) / 2; - need += dn_cfg.fsk_count * sizeof(uint32_t); - x = DN_C_SCH | DN_C_LINK | DN_C_FLOW; - break; - case DN_FS: /* queue show */ - x = DN_C_FS | DN_C_QUEUE; - break; - case DN_GET_COMPAT: /* compatibility mode */ - need = dn_compat_calc_size(); - break; - } - a->flags = x; - if (x & DN_C_SCH) { - need += dn_cfg.schk_count * sizeof(struct dn_sch) / 2; - /* NOT also, each fs might be attached to a sched */ - need += dn_cfg.schk_count * sizeof(struct dn_id) / 2; - } - if (x & DN_C_FS) - need += dn_cfg.fsk_count * sizeof(struct dn_fs); - if (x & DN_C_LINK) { - need += dn_cfg.schk_count * sizeof(struct dn_link) / 2; - } - /* - * When exporting a queue to userland, only pass up the - * struct dn_flow, which is the only visible part. - */ - - if (x & DN_C_QUEUE) - need += dn_cfg.queue_count * sizeof(struct dn_flow); - if (x & DN_C_FLOW) - need += dn_cfg.si_count * (sizeof(struct dn_flow)); - return need; -} - -/* - * If compat != NULL dummynet_get is called in compatibility mode. - * *compat will be the pointer to the buffer to pass to ipfw - */ -int -dummynet_get(struct sockopt *sopt, void **compat) -{ - int have, i, need, error; - char *start = NULL, *buf; - size_t sopt_valsize; - struct dn_id *cmd; - struct copy_args a; - struct copy_range r; - int l = sizeof(struct dn_id); - - bzero(&a, sizeof(a)); - bzero(&r, sizeof(r)); - - /* save and restore original sopt_valsize around copyin */ - sopt_valsize = sopt->sopt_valsize; - - cmd = &r.o; - - if (!compat) { - /* copy at least an oid, and possibly a full object */ - error = sooptcopyin(sopt, cmd, sizeof(r), sizeof(*cmd)); - sopt->sopt_valsize = sopt_valsize; - if (error) - goto done; - l = cmd->len; -#ifdef EMULATE_SYSCTL - /* sysctl emulation. */ - if (cmd->type == DN_SYSCTL_GET) - return kesysctl_emu_get(sopt); -#endif - if (l > sizeof(r)) { - /* request larger than default, allocate buffer */ - cmd = malloc(l, M_DUMMYNET, M_WAITOK); - error = sooptcopyin(sopt, cmd, l, l); - sopt->sopt_valsize = sopt_valsize; - if (error) - goto done; - } - } else { /* compatibility */ - error = 0; - cmd->type = DN_CMD_GET; - cmd->len = sizeof(struct dn_id); - cmd->subtype = DN_GET_COMPAT; - // cmd->id = sopt_valsize; - D("compatibility mode"); - } - a.extra = (struct copy_range *)cmd; - if (cmd->len == sizeof(*cmd)) { /* no range, create a default */ - uint32_t *rp = (uint32_t *)(cmd + 1); - cmd->len += 2* sizeof(uint32_t); - rp[0] = 1; - rp[1] = DN_MAX_ID - 1; - if (cmd->subtype == DN_LINK) { - rp[0] += DN_MAX_ID; - rp[1] += DN_MAX_ID; - } - } - /* Count space (under lock) and allocate (outside lock). - * Exit with lock held if we manage to get enough buffer. - * Try a few times then give up. - */ - for (have = 0, i = 0; i < 10; i++) { - DN_BH_WLOCK(); - need = compute_space(cmd, &a); - - /* if there is a range, ignore value from compute_space() */ - if (l > sizeof(*cmd)) - need = sopt_valsize - sizeof(*cmd); - - if (need < 0) { - DN_BH_WUNLOCK(); - error = EINVAL; - goto done; - } - need += sizeof(*cmd); - cmd->id = need; - if (have >= need) - break; - - DN_BH_WUNLOCK(); - if (start) - free(start, M_DUMMYNET); - start = NULL; - if (need > sopt_valsize) - break; - - have = need; - start = malloc(have, M_DUMMYNET, M_WAITOK | M_ZERO); - } - - if (start == NULL) { - if (compat) { - *compat = NULL; - error = 1; // XXX - } else { - error = sooptcopyout(sopt, cmd, sizeof(*cmd)); - } - goto done; - } - ND("have %d:%d sched %d, %d:%d links %d, %d:%d flowsets %d, " - "%d:%d si %d, %d:%d queues %d", - dn_cfg.schk_count, sizeof(struct dn_sch), DN_SCH, - dn_cfg.schk_count, sizeof(struct dn_link), DN_LINK, - dn_cfg.fsk_count, sizeof(struct dn_fs), DN_FS, - dn_cfg.si_count, sizeof(struct dn_flow), DN_SCH_I, - dn_cfg.queue_count, sizeof(struct dn_queue), DN_QUEUE); - sopt->sopt_valsize = sopt_valsize; - a.type = cmd->subtype; - - if (compat == NULL) { - bcopy(cmd, start, sizeof(*cmd)); - ((struct dn_id*)(start))->len = sizeof(struct dn_id); - buf = start + sizeof(*cmd); - } else - buf = start; - a.start = &buf; - a.end = start + have; - /* start copying other objects */ - if (compat) { - a.type = DN_COMPAT_PIPE; - dn_ht_scan(dn_cfg.schedhash, copy_data_helper_compat, &a); - a.type = DN_COMPAT_QUEUE; - dn_ht_scan(dn_cfg.fshash, copy_data_helper_compat, &a); - } else if (a.type == DN_FS) { - dn_ht_scan(dn_cfg.fshash, copy_data_helper, &a); - } else { - dn_ht_scan(dn_cfg.schedhash, copy_data_helper, &a); - } - DN_BH_WUNLOCK(); - - if (compat) { - *compat = start; - sopt->sopt_valsize = buf - start; - /* free() is done by ip_dummynet_compat() */ - start = NULL; //XXX hack - } else { - error = sooptcopyout(sopt, start, buf - start); - } -done: - if (cmd && cmd != &r.o) - free(cmd, M_DUMMYNET); - if (start) - free(start, M_DUMMYNET); - return error; -} - -/* Callback called on scheduler instance to delete it if idle */ -static int -drain_scheduler_cb(void *_si, void *arg) -{ - struct dn_sch_inst *si = _si; - - if ((si->kflags & DN_ACTIVE) || si->dline.mq.head != NULL) - return 0; - - if (si->sched->fp->flags & DN_MULTIQUEUE) { - if (si->q_count == 0) - return si_destroy(si, NULL); - else - return 0; - } else { /* !DN_MULTIQUEUE */ - if ((si+1)->ni.length == 0) - return si_destroy(si, NULL); - else - return 0; - } - return 0; /* unreachable */ -} - -/* Callback called on scheduler to check if it has instances */ -static int -drain_scheduler_sch_cb(void *_s, void *arg) -{ - struct dn_schk *s = _s; - - if (s->sch.flags & DN_HAVE_MASK) { - dn_ht_scan_bucket(s->siht, &s->drain_bucket, - drain_scheduler_cb, NULL); - s->drain_bucket++; - } else { - if (s->siht) { - if (drain_scheduler_cb(s->siht, NULL) == DNHT_SCAN_DEL) - s->siht = NULL; - } - } - return 0; -} - -/* Called every tick, try to delete a 'bucket' of scheduler */ -void -dn_drain_scheduler(void) -{ - dn_ht_scan_bucket(dn_cfg.schedhash, &dn_cfg.drain_sch, - drain_scheduler_sch_cb, NULL); - dn_cfg.drain_sch++; -} - -/* Callback called on queue to delete if it is idle */ -static int -drain_queue_cb(void *_q, void *arg) -{ - struct dn_queue *q = _q; - - if (q->ni.length == 0) { - dn_delete_queue(q, DN_DESTROY); - return DNHT_SCAN_DEL; /* queue is deleted */ - } - - return 0; /* queue isn't deleted */ -} - -/* Callback called on flowset used to check if it has queues */ -static int -drain_queue_fs_cb(void *_fs, void *arg) -{ - struct dn_fsk *fs = _fs; - - if (fs->fs.flags & DN_QHT_HASH) { - /* Flowset has a hash table for queues */ - dn_ht_scan_bucket(fs->qht, &fs->drain_bucket, - drain_queue_cb, NULL); - fs->drain_bucket++; - } else { - /* No hash table for this flowset, null the pointer - * if the queue is deleted - */ - if (fs->qht) { - if (drain_queue_cb(fs->qht, NULL) == DNHT_SCAN_DEL) - fs->qht = NULL; - } - } - return 0; -} - -/* Called every tick, try to delete a 'bucket' of queue */ -void -dn_drain_queue(void) -{ - /* scan a bucket of flowset */ - dn_ht_scan_bucket(dn_cfg.fshash, &dn_cfg.drain_fs, - drain_queue_fs_cb, NULL); - dn_cfg.drain_fs++; -} - -/* - * Handler for the various dummynet socket options - */ -static int -ip_dn_ctl(struct sockopt *sopt) -{ - void *p = NULL; - int error, l; - - error = priv_check(sopt->sopt_td, PRIV_NETINET_DUMMYNET); - if (error) - return (error); - - /* Disallow sets in really-really secure mode. */ - if (sopt->sopt_dir == SOPT_SET) { - error = securelevel_ge(sopt->sopt_td->td_ucred, 3); - if (error) - return (error); - } - - switch (sopt->sopt_name) { - default : - D("dummynet: unknown option %d", sopt->sopt_name); - error = EINVAL; - break; - - case IP_DUMMYNET_FLUSH: - case IP_DUMMYNET_CONFIGURE: - case IP_DUMMYNET_DEL: /* remove a pipe or queue */ - case IP_DUMMYNET_GET: - D("dummynet: compat option %d", sopt->sopt_name); - error = ip_dummynet_compat(sopt); - break; - - case IP_DUMMYNET3 : - if (sopt->sopt_dir == SOPT_GET) { - error = dummynet_get(sopt, NULL); - break; - } - l = sopt->sopt_valsize; - if (l < sizeof(struct dn_id) || l > 12000) { - D("argument len %d invalid", l); - break; - } - p = malloc(l, M_TEMP, M_WAITOK); // XXX can it fail ? - error = sooptcopyin(sopt, p, l, l); - if (error) - break ; - error = do_config(p, l); - break; - } - - if (p != NULL) - free(p, M_TEMP); - - return error ; -} - - -static void -ip_dn_init(void) -{ - if (dn_cfg.init_done) - return; - printf("DUMMYNET %p with IPv6 initialized (100409)\n", curvnet); - dn_cfg.init_done = 1; - /* Set defaults here. MSVC does not accept initializers, - * and this is also useful for vimages - */ - /* queue limits */ - dn_cfg.slot_limit = 100; /* Foot shooting limit for queues. */ - dn_cfg.byte_limit = 1024 * 1024; - dn_cfg.expire = 1; - - /* RED parameters */ - dn_cfg.red_lookup_depth = 256; /* default lookup table depth */ - dn_cfg.red_avg_pkt_size = 512; /* default medium packet size */ - dn_cfg.red_max_pkt_size = 1500; /* default max packet size */ - - /* hash tables */ - dn_cfg.max_hash_size = 65536; /* max in the hash tables */ - dn_cfg.hash_size = 64; /* default hash size */ - - /* create hash tables for schedulers and flowsets. - * In both we search by key and by pointer. - */ - dn_cfg.schedhash = dn_ht_init(NULL, dn_cfg.hash_size, - offsetof(struct dn_schk, schk_next), - schk_hash, schk_match, schk_new); - dn_cfg.fshash = dn_ht_init(NULL, dn_cfg.hash_size, - offsetof(struct dn_fsk, fsk_next), - fsk_hash, fsk_match, fsk_new); - - /* bucket index to drain object */ - dn_cfg.drain_fs = 0; - dn_cfg.drain_sch = 0; - - heap_init(&dn_cfg.evheap, 16, offsetof(struct dn_id, id)); - SLIST_INIT(&dn_cfg.fsu); - SLIST_INIT(&dn_cfg.schedlist); - - DN_LOCK_INIT(); - - TASK_INIT(&dn_task, 0, dummynet_task, curvnet); - dn_tq = taskqueue_create("dummynet", M_WAITOK, - taskqueue_thread_enqueue, &dn_tq); - taskqueue_start_threads(&dn_tq, 1, PI_NET, "dummynet"); - - callout_init(&dn_timeout, CALLOUT_MPSAFE); - callout_reset(&dn_timeout, 1, dummynet, NULL); - - /* Initialize curr_time adjustment mechanics. */ - getmicrouptime(&dn_cfg.prev_t); -} - -#ifdef KLD_MODULE -static void -ip_dn_destroy(int last) -{ - callout_drain(&dn_timeout); - - DN_BH_WLOCK(); - if (last) { - ND("removing last instance\n"); - ip_dn_ctl_ptr = NULL; - ip_dn_io_ptr = NULL; - } - - dummynet_flush(); - DN_BH_WUNLOCK(); - taskqueue_drain(dn_tq, &dn_task); - taskqueue_free(dn_tq); - - dn_ht_free(dn_cfg.schedhash, 0); - dn_ht_free(dn_cfg.fshash, 0); - heap_free(&dn_cfg.evheap); - - DN_LOCK_DESTROY(); -} -#endif /* KLD_MODULE */ - -static int -dummynet_modevent(module_t mod, int type, void *data) -{ - - if (type == MOD_LOAD) { - if (ip_dn_io_ptr) { - printf("DUMMYNET already loaded\n"); - return EEXIST ; - } - ip_dn_init(); - ip_dn_ctl_ptr = ip_dn_ctl; - ip_dn_io_ptr = dummynet_io; - return 0; - } else if (type == MOD_UNLOAD) { -#if !defined(KLD_MODULE) - printf("dummynet statically compiled, cannot unload\n"); - return EINVAL ; -#else - ip_dn_destroy(1 /* last */); - return 0; -#endif - } else - return EOPNOTSUPP; -} - -/* modevent helpers for the modules */ -static int -load_dn_sched(struct dn_alg *d) -{ - struct dn_alg *s; - - if (d == NULL) - return 1; /* error */ - ip_dn_init(); /* just in case, we need the lock */ - - /* Check that mandatory funcs exists */ - if (d->enqueue == NULL || d->dequeue == NULL) { - D("missing enqueue or dequeue for %s", d->name); - return 1; - } - - /* Search if scheduler already exists */ - DN_BH_WLOCK(); - SLIST_FOREACH(s, &dn_cfg.schedlist, next) { - if (strcmp(s->name, d->name) == 0) { - D("%s already loaded", d->name); - break; /* scheduler already exists */ - } - } - if (s == NULL) - SLIST_INSERT_HEAD(&dn_cfg.schedlist, d, next); - DN_BH_WUNLOCK(); - D("dn_sched %s %sloaded", d->name, s ? "not ":""); - return s ? 1 : 0; -} - -static int -unload_dn_sched(struct dn_alg *s) -{ - struct dn_alg *tmp, *r; - int err = EINVAL; - - ND("called for %s", s->name); - - DN_BH_WLOCK(); - SLIST_FOREACH_SAFE(r, &dn_cfg.schedlist, next, tmp) { - if (strcmp(s->name, r->name) != 0) - continue; - ND("ref_count = %d", r->ref_count); - err = (r->ref_count != 0) ? EBUSY : 0; - if (err == 0) - SLIST_REMOVE(&dn_cfg.schedlist, r, dn_alg, next); - break; - } - DN_BH_WUNLOCK(); - D("dn_sched %s %sunloaded", s->name, err ? "not ":""); - return err; -} - -int -dn_sched_modevent(module_t mod, int cmd, void *arg) -{ - struct dn_alg *sch = arg; - - if (cmd == MOD_LOAD) - return load_dn_sched(sch); - else if (cmd == MOD_UNLOAD) - return unload_dn_sched(sch); - else - return EINVAL; -} - -static moduledata_t dummynet_mod = { - "dummynet", dummynet_modevent, NULL -}; - -#define DN_SI_SUB SI_SUB_PROTO_IFATTACHDOMAIN -#define DN_MODEV_ORD (SI_ORDER_ANY - 128) /* after ipfw */ -DECLARE_MODULE(dummynet, dummynet_mod, DN_SI_SUB, DN_MODEV_ORD); -MODULE_DEPEND(dummynet, ipfw, 2, 2, 2); -MODULE_VERSION(dummynet, 3); - -/* - * Starting up. Done in order after dummynet_modevent() has been called. - * VNET_SYSINIT is also called for each existing vnet and each new vnet. - */ -//VNET_SYSINIT(vnet_dn_init, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_init, NULL); - -/* - * Shutdown handlers up shop. These are done in REVERSE ORDER, but still - * after dummynet_modevent() has been called. Not called on reboot. - * VNET_SYSUNINIT is also called for each exiting vnet as it exits. - * or when the module is unloaded. - */ -//VNET_SYSUNINIT(vnet_dn_uninit, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_destroy, NULL); - -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c deleted file mode 100644 index 0dfab1f8ee91c..0000000000000 --- a/sys/netinet/ipfw/ip_fw2.c +++ /dev/null @@ -1,2790 +0,0 @@ -/*- - * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa - * - * 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 -__FBSDID("$FreeBSD$"); - -/* - * The FreeBSD IP packet firewall, main file - */ - -#include "opt_ipfw.h" -#include "opt_ipdivert.h" -#include "opt_inet.h" -#ifndef INET -#error "IPFIREWALL requires INET" -#endif /* INET */ -#include "opt_inet6.h" -#include "opt_ipsec.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for ETHERTYPE_IP */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#ifdef INET6 -#include -#include -#include -#endif - -#include /* XXX for in_cksum */ - -#ifdef MAC -#include -#endif - -/* - * static variables followed by global ones. - * All ipfw global variables are here. - */ - -/* ipfw_vnet_ready controls when we are open for business */ -static VNET_DEFINE(int, ipfw_vnet_ready) = 0; -#define V_ipfw_vnet_ready VNET(ipfw_vnet_ready) - -static VNET_DEFINE(int, fw_deny_unknown_exthdrs); -#define V_fw_deny_unknown_exthdrs VNET(fw_deny_unknown_exthdrs) - -static VNET_DEFINE(int, fw_permit_single_frag6) = 1; -#define V_fw_permit_single_frag6 VNET(fw_permit_single_frag6) - -#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT -static int default_to_accept = 1; -#else -static int default_to_accept; -#endif - -VNET_DEFINE(int, autoinc_step); -VNET_DEFINE(int, fw_one_pass) = 1; - -VNET_DEFINE(unsigned int, fw_tables_max); -/* Use 128 tables by default */ -static unsigned int default_fw_tables = IPFW_TABLES_DEFAULT; - -/* - * Each rule belongs to one of 32 different sets (0..31). - * The variable set_disable contains one bit per set. - * If the bit is set, all rules in the corresponding set - * are disabled. Set RESVD_SET(31) is reserved for the default rule - * and rules that are not deleted by the flush command, - * and CANNOT be disabled. - * Rules in set RESVD_SET can only be deleted individually. - */ -VNET_DEFINE(u_int32_t, set_disable); -#define V_set_disable VNET(set_disable) - -VNET_DEFINE(int, fw_verbose); -/* counter for ipfw_log(NULL...) */ -VNET_DEFINE(u_int64_t, norule_counter); -VNET_DEFINE(int, verbose_limit); - -/* layer3_chain contains the list of rules for layer 3 */ -VNET_DEFINE(struct ip_fw_chain, layer3_chain); - -ipfw_nat_t *ipfw_nat_ptr = NULL; -struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); -ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; -ipfw_nat_cfg_t *ipfw_nat_del_ptr; -ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; -ipfw_nat_cfg_t *ipfw_nat_get_log_ptr; - -#ifdef SYSCTL_NODE -uint32_t dummy_def = IPFW_DEFAULT_RULE; -static int sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS); - -SYSBEGIN(f3) - -SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); -SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, one_pass, - CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_one_pass), 0, - "Only do a single pass through ipfw when using dummynet(4)"); -SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, autoinc_step, - CTLFLAG_RW, &VNET_NAME(autoinc_step), 0, - "Rule number auto-increment step"); -SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, verbose, - CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_verbose), 0, - "Log matches to ipfw rules"); -SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, - CTLFLAG_RW, &VNET_NAME(verbose_limit), 0, - "Set upper limit of matches of ipfw rules logged"); -SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD, - &dummy_def, 0, - "The default/max possible rule number."); -SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max, - CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU", - "Maximum number of tables"); -SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN, - &default_to_accept, 0, - "Make the default rule accept all packets."); -TUNABLE_INT("net.inet.ip.fw.default_to_accept", &default_to_accept); -TUNABLE_INT("net.inet.ip.fw.tables_max", (int *)&default_fw_tables); -SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, static_count, - CTLFLAG_RD, &VNET_NAME(layer3_chain.n_rules), 0, - "Number of static rules"); - -#ifdef INET6 -SYSCTL_DECL(_net_inet6_ip6); -SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); -SYSCTL_VNET_INT(_net_inet6_ip6_fw, OID_AUTO, deny_unknown_exthdrs, - CTLFLAG_RW | CTLFLAG_SECURE, &VNET_NAME(fw_deny_unknown_exthdrs), 0, - "Deny packets with unknown IPv6 Extension Headers"); -SYSCTL_VNET_INT(_net_inet6_ip6_fw, OID_AUTO, permit_single_frag6, - CTLFLAG_RW | CTLFLAG_SECURE, &VNET_NAME(fw_permit_single_frag6), 0, - "Permit single packet IPv6 fragments"); -#endif /* INET6 */ - -SYSEND - -#endif /* SYSCTL_NODE */ - - -/* - * Some macros used in the various matching options. - * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T - * Other macros just cast void * into the appropriate type - */ -#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) -#define TCP(p) ((struct tcphdr *)(p)) -#define SCTP(p) ((struct sctphdr *)(p)) -#define UDP(p) ((struct udphdr *)(p)) -#define ICMP(p) ((struct icmphdr *)(p)) -#define ICMP6(p) ((struct icmp6_hdr *)(p)) - -static __inline int -icmptype_match(struct icmphdr *icmp, ipfw_insn_u32 *cmd) -{ - int type = icmp->icmp_type; - - return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<icmp_type; - - return (type <= ICMP_MAXTYPE && (TT & (1<arg1 or cmd->d[0]. - * - * We scan options and store the bits we find set. We succeed if - * - * (want_set & ~bits) == 0 && (want_clear & ~bits) == want_clear - * - * The code is sometimes optimized not to store additional variables. - */ - -static int -flags_match(ipfw_insn *cmd, u_int8_t bits) -{ - u_char want_clear; - bits = ~bits; - - if ( ((cmd->arg1 & 0xff) & bits) != 0) - return 0; /* some bits we want set were clear */ - want_clear = (cmd->arg1 >> 8) & 0xff; - if ( (want_clear & bits) != want_clear) - return 0; /* some bits we want clear were set */ - return 1; -} - -static int -ipopts_match(struct ip *ip, ipfw_insn *cmd) -{ - int optlen, bits = 0; - u_char *cp = (u_char *)(ip + 1); - int x = (ip->ip_hl << 2) - sizeof (struct ip); - - for (; x > 0; x -= optlen, cp += optlen) { - int opt = cp[IPOPT_OPTVAL]; - - if (opt == IPOPT_EOL) - break; - if (opt == IPOPT_NOP) - optlen = 1; - else { - optlen = cp[IPOPT_OLEN]; - if (optlen <= 0 || optlen > x) - return 0; /* invalid or truncated */ - } - switch (opt) { - - default: - break; - - case IPOPT_LSRR: - bits |= IP_FW_IPOPT_LSRR; - break; - - case IPOPT_SSRR: - bits |= IP_FW_IPOPT_SSRR; - break; - - case IPOPT_RR: - bits |= IP_FW_IPOPT_RR; - break; - - case IPOPT_TS: - bits |= IP_FW_IPOPT_TS; - break; - } - } - return (flags_match(cmd, bits)); -} - -static int -tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) -{ - int optlen, bits = 0; - u_char *cp = (u_char *)(tcp + 1); - int x = (tcp->th_off << 2) - sizeof(struct tcphdr); - - for (; x > 0; x -= optlen, cp += optlen) { - int opt = cp[0]; - if (opt == TCPOPT_EOL) - break; - if (opt == TCPOPT_NOP) - optlen = 1; - else { - optlen = cp[1]; - if (optlen <= 0) - break; - } - - switch (opt) { - - default: - break; - - case TCPOPT_MAXSEG: - bits |= IP_FW_TCPOPT_MSS; - break; - - case TCPOPT_WINDOW: - bits |= IP_FW_TCPOPT_WINDOW; - break; - - case TCPOPT_SACK_PERMITTED: - case TCPOPT_SACK: - bits |= IP_FW_TCPOPT_SACK; - break; - - case TCPOPT_TIMESTAMP: - bits |= IP_FW_TCPOPT_TS; - break; - - } - } - return (flags_match(cmd, bits)); -} - -static int -iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uint32_t *tablearg) -{ - if (ifp == NULL) /* no iface with this packet, match fails */ - return 0; - /* Check by name or by IP address */ - if (cmd->name[0] != '\0') { /* match by name */ - if (cmd->name[0] == '\1') /* use tablearg to match */ - return ipfw_lookup_table_extended(chain, cmd->p.glob, - ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE); - /* Check name */ - if (cmd->p.glob) { - if (fnmatch(cmd->name, ifp->if_xname, 0) == 0) - return(1); - } else { - if (strncmp(ifp->if_xname, cmd->name, IFNAMSIZ) == 0) - return(1); - } - } else { -#ifdef __FreeBSD__ /* and OSX too ? */ - struct ifaddr *ia; - - if_addr_rlock(ifp); - TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) { - if (ia->ifa_addr->sa_family != AF_INET) - continue; - if (cmd->p.ip.s_addr == ((struct sockaddr_in *) - (ia->ifa_addr))->sin_addr.s_addr) { - if_addr_runlock(ifp); - return(1); /* match */ - } - } - if_addr_runlock(ifp); -#endif /* __FreeBSD__ */ - } - return(0); /* no match, fail ... */ -} - -/* - * The verify_path function checks if a route to the src exists and - * if it is reachable via ifp (when provided). - * - * The 'verrevpath' option checks that the interface that an IP packet - * arrives on is the same interface that traffic destined for the - * packet's source address would be routed out of. - * The 'versrcreach' option just checks that the source address is - * reachable via any route (except default) in the routing table. - * These two are a measure to block forged packets. This is also - * commonly known as "anti-spoofing" or Unicast Reverse Path - * Forwarding (Unicast RFP) in Cisco-ese. The name of the knobs - * is purposely reminiscent of the Cisco IOS command, - * - * ip verify unicast reverse-path - * ip verify unicast source reachable-via any - * - * which implements the same functionality. But note that the syntax - * is misleading, and the check may be performed on all IP packets - * whether unicast, multicast, or broadcast. - */ -static int -verify_path(struct in_addr src, struct ifnet *ifp, u_int fib) -{ -#ifndef __FreeBSD__ - return 0; -#else - struct route ro; - struct sockaddr_in *dst; - - bzero(&ro, sizeof(ro)); - - dst = (struct sockaddr_in *)&(ro.ro_dst); - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = src; - in_rtalloc_ign(&ro, 0, fib); - - if (ro.ro_rt == NULL) - return 0; - - /* - * If ifp is provided, check for equality with rtentry. - * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp, - * in order to pass packets injected back by if_simloop(): - * if useloopback == 1 routing entry (via lo0) for our own address - * may exist, so we need to handle routing assymetry. - */ - if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) { - RTFREE(ro.ro_rt); - return 0; - } - - /* if no ifp provided, check if rtentry is not default route */ - if (ifp == NULL && - satosin(rt_key(ro.ro_rt))->sin_addr.s_addr == INADDR_ANY) { - RTFREE(ro.ro_rt); - return 0; - } - - /* or if this is a blackhole/reject route */ - if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { - RTFREE(ro.ro_rt); - return 0; - } - - /* found valid route */ - RTFREE(ro.ro_rt); - return 1; -#endif /* __FreeBSD__ */ -} - -#ifdef INET6 -/* - * ipv6 specific rules here... - */ -static __inline int -icmp6type_match (int type, ipfw_insn_u32 *cmd) -{ - return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) ); -} - -static int -flow6id_match( int curr_flow, ipfw_insn_u32 *cmd ) -{ - int i; - for (i=0; i <= cmd->o.arg1; ++i ) - if (curr_flow == cmd->d[i] ) - return 1; - return 0; -} - -/* support for IP6_*_ME opcodes */ -static int -search_ip6_addr_net (struct in6_addr * ip6_addr) -{ - struct ifnet *mdc; - struct ifaddr *mdc2; - struct in6_ifaddr *fdm; - struct in6_addr copia; - - TAILQ_FOREACH(mdc, &V_ifnet, if_link) { - if_addr_rlock(mdc); - TAILQ_FOREACH(mdc2, &mdc->if_addrhead, ifa_link) { - if (mdc2->ifa_addr->sa_family == AF_INET6) { - fdm = (struct in6_ifaddr *)mdc2; - copia = fdm->ia_addr.sin6_addr; - /* need for leaving scope_id in the sock_addr */ - in6_clearscope(&copia); - if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia)) { - if_addr_runlock(mdc); - return 1; - } - } - } - if_addr_runlock(mdc); - } - return 0; -} - -static int -verify_path6(struct in6_addr *src, struct ifnet *ifp, u_int fib) -{ - struct route_in6 ro; - struct sockaddr_in6 *dst; - - bzero(&ro, sizeof(ro)); - - dst = (struct sockaddr_in6 * )&(ro.ro_dst); - dst->sin6_family = AF_INET6; - dst->sin6_len = sizeof(*dst); - dst->sin6_addr = *src; - - in6_rtalloc_ign(&ro, 0, fib); - if (ro.ro_rt == NULL) - return 0; - - /* - * if ifp is provided, check for equality with rtentry - * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp, - * to support the case of sending packets to an address of our own. - * (where the former interface is the first argument of if_simloop() - * (=ifp), the latter is lo0) - */ - if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) { - RTFREE(ro.ro_rt); - return 0; - } - - /* if no ifp provided, check if rtentry is not default route */ - if (ifp == NULL && - IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(ro.ro_rt))->sin6_addr)) { - RTFREE(ro.ro_rt); - return 0; - } - - /* or if this is a blackhole/reject route */ - if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { - RTFREE(ro.ro_rt); - return 0; - } - - /* found valid route */ - RTFREE(ro.ro_rt); - return 1; - -} - -static int -is_icmp6_query(int icmp6_type) -{ - if ((icmp6_type <= ICMP6_MAXTYPE) && - (icmp6_type == ICMP6_ECHO_REQUEST || - icmp6_type == ICMP6_MEMBERSHIP_QUERY || - icmp6_type == ICMP6_WRUREQUEST || - icmp6_type == ICMP6_FQDN_QUERY || - icmp6_type == ICMP6_NI_QUERY)) - return (1); - - return (0); -} - -static void -send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6) -{ - struct mbuf *m; - - m = args->m; - if (code == ICMP6_UNREACH_RST && args->f_id.proto == IPPROTO_TCP) { - struct tcphdr *tcp; - tcp = (struct tcphdr *)((char *)ip6 + hlen); - - if ((tcp->th_flags & TH_RST) == 0) { - struct mbuf *m0; - m0 = ipfw_send_pkt(args->m, &(args->f_id), - ntohl(tcp->th_seq), ntohl(tcp->th_ack), - tcp->th_flags | TH_RST); - if (m0 != NULL) - ip6_output(m0, NULL, NULL, 0, NULL, NULL, - NULL); - } - FREE_PKT(m); - } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ -#if 0 - /* - * Unlike above, the mbufs need to line up with the ip6 hdr, - * as the contents are read. We need to m_adj() the - * needed amount. - * The mbuf will however be thrown away so we can adjust it. - * Remember we did an m_pullup on it already so we - * can make some assumptions about contiguousness. - */ - if (args->L3offset) - m_adj(m, args->L3offset); -#endif - icmp6_error(m, ICMP6_DST_UNREACH, code, 0); - } else - FREE_PKT(m); - - args->m = NULL; -} - -#endif /* INET6 */ - - -/* - * sends a reject message, consuming the mbuf passed as an argument. - */ -static void -send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip) -{ - -#if 0 - /* XXX When ip is not guaranteed to be at mtod() we will - * need to account for this */ - * The mbuf will however be thrown away so we can adjust it. - * Remember we did an m_pullup on it already so we - * can make some assumptions about contiguousness. - */ - if (args->L3offset) - m_adj(m, args->L3offset); -#endif - if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */ - /* We need the IP header in host order for icmp_error(). */ - SET_HOST_IPLEN(ip); - icmp_error(args->m, ICMP_UNREACH, code, 0L, 0); - } else if (args->f_id.proto == IPPROTO_TCP) { - struct tcphdr *const tcp = - L3HDR(struct tcphdr, mtod(args->m, struct ip *)); - if ( (tcp->th_flags & TH_RST) == 0) { - struct mbuf *m; - m = ipfw_send_pkt(args->m, &(args->f_id), - ntohl(tcp->th_seq), ntohl(tcp->th_ack), - tcp->th_flags | TH_RST); - if (m != NULL) - ip_output(m, NULL, NULL, 0, NULL, NULL); - } - FREE_PKT(args->m); - } else - FREE_PKT(args->m); - args->m = NULL; -} - -/* - * Support for uid/gid/jail lookup. These tests are expensive - * (because we may need to look into the list of active sockets) - * so we cache the results. ugid_lookupp is 0 if we have not - * yet done a lookup, 1 if we succeeded, and -1 if we tried - * and failed. The function always returns the match value. - * We could actually spare the variable and use *uc, setting - * it to '(void *)check_uidgid if we have no info, NULL if - * we tried and failed, or any other value if successful. - */ -static int -check_uidgid(ipfw_insn_u32 *insn, struct ip_fw_args *args, int *ugid_lookupp, - struct ucred **uc) -{ -#ifndef __FreeBSD__ - /* XXX */ - return cred_check(insn, proto, oif, - dst_ip, dst_port, src_ip, src_port, - (struct bsd_ucred *)uc, ugid_lookupp, ((struct mbuf *)inp)->m_skb); -#else /* FreeBSD */ - struct in_addr src_ip, dst_ip; - struct inpcbinfo *pi; - struct ipfw_flow_id *id; - struct inpcb *pcb, *inp; - struct ifnet *oif; - int lookupflags; - int match; - - id = &args->f_id; - inp = args->inp; - oif = args->oif; - - /* - * Check to see if the UDP or TCP stack supplied us with - * the PCB. If so, rather then holding a lock and looking - * up the PCB, we can use the one that was supplied. - */ - if (inp && *ugid_lookupp == 0) { - INP_LOCK_ASSERT(inp); - if (inp->inp_socket != NULL) { - *uc = crhold(inp->inp_cred); - *ugid_lookupp = 1; - } else - *ugid_lookupp = -1; - } - /* - * If we have already been here and the packet has no - * PCB entry associated with it, then we can safely - * assume that this is a no match. - */ - if (*ugid_lookupp == -1) - return (0); - if (id->proto == IPPROTO_TCP) { - lookupflags = 0; - pi = &V_tcbinfo; - } else if (id->proto == IPPROTO_UDP) { - lookupflags = INPLOOKUP_WILDCARD; - pi = &V_udbinfo; - } else - return 0; - lookupflags |= INPLOOKUP_RLOCKPCB; - match = 0; - if (*ugid_lookupp == 0) { - if (id->addr_type == 6) { -#ifdef INET6 - if (oif == NULL) - pcb = in6_pcblookup_mbuf(pi, - &id->src_ip6, htons(id->src_port), - &id->dst_ip6, htons(id->dst_port), - lookupflags, oif, args->m); - else - pcb = in6_pcblookup_mbuf(pi, - &id->dst_ip6, htons(id->dst_port), - &id->src_ip6, htons(id->src_port), - lookupflags, oif, args->m); -#else - *ugid_lookupp = -1; - return (0); -#endif - } else { - src_ip.s_addr = htonl(id->src_ip); - dst_ip.s_addr = htonl(id->dst_ip); - if (oif == NULL) - pcb = in_pcblookup_mbuf(pi, - src_ip, htons(id->src_port), - dst_ip, htons(id->dst_port), - lookupflags, oif, args->m); - else - pcb = in_pcblookup_mbuf(pi, - dst_ip, htons(id->dst_port), - src_ip, htons(id->src_port), - lookupflags, oif, args->m); - } - if (pcb != NULL) { - INP_RLOCK_ASSERT(pcb); - *uc = crhold(pcb->inp_cred); - *ugid_lookupp = 1; - INP_RUNLOCK(pcb); - } - if (*ugid_lookupp == 0) { - /* - * We tried and failed, set the variable to -1 - * so we will not try again on this packet. - */ - *ugid_lookupp = -1; - return (0); - } - } - if (insn->o.opcode == O_UID) - match = ((*uc)->cr_uid == (uid_t)insn->d[0]); - else if (insn->o.opcode == O_GID) - match = groupmember((gid_t)insn->d[0], *uc); - else if (insn->o.opcode == O_JAIL) - match = ((*uc)->cr_prison->pr_id == (int)insn->d[0]); - return (match); -#endif /* __FreeBSD__ */ -} - -/* - * Helper function to set args with info on the rule after the matching - * one. slot is precise, whereas we guess rule_id as they are - * assigned sequentially. - */ -static inline void -set_match(struct ip_fw_args *args, int slot, - struct ip_fw_chain *chain) -{ - args->rule.chain_id = chain->id; - args->rule.slot = slot + 1; /* we use 0 as a marker */ - args->rule.rule_id = 1 + chain->map[slot]->id; - args->rule.rulenum = chain->map[slot]->rulenum; -} - -/* - * The main check routine for the firewall. - * - * All arguments are in args so we can modify them and return them - * back to the caller. - * - * Parameters: - * - * args->m (in/out) The packet; we set to NULL when/if we nuke it. - * Starts with the IP header. - * args->eh (in) Mac header if present, NULL for layer3 packet. - * args->L3offset Number of bytes bypassed if we came from L2. - * e.g. often sizeof(eh) ** NOTYET ** - * args->oif Outgoing interface, NULL if packet is incoming. - * The incoming interface is in the mbuf. (in) - * args->divert_rule (in/out) - * Skip up to the first rule past this rule number; - * upon return, non-zero port number for divert or tee. - * - * args->rule Pointer to the last matching rule (in/out) - * args->next_hop Socket we are forwarding to (out). - * args->next_hop6 IPv6 next hop we are forwarding to (out). - * args->f_id Addresses grabbed from the packet (out) - * args->rule.info a cookie depending on rule action - * - * Return value: - * - * IP_FW_PASS the packet must be accepted - * IP_FW_DENY the packet must be dropped - * IP_FW_DIVERT divert packet, port in m_tag - * IP_FW_TEE tee packet, port in m_tag - * IP_FW_DUMMYNET to dummynet, pipe in args->cookie - * IP_FW_NETGRAPH into netgraph, cookie args->cookie - * args->rule contains the matching rule, - * args->rule.info has additional information. - * - */ -int -ipfw_chk(struct ip_fw_args *args) -{ - - /* - * Local variables holding state while processing a packet: - * - * IMPORTANT NOTE: to speed up the processing of rules, there - * are some assumption on the values of the variables, which - * are documented here. Should you change them, please check - * the implementation of the various instructions to make sure - * that they still work. - * - * args->eh The MAC header. It is non-null for a layer2 - * packet, it is NULL for a layer-3 packet. - * **notyet** - * args->L3offset Offset in the packet to the L3 (IP or equiv.) header. - * - * m | args->m Pointer to the mbuf, as received from the caller. - * It may change if ipfw_chk() does an m_pullup, or if it - * consumes the packet because it calls send_reject(). - * XXX This has to change, so that ipfw_chk() never modifies - * or consumes the buffer. - * ip is the beginning of the ip(4 or 6) header. - * Calculated by adding the L3offset to the start of data. - * (Until we start using L3offset, the packet is - * supposed to start with the ip header). - */ - struct mbuf *m = args->m; - struct ip *ip = mtod(m, struct ip *); - - /* - * For rules which contain uid/gid or jail constraints, cache - * a copy of the users credentials after the pcb lookup has been - * executed. This will speed up the processing of rules with - * these types of constraints, as well as decrease contention - * on pcb related locks. - */ -#ifndef __FreeBSD__ - struct bsd_ucred ucred_cache; -#else - struct ucred *ucred_cache = NULL; -#endif - int ucred_lookup = 0; - - /* - * oif | args->oif If NULL, ipfw_chk has been called on the - * inbound path (ether_input, ip_input). - * If non-NULL, ipfw_chk has been called on the outbound path - * (ether_output, ip_output). - */ - struct ifnet *oif = args->oif; - - int f_pos = 0; /* index of current rule in the array */ - int retval = 0; - - /* - * hlen The length of the IP header. - */ - u_int hlen = 0; /* hlen >0 means we have an IP pkt */ - - /* - * offset The offset of a fragment. offset != 0 means that - * we have a fragment at this offset of an IPv4 packet. - * offset == 0 means that (if this is an IPv4 packet) - * this is the first or only fragment. - * For IPv6 offset|ip6f_mf == 0 means there is no Fragment Header - * or there is a single packet fragement (fragement header added - * without needed). We will treat a single packet fragment as if - * there was no fragment header (or log/block depending on the - * V_fw_permit_single_frag6 sysctl setting). - */ - u_short offset = 0; - u_short ip6f_mf = 0; - - /* - * Local copies of addresses. They are only valid if we have - * an IP packet. - * - * proto The protocol. Set to 0 for non-ip packets, - * or to the protocol read from the packet otherwise. - * proto != 0 means that we have an IPv4 packet. - * - * src_port, dst_port port numbers, in HOST format. Only - * valid for TCP and UDP packets. - * - * src_ip, dst_ip ip addresses, in NETWORK format. - * Only valid for IPv4 packets. - */ - uint8_t proto; - uint16_t src_port = 0, dst_port = 0; /* NOTE: host format */ - struct in_addr src_ip, dst_ip; /* NOTE: network format */ - uint16_t iplen=0; - int pktlen; - uint16_t etype = 0; /* Host order stored ether type */ - - /* - * dyn_dir = MATCH_UNKNOWN when rules unchecked, - * MATCH_NONE when checked and not matched (q = NULL), - * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) - */ - int dyn_dir = MATCH_UNKNOWN; - ipfw_dyn_rule *q = NULL; - struct ip_fw_chain *chain = &V_layer3_chain; - - /* - * We store in ulp a pointer to the upper layer protocol header. - * In the ipv4 case this is easy to determine from the header, - * but for ipv6 we might have some additional headers in the middle. - * ulp is NULL if not found. - */ - void *ulp = NULL; /* upper layer protocol pointer. */ - - /* XXX ipv6 variables */ - int is_ipv6 = 0; - uint8_t icmp6_type = 0; - uint16_t ext_hd = 0; /* bits vector for extension header filtering */ - /* end of ipv6 variables */ - - int is_ipv4 = 0; - - int done = 0; /* flag to exit the outer loop */ - - if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready)) - return (IP_FW_PASS); /* accept */ - - dst_ip.s_addr = 0; /* make sure it is initialized */ - src_ip.s_addr = 0; /* make sure it is initialized */ - pktlen = m->m_pkthdr.len; - args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */ - proto = args->f_id.proto = 0; /* mark f_id invalid */ - /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */ - -/* - * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, - * then it sets p to point at the offset "len" in the mbuf. WARNING: the - * pointer might become stale after other pullups (but we never use it - * this way). - */ -#define PULLUP_TO(_len, p, T) PULLUP_LEN(_len, p, sizeof(T)) -#define PULLUP_LEN(_len, p, T) \ -do { \ - int x = (_len) + T; \ - if ((m)->m_len < x) { \ - args->m = m = m_pullup(m, x); \ - if (m == NULL) \ - goto pullup_failed; \ - } \ - p = (mtod(m, char *) + (_len)); \ -} while (0) - - /* - * if we have an ether header, - */ - if (args->eh) - etype = ntohs(args->eh->ether_type); - - /* Identify IP packets and fill up variables. */ - if (pktlen >= sizeof(struct ip6_hdr) && - (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) { - struct ip6_hdr *ip6 = (struct ip6_hdr *)ip; - is_ipv6 = 1; - args->f_id.addr_type = 6; - hlen = sizeof(struct ip6_hdr); - proto = ip6->ip6_nxt; - - /* Search extension headers to find upper layer protocols */ - while (ulp == NULL && offset == 0) { - switch (proto) { - case IPPROTO_ICMPV6: - PULLUP_TO(hlen, ulp, struct icmp6_hdr); - icmp6_type = ICMP6(ulp)->icmp6_type; - break; - - case IPPROTO_TCP: - PULLUP_TO(hlen, ulp, struct tcphdr); - dst_port = TCP(ulp)->th_dport; - src_port = TCP(ulp)->th_sport; - /* save flags for dynamic rules */ - args->f_id._flags = TCP(ulp)->th_flags; - break; - - case IPPROTO_SCTP: - PULLUP_TO(hlen, ulp, struct sctphdr); - src_port = SCTP(ulp)->src_port; - dst_port = SCTP(ulp)->dest_port; - break; - - case IPPROTO_UDP: - PULLUP_TO(hlen, ulp, struct udphdr); - dst_port = UDP(ulp)->uh_dport; - src_port = UDP(ulp)->uh_sport; - break; - - case IPPROTO_HOPOPTS: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_hbh); - ext_hd |= EXT_HOPOPTS; - hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; - proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; - ulp = NULL; - break; - - case IPPROTO_ROUTING: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_rthdr); - switch (((struct ip6_rthdr *)ulp)->ip6r_type) { - case 0: - ext_hd |= EXT_RTHDR0; - break; - case 2: - ext_hd |= EXT_RTHDR2; - break; - default: - if (V_fw_verbose) - printf("IPFW2: IPV6 - Unknown " - "Routing Header type(%d)\n", - ((struct ip6_rthdr *) - ulp)->ip6r_type); - if (V_fw_deny_unknown_exthdrs) - return (IP_FW_DENY); - break; - } - ext_hd |= EXT_ROUTING; - hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; - proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; - ulp = NULL; - break; - - case IPPROTO_FRAGMENT: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_frag); - ext_hd |= EXT_FRAGMENT; - hlen += sizeof (struct ip6_frag); - proto = ((struct ip6_frag *)ulp)->ip6f_nxt; - offset = ((struct ip6_frag *)ulp)->ip6f_offlg & - IP6F_OFF_MASK; - ip6f_mf = ((struct ip6_frag *)ulp)->ip6f_offlg & - IP6F_MORE_FRAG; - if (V_fw_permit_single_frag6 == 0 && - offset == 0 && ip6f_mf == 0) { - if (V_fw_verbose) - printf("IPFW2: IPV6 - Invalid " - "Fragment Header\n"); - if (V_fw_deny_unknown_exthdrs) - return (IP_FW_DENY); - break; - } - args->f_id.extra = - ntohl(((struct ip6_frag *)ulp)->ip6f_ident); - ulp = NULL; - break; - - case IPPROTO_DSTOPTS: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_hbh); - ext_hd |= EXT_DSTOPTS; - hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; - proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; - ulp = NULL; - break; - - case IPPROTO_AH: /* RFC 2402 */ - PULLUP_TO(hlen, ulp, struct ip6_ext); - ext_hd |= EXT_AH; - hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2; - proto = ((struct ip6_ext *)ulp)->ip6e_nxt; - ulp = NULL; - break; - - case IPPROTO_ESP: /* RFC 2406 */ - PULLUP_TO(hlen, ulp, uint32_t); /* SPI, Seq# */ - /* Anything past Seq# is variable length and - * data past this ext. header is encrypted. */ - ext_hd |= EXT_ESP; - break; - - case IPPROTO_NONE: /* RFC 2460 */ - /* - * Packet ends here, and IPv6 header has - * already been pulled up. If ip6e_len!=0 - * then octets must be ignored. - */ - ulp = ip; /* non-NULL to get out of loop. */ - break; - - case IPPROTO_OSPFIGP: - /* XXX OSPF header check? */ - PULLUP_TO(hlen, ulp, struct ip6_ext); - break; - - case IPPROTO_PIM: - /* XXX PIM header check? */ - PULLUP_TO(hlen, ulp, struct pim); - break; - - case IPPROTO_CARP: - PULLUP_TO(hlen, ulp, struct carp_header); - if (((struct carp_header *)ulp)->carp_version != - CARP_VERSION) - return (IP_FW_DENY); - if (((struct carp_header *)ulp)->carp_type != - CARP_ADVERTISEMENT) - return (IP_FW_DENY); - break; - - case IPPROTO_IPV6: /* RFC 2893 */ - PULLUP_TO(hlen, ulp, struct ip6_hdr); - break; - - case IPPROTO_IPV4: /* RFC 2893 */ - PULLUP_TO(hlen, ulp, struct ip); - break; - - default: - if (V_fw_verbose) - printf("IPFW2: IPV6 - Unknown " - "Extension Header(%d), ext_hd=%x\n", - proto, ext_hd); - if (V_fw_deny_unknown_exthdrs) - return (IP_FW_DENY); - PULLUP_TO(hlen, ulp, struct ip6_ext); - break; - } /*switch */ - } - ip = mtod(m, struct ip *); - ip6 = (struct ip6_hdr *)ip; - args->f_id.src_ip6 = ip6->ip6_src; - args->f_id.dst_ip6 = ip6->ip6_dst; - args->f_id.src_ip = 0; - args->f_id.dst_ip = 0; - args->f_id.flow_id6 = ntohl(ip6->ip6_flow); - } else if (pktlen >= sizeof(struct ip) && - (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) { - is_ipv4 = 1; - hlen = ip->ip_hl << 2; - args->f_id.addr_type = 4; - - /* - * Collect parameters into local variables for faster matching. - */ - proto = ip->ip_p; - src_ip = ip->ip_src; - dst_ip = ip->ip_dst; - offset = ntohs(ip->ip_off) & IP_OFFMASK; - iplen = ntohs(ip->ip_len); - pktlen = iplen < pktlen ? iplen : pktlen; - - if (offset == 0) { - switch (proto) { - case IPPROTO_TCP: - PULLUP_TO(hlen, ulp, struct tcphdr); - dst_port = TCP(ulp)->th_dport; - src_port = TCP(ulp)->th_sport; - /* save flags for dynamic rules */ - args->f_id._flags = TCP(ulp)->th_flags; - break; - - case IPPROTO_SCTP: - PULLUP_TO(hlen, ulp, struct sctphdr); - src_port = SCTP(ulp)->src_port; - dst_port = SCTP(ulp)->dest_port; - break; - - case IPPROTO_UDP: - PULLUP_TO(hlen, ulp, struct udphdr); - dst_port = UDP(ulp)->uh_dport; - src_port = UDP(ulp)->uh_sport; - break; - - case IPPROTO_ICMP: - PULLUP_TO(hlen, ulp, struct icmphdr); - //args->f_id.flags = ICMP(ulp)->icmp_type; - break; - - default: - break; - } - } - - ip = mtod(m, struct ip *); - args->f_id.src_ip = ntohl(src_ip.s_addr); - args->f_id.dst_ip = ntohl(dst_ip.s_addr); - } -#undef PULLUP_TO - if (proto) { /* we may have port numbers, store them */ - args->f_id.proto = proto; - args->f_id.src_port = src_port = ntohs(src_port); - args->f_id.dst_port = dst_port = ntohs(dst_port); - } - - IPFW_RLOCK(chain); - if (! V_ipfw_vnet_ready) { /* shutting down, leave NOW. */ - IPFW_RUNLOCK(chain); - return (IP_FW_PASS); /* accept */ - } - if (args->rule.slot) { - /* - * Packet has already been tagged as a result of a previous - * match on rule args->rule aka args->rule_id (PIPE, QUEUE, - * REASS, NETGRAPH, DIVERT/TEE...) - * Validate the slot and continue from the next one - * if still present, otherwise do a lookup. - */ - f_pos = (args->rule.chain_id == chain->id) ? - args->rule.slot : - ipfw_find_rule(chain, args->rule.rulenum, - args->rule.rule_id); - } else { - f_pos = 0; - } - - /* - * Now scan the rules, and parse microinstructions for each rule. - * We have two nested loops and an inner switch. Sometimes we - * need to break out of one or both loops, or re-enter one of - * the loops with updated variables. Loop variables are: - * - * f_pos (outer loop) points to the current rule. - * On output it points to the matching rule. - * done (outer loop) is used as a flag to break the loop. - * l (inner loop) residual length of current rule. - * cmd points to the current microinstruction. - * - * We break the inner loop by setting l=0 and possibly - * cmdlen=0 if we don't want to advance cmd. - * We break the outer loop by setting done=1 - * We can restart the inner loop by setting l>0 and f_pos, f, cmd - * as needed. - */ - for (; f_pos < chain->n_rules; f_pos++) { - ipfw_insn *cmd; - uint32_t tablearg = 0; - int l, cmdlen, skip_or; /* skip rest of OR block */ - struct ip_fw *f; - - f = chain->map[f_pos]; - if (V_set_disable & (1 << f->set) ) - continue; - - skip_or = 0; - for (l = f->cmd_len, cmd = f->cmd ; l > 0 ; - l -= cmdlen, cmd += cmdlen) { - int match; - - /* - * check_body is a jump target used when we find a - * CHECK_STATE, and need to jump to the body of - * the target rule. - */ - -/* check_body: */ - cmdlen = F_LEN(cmd); - /* - * An OR block (insn_1 || .. || insn_n) has the - * F_OR bit set in all but the last instruction. - * The first match will set "skip_or", and cause - * the following instructions to be skipped until - * past the one with the F_OR bit clear. - */ - if (skip_or) { /* skip this instruction */ - if ((cmd->len & F_OR) == 0) - skip_or = 0; /* next one is good */ - continue; - } - match = 0; /* set to 1 if we succeed */ - - switch (cmd->opcode) { - /* - * The first set of opcodes compares the packet's - * fields with some pattern, setting 'match' if a - * match is found. At the end of the loop there is - * logic to deal with F_NOT and F_OR flags associated - * with the opcode. - */ - case O_NOP: - match = 1; - break; - - case O_FORWARD_MAC: - printf("ipfw: opcode %d unimplemented\n", - cmd->opcode); - break; - - case O_GID: - case O_UID: - case O_JAIL: - /* - * We only check offset == 0 && proto != 0, - * as this ensures that we have a - * packet with the ports info. - */ - if (offset != 0) - break; - if (proto == IPPROTO_TCP || - proto == IPPROTO_UDP) - match = check_uidgid( - (ipfw_insn_u32 *)cmd, - args, &ucred_lookup, -#ifdef __FreeBSD__ - &ucred_cache); -#else - (void *)&ucred_cache); -#endif - break; - - case O_RECV: - match = iface_match(m->m_pkthdr.rcvif, - (ipfw_insn_if *)cmd, chain, &tablearg); - break; - - case O_XMIT: - match = iface_match(oif, (ipfw_insn_if *)cmd, - chain, &tablearg); - break; - - case O_VIA: - match = iface_match(oif ? oif : - m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd, - chain, &tablearg); - break; - - case O_MACADDR2: - if (args->eh != NULL) { /* have MAC header */ - u_int32_t *want = (u_int32_t *) - ((ipfw_insn_mac *)cmd)->addr; - u_int32_t *mask = (u_int32_t *) - ((ipfw_insn_mac *)cmd)->mask; - u_int32_t *hdr = (u_int32_t *)args->eh; - - match = - ( want[0] == (hdr[0] & mask[0]) && - want[1] == (hdr[1] & mask[1]) && - want[2] == (hdr[2] & mask[2]) ); - } - break; - - case O_MAC_TYPE: - if (args->eh != NULL) { - u_int16_t *p = - ((ipfw_insn_u16 *)cmd)->ports; - int i; - - for (i = cmdlen - 1; !match && i>0; - i--, p += 2) - match = (etype >= p[0] && - etype <= p[1]); - } - break; - - case O_FRAG: - match = (offset != 0); - break; - - case O_IN: /* "out" is "not in" */ - match = (oif == NULL); - break; - - case O_LAYER2: - match = (args->eh != NULL); - break; - - case O_DIVERTED: - { - /* For diverted packets, args->rule.info - * contains the divert port (in host format) - * reason and direction. - */ - uint32_t i = args->rule.info; - match = (i&IPFW_IS_MASK) == IPFW_IS_DIVERT && - cmd->arg1 & ((i & IPFW_INFO_IN) ? 1 : 2); - } - break; - - case O_PROTO: - /* - * We do not allow an arg of 0 so the - * check of "proto" only suffices. - */ - match = (proto == cmd->arg1); - break; - - case O_IP_SRC: - match = is_ipv4 && - (((ipfw_insn_ip *)cmd)->addr.s_addr == - src_ip.s_addr); - break; - - case O_IP_SRC_LOOKUP: - case O_IP_DST_LOOKUP: - if (is_ipv4) { - uint32_t key = - (cmd->opcode == O_IP_DST_LOOKUP) ? - dst_ip.s_addr : src_ip.s_addr; - uint32_t v = 0; - - if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { - /* generic lookup. The key must be - * in 32bit big-endian format. - */ - v = ((ipfw_insn_u32 *)cmd)->d[1]; - if (v == 0) - key = dst_ip.s_addr; - else if (v == 1) - key = src_ip.s_addr; - else if (v == 6) /* dscp */ - key = (ip->ip_tos >> 2) & 0x3f; - else if (offset != 0) - break; - else if (proto != IPPROTO_TCP && - proto != IPPROTO_UDP) - break; - else if (v == 2) - key = htonl(dst_port); - else if (v == 3) - key = htonl(src_port); - else if (v == 4 || v == 5) { - check_uidgid( - (ipfw_insn_u32 *)cmd, - args, &ucred_lookup, -#ifdef __FreeBSD__ - &ucred_cache); - if (v == 4 /* O_UID */) - key = ucred_cache->cr_uid; - else if (v == 5 /* O_JAIL */) - key = ucred_cache->cr_prison->pr_id; -#else /* !__FreeBSD__ */ - (void *)&ucred_cache); - if (v ==4 /* O_UID */) - key = ucred_cache.uid; - else if (v == 5 /* O_JAIL */) - key = ucred_cache.xid; -#endif /* !__FreeBSD__ */ - key = htonl(key); - } else - break; - } - match = ipfw_lookup_table(chain, - cmd->arg1, key, &v); - if (!match) - break; - if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) - match = - ((ipfw_insn_u32 *)cmd)->d[0] == v; - else - tablearg = v; - } else if (is_ipv6) { - uint32_t v = 0; - void *pkey = (cmd->opcode == O_IP_DST_LOOKUP) ? - &args->f_id.dst_ip6: &args->f_id.src_ip6; - match = ipfw_lookup_table_extended(chain, - cmd->arg1, pkey, &v, - IPFW_TABLE_CIDR); - if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) - match = ((ipfw_insn_u32 *)cmd)->d[0] == v; - if (match) - tablearg = v; - } - break; - - case O_IP_SRC_MASK: - case O_IP_DST_MASK: - if (is_ipv4) { - uint32_t a = - (cmd->opcode == O_IP_DST_MASK) ? - dst_ip.s_addr : src_ip.s_addr; - uint32_t *p = ((ipfw_insn_u32 *)cmd)->d; - int i = cmdlen-1; - - for (; !match && i>0; i-= 2, p+= 2) - match = (p[0] == (a & p[1])); - } - break; - - case O_IP_SRC_ME: - if (is_ipv4) { - struct ifnet *tif; - - INADDR_TO_IFP(src_ip, tif); - match = (tif != NULL); - break; - } -#ifdef INET6 - /* FALLTHROUGH */ - case O_IP6_SRC_ME: - match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6); -#endif - break; - - case O_IP_DST_SET: - case O_IP_SRC_SET: - if (is_ipv4) { - u_int32_t *d = (u_int32_t *)(cmd+1); - u_int32_t addr = - cmd->opcode == O_IP_DST_SET ? - args->f_id.dst_ip : - args->f_id.src_ip; - - if (addr < d[0]) - break; - addr -= d[0]; /* subtract base */ - match = (addr < cmd->arg1) && - ( d[ 1 + (addr>>5)] & - (1<<(addr & 0x1f)) ); - } - break; - - case O_IP_DST: - match = is_ipv4 && - (((ipfw_insn_ip *)cmd)->addr.s_addr == - dst_ip.s_addr); - break; - - case O_IP_DST_ME: - if (is_ipv4) { - struct ifnet *tif; - - INADDR_TO_IFP(dst_ip, tif); - match = (tif != NULL); - break; - } -#ifdef INET6 - /* FALLTHROUGH */ - case O_IP6_DST_ME: - match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6); -#endif - break; - - - case O_IP_SRCPORT: - case O_IP_DSTPORT: - /* - * offset == 0 && proto != 0 is enough - * to guarantee that we have a - * packet with port info. - */ - if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP) - && offset == 0) { - u_int16_t x = - (cmd->opcode == O_IP_SRCPORT) ? - src_port : dst_port ; - u_int16_t *p = - ((ipfw_insn_u16 *)cmd)->ports; - int i; - - for (i = cmdlen - 1; !match && i>0; - i--, p += 2) - match = (x>=p[0] && x<=p[1]); - } - break; - - case O_ICMPTYPE: - match = (offset == 0 && proto==IPPROTO_ICMP && - icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); - break; - -#ifdef INET6 - case O_ICMP6TYPE: - match = is_ipv6 && offset == 0 && - proto==IPPROTO_ICMPV6 && - icmp6type_match( - ICMP6(ulp)->icmp6_type, - (ipfw_insn_u32 *)cmd); - break; -#endif /* INET6 */ - - case O_IPOPT: - match = (is_ipv4 && - ipopts_match(ip, cmd) ); - break; - - case O_IPVER: - match = (is_ipv4 && - cmd->arg1 == ip->ip_v); - break; - - case O_IPID: - case O_IPLEN: - case O_IPTTL: - if (is_ipv4) { /* only for IP packets */ - uint16_t x; - uint16_t *p; - int i; - - if (cmd->opcode == O_IPLEN) - x = iplen; - else if (cmd->opcode == O_IPTTL) - x = ip->ip_ttl; - else /* must be IPID */ - x = ntohs(ip->ip_id); - if (cmdlen == 1) { - match = (cmd->arg1 == x); - break; - } - /* otherwise we have ranges */ - p = ((ipfw_insn_u16 *)cmd)->ports; - i = cmdlen - 1; - for (; !match && i>0; i--, p += 2) - match = (x >= p[0] && x <= p[1]); - } - break; - - case O_IPPRECEDENCE: - match = (is_ipv4 && - (cmd->arg1 == (ip->ip_tos & 0xe0)) ); - break; - - case O_IPTOS: - match = (is_ipv4 && - flags_match(cmd, ip->ip_tos)); - break; - - case O_TCPDATALEN: - if (proto == IPPROTO_TCP && offset == 0) { - struct tcphdr *tcp; - uint16_t x; - uint16_t *p; - int i; - - tcp = TCP(ulp); - x = iplen - - ((ip->ip_hl + tcp->th_off) << 2); - if (cmdlen == 1) { - match = (cmd->arg1 == x); - break; - } - /* otherwise we have ranges */ - p = ((ipfw_insn_u16 *)cmd)->ports; - i = cmdlen - 1; - for (; !match && i>0; i--, p += 2) - match = (x >= p[0] && x <= p[1]); - } - break; - - case O_TCPFLAGS: - match = (proto == IPPROTO_TCP && offset == 0 && - flags_match(cmd, TCP(ulp)->th_flags)); - break; - - case O_TCPOPTS: - PULLUP_LEN(hlen, ulp, (TCP(ulp)->th_off << 2)); - match = (proto == IPPROTO_TCP && offset == 0 && - tcpopts_match(TCP(ulp), cmd)); - break; - - case O_TCPSEQ: - match = (proto == IPPROTO_TCP && offset == 0 && - ((ipfw_insn_u32 *)cmd)->d[0] == - TCP(ulp)->th_seq); - break; - - case O_TCPACK: - match = (proto == IPPROTO_TCP && offset == 0 && - ((ipfw_insn_u32 *)cmd)->d[0] == - TCP(ulp)->th_ack); - break; - - case O_TCPWIN: - if (proto == IPPROTO_TCP && offset == 0) { - uint16_t x; - uint16_t *p; - int i; - - x = ntohs(TCP(ulp)->th_win); - if (cmdlen == 1) { - match = (cmd->arg1 == x); - break; - } - /* Otherwise we have ranges. */ - p = ((ipfw_insn_u16 *)cmd)->ports; - i = cmdlen - 1; - for (; !match && i > 0; i--, p += 2) - match = (x >= p[0] && x <= p[1]); - } - break; - - case O_ESTAB: - /* reject packets which have SYN only */ - /* XXX should i also check for TH_ACK ? */ - match = (proto == IPPROTO_TCP && offset == 0 && - (TCP(ulp)->th_flags & - (TH_RST | TH_ACK | TH_SYN)) != TH_SYN); - break; - - case O_ALTQ: { - struct pf_mtag *at; - struct m_tag *mtag; - ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; - - /* - * ALTQ uses mbuf tags from another - * packet filtering system - pf(4). - * We allocate a tag in its format - * and fill it in, pretending to be pf(4). - */ - match = 1; - at = pf_find_mtag(m); - if (at != NULL && at->qid != 0) - break; - mtag = m_tag_get(PACKET_TAG_PF, - sizeof(struct pf_mtag), M_NOWAIT | M_ZERO); - if (mtag == NULL) { - /* - * Let the packet fall back to the - * default ALTQ. - */ - break; - } - m_tag_prepend(m, mtag); - at = (struct pf_mtag *)(mtag + 1); - at->qid = altq->qid; - at->hdr = ip; - break; - } - - case O_LOG: - ipfw_log(f, hlen, args, m, - oif, offset | ip6f_mf, tablearg, ip); - match = 1; - break; - - case O_PROB: - match = (random()<((ipfw_insn_u32 *)cmd)->d[0]); - break; - - case O_VERREVPATH: - /* Outgoing packets automatically pass/match */ - match = ((oif != NULL) || - (m->m_pkthdr.rcvif == NULL) || - ( -#ifdef INET6 - is_ipv6 ? - verify_path6(&(args->f_id.src_ip6), - m->m_pkthdr.rcvif, args->f_id.fib) : -#endif - verify_path(src_ip, m->m_pkthdr.rcvif, - args->f_id.fib))); - break; - - case O_VERSRCREACH: - /* Outgoing packets automatically pass/match */ - match = (hlen > 0 && ((oif != NULL) || -#ifdef INET6 - is_ipv6 ? - verify_path6(&(args->f_id.src_ip6), - NULL, args->f_id.fib) : -#endif - verify_path(src_ip, NULL, args->f_id.fib))); - break; - - case O_ANTISPOOF: - /* Outgoing packets automatically pass/match */ - if (oif == NULL && hlen > 0 && - ( (is_ipv4 && in_localaddr(src_ip)) -#ifdef INET6 - || (is_ipv6 && - in6_localaddr(&(args->f_id.src_ip6))) -#endif - )) - match = -#ifdef INET6 - is_ipv6 ? verify_path6( - &(args->f_id.src_ip6), - m->m_pkthdr.rcvif, - args->f_id.fib) : -#endif - verify_path(src_ip, - m->m_pkthdr.rcvif, - args->f_id.fib); - else - match = 1; - break; - - case O_IPSEC: -#ifdef IPSEC - match = (m_tag_find(m, - PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL); -#endif - /* otherwise no match */ - break; - -#ifdef INET6 - case O_IP6_SRC: - match = is_ipv6 && - IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6, - &((ipfw_insn_ip6 *)cmd)->addr6); - break; - - case O_IP6_DST: - match = is_ipv6 && - IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6, - &((ipfw_insn_ip6 *)cmd)->addr6); - break; - case O_IP6_SRC_MASK: - case O_IP6_DST_MASK: - if (is_ipv6) { - int i = cmdlen - 1; - struct in6_addr p; - struct in6_addr *d = - &((ipfw_insn_ip6 *)cmd)->addr6; - - for (; !match && i > 0; d += 2, - i -= F_INSN_SIZE(struct in6_addr) - * 2) { - p = (cmd->opcode == - O_IP6_SRC_MASK) ? - args->f_id.src_ip6: - args->f_id.dst_ip6; - APPLY_MASK(&p, &d[1]); - match = - IN6_ARE_ADDR_EQUAL(&d[0], - &p); - } - } - break; - - case O_FLOW6ID: - match = is_ipv6 && - flow6id_match(args->f_id.flow_id6, - (ipfw_insn_u32 *) cmd); - break; - - case O_EXT_HDR: - match = is_ipv6 && - (ext_hd & ((ipfw_insn *) cmd)->arg1); - break; - - case O_IP6: - match = is_ipv6; - break; -#endif - - case O_IP4: - match = is_ipv4; - break; - - case O_TAG: { - struct m_tag *mtag; - uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - - /* Packet is already tagged with this tag? */ - mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL); - - /* We have `untag' action when F_NOT flag is - * present. And we must remove this mtag from - * mbuf and reset `match' to zero (`match' will - * be inversed later). - * Otherwise we should allocate new mtag and - * push it into mbuf. - */ - if (cmd->len & F_NOT) { /* `untag' action */ - if (mtag != NULL) - m_tag_delete(m, mtag); - match = 0; - } else { - if (mtag == NULL) { - mtag = m_tag_alloc( MTAG_IPFW, - tag, 0, M_NOWAIT); - if (mtag != NULL) - m_tag_prepend(m, mtag); - } - match = 1; - } - break; - } - - case O_FIB: /* try match the specified fib */ - if (args->f_id.fib == cmd->arg1) - match = 1; - break; - - case O_SOCKARG: { - struct inpcb *inp = args->inp; - struct inpcbinfo *pi; - - if (is_ipv6) /* XXX can we remove this ? */ - break; - - if (proto == IPPROTO_TCP) - pi = &V_tcbinfo; - else if (proto == IPPROTO_UDP) - pi = &V_udbinfo; - else - break; - - /* - * XXXRW: so_user_cookie should almost - * certainly be inp_user_cookie? - */ - - /* For incomming packet, lookup up the - inpcb using the src/dest ip/port tuple */ - if (inp == NULL) { - inp = in_pcblookup(pi, - src_ip, htons(src_port), - dst_ip, htons(dst_port), - INPLOOKUP_RLOCKPCB, NULL); - if (inp != NULL) { - tablearg = - inp->inp_socket->so_user_cookie; - if (tablearg) - match = 1; - INP_RUNLOCK(inp); - } - } else { - if (inp->inp_socket) { - tablearg = - inp->inp_socket->so_user_cookie; - if (tablearg) - match = 1; - } - } - break; - } - - case O_TAGGED: { - struct m_tag *mtag; - uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - - if (cmdlen == 1) { - match = m_tag_locate(m, MTAG_IPFW, - tag, NULL) != NULL; - break; - } - - /* we have ranges */ - for (mtag = m_tag_first(m); - mtag != NULL && !match; - mtag = m_tag_next(m, mtag)) { - uint16_t *p; - int i; - - if (mtag->m_tag_cookie != MTAG_IPFW) - continue; - - p = ((ipfw_insn_u16 *)cmd)->ports; - i = cmdlen - 1; - for(; !match && i > 0; i--, p += 2) - match = - mtag->m_tag_id >= p[0] && - mtag->m_tag_id <= p[1]; - } - break; - } - - /* - * The second set of opcodes represents 'actions', - * i.e. the terminal part of a rule once the packet - * matches all previous patterns. - * Typically there is only one action for each rule, - * and the opcode is stored at the end of the rule - * (but there are exceptions -- see below). - * - * In general, here we set retval and terminate the - * outer loop (would be a 'break 3' in some language, - * but we need to set l=0, done=1) - * - * Exceptions: - * O_COUNT and O_SKIPTO actions: - * instead of terminating, we jump to the next rule - * (setting l=0), or to the SKIPTO target (setting - * f/f_len, cmd and l as needed), respectively. - * - * O_TAG, O_LOG and O_ALTQ action parameters: - * perform some action and set match = 1; - * - * O_LIMIT and O_KEEP_STATE: these opcodes are - * not real 'actions', and are stored right - * before the 'action' part of the rule. - * These opcodes try to install an entry in the - * state tables; if successful, we continue with - * the next opcode (match=1; break;), otherwise - * the packet must be dropped (set retval, - * break loops with l=0, done=1) - * - * O_PROBE_STATE and O_CHECK_STATE: these opcodes - * cause a lookup of the state table, and a jump - * to the 'action' part of the parent rule - * if an entry is found, or - * (CHECK_STATE only) a jump to the next rule if - * the entry is not found. - * The result of the lookup is cached so that - * further instances of these opcodes become NOPs. - * The jump to the next rule is done by setting - * l=0, cmdlen=0. - */ - case O_LIMIT: - case O_KEEP_STATE: - if (ipfw_install_state(f, - (ipfw_insn_limit *)cmd, args, tablearg)) { - /* error or limit violation */ - retval = IP_FW_DENY; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - } - match = 1; - break; - - case O_PROBE_STATE: - case O_CHECK_STATE: - /* - * dynamic rules are checked at the first - * keep-state or check-state occurrence, - * with the result being stored in dyn_dir. - * The compiler introduces a PROBE_STATE - * instruction for us when we have a - * KEEP_STATE (because PROBE_STATE needs - * to be run first). - */ - if (dyn_dir == MATCH_UNKNOWN && - (q = ipfw_lookup_dyn_rule(&args->f_id, - &dyn_dir, proto == IPPROTO_TCP ? - TCP(ulp) : NULL)) - != NULL) { - /* - * Found dynamic entry, update stats - * and jump to the 'action' part of - * the parent rule by setting - * f, cmd, l and clearing cmdlen. - */ - q->pcnt++; - q->bcnt += pktlen; - /* XXX we would like to have f_pos - * readily accessible in the dynamic - * rule, instead of having to - * lookup q->rule. - */ - f = q->rule; - f_pos = ipfw_find_rule(chain, - f->rulenum, f->id); - cmd = ACTION_PTR(f); - l = f->cmd_len - f->act_ofs; - ipfw_dyn_unlock(); - cmdlen = 0; - match = 1; - break; - } - /* - * Dynamic entry not found. If CHECK_STATE, - * skip to next rule, if PROBE_STATE just - * ignore and continue with next opcode. - */ - if (cmd->opcode == O_CHECK_STATE) - l = 0; /* exit inner loop */ - match = 1; - break; - - case O_ACCEPT: - retval = 0; /* accept */ - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - - case O_PIPE: - case O_QUEUE: - set_match(args, f_pos, chain); - args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - if (cmd->opcode == O_PIPE) - args->rule.info |= IPFW_IS_PIPE; - if (V_fw_one_pass) - args->rule.info |= IPFW_ONEPASS; - retval = IP_FW_DUMMYNET; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - - case O_DIVERT: - case O_TEE: - if (args->eh) /* not on layer 2 */ - break; - /* otherwise this is terminal */ - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - retval = (cmd->opcode == O_DIVERT) ? - IP_FW_DIVERT : IP_FW_TEE; - set_match(args, f_pos, chain); - args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - break; - - case O_COUNT: - f->pcnt++; /* update stats */ - f->bcnt += pktlen; - f->timestamp = time_uptime; - l = 0; /* exit inner loop */ - break; - - case O_SKIPTO: - f->pcnt++; /* update stats */ - f->bcnt += pktlen; - f->timestamp = time_uptime; - /* If possible use cached f_pos (in f->next_rule), - * whose version is written in f->next_rule - * (horrible hacks to avoid changing the ABI). - */ - if (cmd->arg1 != IP_FW_TABLEARG && - (uintptr_t)f->x_next == chain->id) { - f_pos = (uintptr_t)f->next_rule; - } else { - int i = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - /* make sure we do not jump backward */ - if (i <= f->rulenum) - i = f->rulenum + 1; - f_pos = ipfw_find_rule(chain, i, 0); - /* update the cache */ - if (cmd->arg1 != IP_FW_TABLEARG) { - f->next_rule = - (void *)(uintptr_t)f_pos; - f->x_next = - (void *)(uintptr_t)chain->id; - } - } - /* - * Skip disabled rules, and re-enter - * the inner loop with the correct - * f_pos, f, l and cmd. - * Also clear cmdlen and skip_or - */ - for (; f_pos < chain->n_rules - 1 && - (V_set_disable & - (1 << chain->map[f_pos]->set)); - f_pos++) - ; - /* Re-enter the inner loop at the skipto rule. */ - f = chain->map[f_pos]; - l = f->cmd_len; - cmd = f->cmd; - match = 1; - cmdlen = 0; - skip_or = 0; - continue; - break; /* not reached */ - - case O_CALLRETURN: { - /* - * Implementation of `subroutine' call/return, - * in the stack carried in an mbuf tag. This - * is different from `skipto' in that any call - * address is possible (`skipto' must prevent - * backward jumps to avoid endless loops). - * We have `return' action when F_NOT flag is - * present. The `m_tag_id' field is used as - * stack pointer. - */ - struct m_tag *mtag; - uint16_t jmpto, *stack; - -#define IS_CALL ((cmd->len & F_NOT) == 0) -#define IS_RETURN ((cmd->len & F_NOT) != 0) - /* - * Hand-rolled version of m_tag_locate() with - * wildcard `type'. - * If not already tagged, allocate new tag. - */ - mtag = m_tag_first(m); - while (mtag != NULL) { - if (mtag->m_tag_cookie == - MTAG_IPFW_CALL) - break; - mtag = m_tag_next(m, mtag); - } - if (mtag == NULL && IS_CALL) { - mtag = m_tag_alloc(MTAG_IPFW_CALL, 0, - IPFW_CALLSTACK_SIZE * - sizeof(uint16_t), M_NOWAIT); - if (mtag != NULL) - m_tag_prepend(m, mtag); - } - - /* - * On error both `call' and `return' just - * continue with next rule. - */ - if (IS_RETURN && (mtag == NULL || - mtag->m_tag_id == 0)) { - l = 0; /* exit inner loop */ - break; - } - if (IS_CALL && (mtag == NULL || - mtag->m_tag_id >= IPFW_CALLSTACK_SIZE)) { - printf("ipfw: call stack error, " - "go to next rule\n"); - l = 0; /* exit inner loop */ - break; - } - - f->pcnt++; /* update stats */ - f->bcnt += pktlen; - f->timestamp = time_uptime; - stack = (uint16_t *)(mtag + 1); - - /* - * The `call' action may use cached f_pos - * (in f->next_rule), whose version is written - * in f->next_rule. - * The `return' action, however, doesn't have - * fixed jump address in cmd->arg1 and can't use - * cache. - */ - if (IS_CALL) { - stack[mtag->m_tag_id] = f->rulenum; - mtag->m_tag_id++; - if (cmd->arg1 != IP_FW_TABLEARG && - (uintptr_t)f->x_next == chain->id) { - f_pos = (uintptr_t)f->next_rule; - } else { - jmpto = (cmd->arg1 == - IP_FW_TABLEARG) ? tablearg: - cmd->arg1; - f_pos = ipfw_find_rule(chain, - jmpto, 0); - /* update the cache */ - if (cmd->arg1 != - IP_FW_TABLEARG) { - f->next_rule = - (void *)(uintptr_t) - f_pos; - f->x_next = - (void *)(uintptr_t) - chain->id; - } - } - } else { /* `return' action */ - mtag->m_tag_id--; - jmpto = stack[mtag->m_tag_id] + 1; - f_pos = ipfw_find_rule(chain, jmpto, 0); - } - - /* - * Skip disabled rules, and re-enter - * the inner loop with the correct - * f_pos, f, l and cmd. - * Also clear cmdlen and skip_or - */ - for (; f_pos < chain->n_rules - 1 && - (V_set_disable & - (1 << chain->map[f_pos]->set)); f_pos++) - ; - /* Re-enter the inner loop at the dest rule. */ - f = chain->map[f_pos]; - l = f->cmd_len; - cmd = f->cmd; - cmdlen = 0; - skip_or = 0; - continue; - break; /* NOTREACHED */ - } -#undef IS_CALL -#undef IS_RETURN - - case O_REJECT: - /* - * Drop the packet and send a reject notice - * if the packet is not ICMP (or is an ICMP - * query), and it is not multicast/broadcast. - */ - if (hlen > 0 && is_ipv4 && offset == 0 && - (proto != IPPROTO_ICMP || - is_icmp_query(ICMP(ulp))) && - !(m->m_flags & (M_BCAST|M_MCAST)) && - !IN_MULTICAST(ntohl(dst_ip.s_addr))) { - send_reject(args, cmd->arg1, iplen, ip); - m = args->m; - } - /* FALLTHROUGH */ -#ifdef INET6 - case O_UNREACH6: - if (hlen > 0 && is_ipv6 && - ((offset & IP6F_OFF_MASK) == 0) && - (proto != IPPROTO_ICMPV6 || - (is_icmp6_query(icmp6_type) == 1)) && - !(m->m_flags & (M_BCAST|M_MCAST)) && - !IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) { - send_reject6( - args, cmd->arg1, hlen, - (struct ip6_hdr *)ip); - m = args->m; - } - /* FALLTHROUGH */ -#endif - case O_DENY: - retval = IP_FW_DENY; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - - case O_FORWARD_IP: - if (args->eh) /* not valid on layer2 pkts */ - break; - if (q == NULL || q->rule != f || - dyn_dir == MATCH_FORWARD) { - struct sockaddr_in *sa; - sa = &(((ipfw_insn_sa *)cmd)->sa); - if (sa->sin_addr.s_addr == INADDR_ANY) { - bcopy(sa, &args->hopstore, - sizeof(*sa)); - args->hopstore.sin_addr.s_addr = - htonl(tablearg); - args->next_hop = &args->hopstore; - } else { - args->next_hop = sa; - } - } - retval = IP_FW_PASS; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - -#ifdef INET6 - case O_FORWARD_IP6: - if (args->eh) /* not valid on layer2 pkts */ - break; - if (q == NULL || q->rule != f || - dyn_dir == MATCH_FORWARD) { - struct sockaddr_in6 *sin6; - - sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); - args->next_hop6 = sin6; - } - retval = IP_FW_PASS; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; -#endif - - case O_NETGRAPH: - case O_NGTEE: - set_match(args, f_pos, chain); - args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - if (V_fw_one_pass) - args->rule.info |= IPFW_ONEPASS; - retval = (cmd->opcode == O_NETGRAPH) ? - IP_FW_NETGRAPH : IP_FW_NGTEE; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - - case O_SETFIB: { - uint32_t fib; - - f->pcnt++; /* update stats */ - f->bcnt += pktlen; - f->timestamp = time_uptime; - fib = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg: - cmd->arg1; - if (fib >= rt_numfibs) - fib = 0; - M_SETFIB(m, fib); - args->f_id.fib = fib; - l = 0; /* exit inner loop */ - break; - } - - case O_NAT: - if (!IPFW_NAT_LOADED) { - retval = IP_FW_DENY; - } else { - struct cfg_nat *t; - int nat_id; - - set_match(args, f_pos, chain); - /* Check if this is 'global' nat rule */ - if (cmd->arg1 == 0) { - retval = ipfw_nat_ptr(args, NULL, m); - l = 0; - done = 1; - break; - } - t = ((ipfw_insn_nat *)cmd)->nat; - if (t == NULL) { - nat_id = (cmd->arg1 == IP_FW_TABLEARG) ? - tablearg : cmd->arg1; - t = (*lookup_nat_ptr)(&chain->nat, nat_id); - - if (t == NULL) { - retval = IP_FW_DENY; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - } - if (cmd->arg1 != IP_FW_TABLEARG) - ((ipfw_insn_nat *)cmd)->nat = t; - } - retval = ipfw_nat_ptr(args, t, m); - } - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ - break; - - case O_REASS: { - int ip_off; - - f->pcnt++; - f->bcnt += pktlen; - l = 0; /* in any case exit inner loop */ - ip_off = ntohs(ip->ip_off); - - /* if not fragmented, go to next rule */ - if ((ip_off & (IP_MF | IP_OFFMASK)) == 0) - break; - /* - * ip_reass() expects len & off in host - * byte order. - */ - SET_HOST_IPLEN(ip); - - args->m = m = ip_reass(m); - - /* - * do IP header checksum fixup. - */ - if (m == NULL) { /* fragment got swallowed */ - retval = IP_FW_DENY; - } else { /* good, packet complete */ - int hlen; - - ip = mtod(m, struct ip *); - hlen = ip->ip_hl << 2; - SET_NET_IPLEN(ip); - ip->ip_sum = 0; - if (hlen == sizeof(struct ip)) - ip->ip_sum = in_cksum_hdr(ip); - else - ip->ip_sum = in_cksum(m, hlen); - retval = IP_FW_REASS; - set_match(args, f_pos, chain); - } - done = 1; /* exit outer loop */ - break; - } - - default: - panic("-- unknown opcode %d\n", cmd->opcode); - } /* end of switch() on opcodes */ - /* - * if we get here with l=0, then match is irrelevant. - */ - - if (cmd->len & F_NOT) - match = !match; - - if (match) { - if (cmd->len & F_OR) - skip_or = 1; - } else { - if (!(cmd->len & F_OR)) /* not an OR block, */ - break; /* try next rule */ - } - - } /* end of inner loop, scan opcodes */ -#undef PULLUP_LEN - - if (done) - break; - -/* next_rule:; */ /* try next rule */ - - } /* end of outer for, scan rules */ - - if (done) { - struct ip_fw *rule = chain->map[f_pos]; - /* Update statistics */ - rule->pcnt++; - rule->bcnt += pktlen; - rule->timestamp = time_uptime; - } else { - retval = IP_FW_DENY; - printf("ipfw: ouch!, skip past end of rules, denying packet\n"); - } - IPFW_RUNLOCK(chain); -#ifdef __FreeBSD__ - if (ucred_cache != NULL) - crfree(ucred_cache); -#endif - return (retval); - -pullup_failed: - if (V_fw_verbose) - printf("ipfw: pullup failed\n"); - return (IP_FW_DENY); -} - -/* - * Set maximum number of tables that can be used in given VNET ipfw instance. - */ -#ifdef SYSCTL_NODE -static int -sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS) -{ - int error; - unsigned int ntables; - - ntables = V_fw_tables_max; - - error = sysctl_handle_int(oidp, &ntables, 0, req); - /* Read operation or some error */ - if ((error != 0) || (req->newptr == NULL)) - return (error); - - return (ipfw_resize_tables(&V_layer3_chain, ntables)); -} -#endif -/* - * Module and VNET glue - */ - -/* - * Stuff that must be initialised only on boot or module load - */ -static int -ipfw_init(void) -{ - int error = 0; - - ipfw_dyn_attach(); - /* - * Only print out this stuff the first time around, - * when called from the sysinit code. - */ - printf("ipfw2 " -#ifdef INET6 - "(+ipv6) " -#endif - "initialized, divert %s, nat %s, " - "rule-based forwarding " -#ifdef IPFIREWALL_FORWARD - "enabled, " -#else - "disabled, " -#endif - "default to %s, logging ", -#ifdef IPDIVERT - "enabled", -#else - "loadable", -#endif -#ifdef IPFIREWALL_NAT - "enabled", -#else - "loadable", -#endif - default_to_accept ? "accept" : "deny"); - - /* - * Note: V_xxx variables can be accessed here but the vnet specific - * initializer may not have been called yet for the VIMAGE case. - * Tuneables will have been processed. We will print out values for - * the default vnet. - * XXX This should all be rationalized AFTER 8.0 - */ - if (V_fw_verbose == 0) - printf("disabled\n"); - else if (V_verbose_limit == 0) - printf("unlimited\n"); - else - printf("limited to %d packets/entry by default\n", - V_verbose_limit); - - /* Check user-supplied table count for validness */ - if (default_fw_tables > IPFW_TABLES_MAX) - default_fw_tables = IPFW_TABLES_MAX; - - ipfw_log_bpf(1); /* init */ - return (error); -} - -/* - * Called for the removal of the last instance only on module unload. - */ -static void -ipfw_destroy(void) -{ - - ipfw_log_bpf(0); /* uninit */ - ipfw_dyn_detach(); - printf("IP firewall unloaded\n"); -} - -/* - * Stuff that must be initialized for every instance - * (including the first of course). - */ -static int -vnet_ipfw_init(const void *unused) -{ - int error; - struct ip_fw *rule = NULL; - struct ip_fw_chain *chain; - - chain = &V_layer3_chain; - - /* First set up some values that are compile time options */ - V_autoinc_step = 100; /* bounded to 1..1000 in add_rule() */ - V_fw_deny_unknown_exthdrs = 1; -#ifdef IPFIREWALL_VERBOSE - V_fw_verbose = 1; -#endif -#ifdef IPFIREWALL_VERBOSE_LIMIT - V_verbose_limit = IPFIREWALL_VERBOSE_LIMIT; -#endif -#ifdef IPFIREWALL_NAT - LIST_INIT(&chain->nat); -#endif - - /* insert the default rule and create the initial map */ - chain->n_rules = 1; - chain->static_len = sizeof(struct ip_fw); - chain->map = malloc(sizeof(struct ip_fw *), M_IPFW, M_WAITOK | M_ZERO); - if (chain->map) - rule = malloc(chain->static_len, M_IPFW, M_WAITOK | M_ZERO); - - /* Set initial number of tables */ - V_fw_tables_max = default_fw_tables; - error = ipfw_init_tables(chain); - if (error) { - printf("ipfw2: setting up tables failed\n"); - free(chain->map, M_IPFW); - free(rule, M_IPFW); - return (ENOSPC); - } - - /* fill and insert the default rule */ - rule->act_ofs = 0; - rule->rulenum = IPFW_DEFAULT_RULE; - rule->cmd_len = 1; - rule->set = RESVD_SET; - rule->cmd[0].len = 1; - rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY; - chain->rules = chain->default_rule = chain->map[0] = rule; - chain->id = rule->id = 1; - - IPFW_LOCK_INIT(chain); - ipfw_dyn_init(); - - /* First set up some values that are compile time options */ - V_ipfw_vnet_ready = 1; /* Open for business */ - - /* - * Hook the sockopt handler and pfil hooks for ipv4 and ipv6. - * Even if the latter two fail we still keep the module alive - * because the sockopt and layer2 paths are still useful. - * ipfw[6]_hook return 0 on success, ENOENT on failure, - * so we can ignore the exact return value and just set a flag. - * - * Note that V_fw[6]_enable are manipulated by a SYSCTL_PROC so - * changes in the underlying (per-vnet) variables trigger - * immediate hook()/unhook() calls. - * In layer2 we have the same behaviour, except that V_ether_ipfw - * is checked on each packet because there are no pfil hooks. - */ - V_ip_fw_ctl_ptr = ipfw_ctl; - error = ipfw_attach_hooks(1); - return (error); -} - -/* - * Called for the removal of each instance. - */ -static int -vnet_ipfw_uninit(const void *unused) -{ - struct ip_fw *reap, *rule; - struct ip_fw_chain *chain = &V_layer3_chain; - int i; - - V_ipfw_vnet_ready = 0; /* tell new callers to go away */ - /* - * disconnect from ipv4, ipv6, layer2 and sockopt. - * Then grab, release and grab again the WLOCK so we make - * sure the update is propagated and nobody will be in. - */ - (void)ipfw_attach_hooks(0 /* detach */); - V_ip_fw_ctl_ptr = NULL; - IPFW_UH_WLOCK(chain); - IPFW_UH_WUNLOCK(chain); - IPFW_UH_WLOCK(chain); - - IPFW_WLOCK(chain); - ipfw_dyn_uninit(0); /* run the callout_drain */ - IPFW_WUNLOCK(chain); - - ipfw_destroy_tables(chain); - reap = NULL; - IPFW_WLOCK(chain); - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - rule->x_next = reap; - reap = rule; - } - if (chain->map) - free(chain->map, M_IPFW); - IPFW_WUNLOCK(chain); - IPFW_UH_WUNLOCK(chain); - if (reap != NULL) - ipfw_reap_rules(reap); - IPFW_LOCK_DESTROY(chain); - ipfw_dyn_uninit(1); /* free the remaining parts */ - return 0; -} - -/* - * Module event handler. - * In general we have the choice of handling most of these events by the - * event handler or by the (VNET_)SYS(UN)INIT handlers. I have chosen to - * use the SYSINIT handlers as they are more capable of expressing the - * flow of control during module and vnet operations, so this is just - * a skeleton. Note there is no SYSINIT equivalent of the module - * SHUTDOWN handler, but we don't have anything to do in that case anyhow. - */ -static int -ipfw_modevent(module_t mod, int type, void *unused) -{ - int err = 0; - - switch (type) { - case MOD_LOAD: - /* Called once at module load or - * system boot if compiled in. */ - break; - case MOD_QUIESCE: - /* Called before unload. May veto unloading. */ - break; - case MOD_UNLOAD: - /* Called during unload. */ - break; - case MOD_SHUTDOWN: - /* Called during system shutdown. */ - break; - default: - err = EOPNOTSUPP; - break; - } - return err; -} - -static moduledata_t ipfwmod = { - "ipfw", - ipfw_modevent, - 0 -}; - -/* Define startup order. */ -#define IPFW_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN -#define IPFW_MODEVENT_ORDER (SI_ORDER_ANY - 255) /* On boot slot in here. */ -#define IPFW_MODULE_ORDER (IPFW_MODEVENT_ORDER + 1) /* A little later. */ -#define IPFW_VNET_ORDER (IPFW_MODEVENT_ORDER + 2) /* Later still. */ - -DECLARE_MODULE(ipfw, ipfwmod, IPFW_SI_SUB_FIREWALL, IPFW_MODEVENT_ORDER); -MODULE_VERSION(ipfw, 2); -/* should declare some dependencies here */ - -/* - * Starting up. Done in order after ipfwmod() has been called. - * VNET_SYSINIT is also called for each existing vnet and each new vnet. - */ -SYSINIT(ipfw_init, IPFW_SI_SUB_FIREWALL, IPFW_MODULE_ORDER, - ipfw_init, NULL); -VNET_SYSINIT(vnet_ipfw_init, IPFW_SI_SUB_FIREWALL, IPFW_VNET_ORDER, - vnet_ipfw_init, NULL); - -/* - * Closing up shop. These are done in REVERSE ORDER, but still - * after ipfwmod() has been called. Not called on reboot. - * VNET_SYSUNINIT is also called for each exiting vnet as it exits. - * or when the module is unloaded. - */ -SYSUNINIT(ipfw_destroy, IPFW_SI_SUB_FIREWALL, IPFW_MODULE_ORDER, - ipfw_destroy, NULL); -VNET_SYSUNINIT(vnet_ipfw_uninit, IPFW_SI_SUB_FIREWALL, IPFW_VNET_ORDER, - vnet_ipfw_uninit, NULL); -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw_dynamic.c b/sys/netinet/ipfw/ip_fw_dynamic.c deleted file mode 100644 index edf7639e893d3..0000000000000 --- a/sys/netinet/ipfw/ip_fw_dynamic.c +++ /dev/null @@ -1,1244 +0,0 @@ -/*- - * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa - * - * 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 -__FBSDID("$FreeBSD$"); - -#define DEB(x) -#define DDB(x) x - -/* - * Dynamic rule support for ipfw - */ - -#include "opt_ipfw.h" -#include "opt_inet.h" -#ifndef INET -#error IPFIREWALL requires INET. -#endif /* INET */ -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for ETHERTYPE_IP */ -#include -#include - -#include -#include -#include /* ip_defttl */ -#include -#include -#include -#include - -#include /* IN6_ARE_ADDR_EQUAL */ -#ifdef INET6 -#include -#include -#endif - -#include /* XXX for in_cksum */ - -#ifdef MAC -#include -#endif - -/* - * Description of dynamic rules. - * - * Dynamic rules are stored in lists accessed through a hash table - * (ipfw_dyn_v) whose size is curr_dyn_buckets. This value can - * be modified through the sysctl variable dyn_buckets which is - * updated when the table becomes empty. - * - * XXX currently there is only one list, ipfw_dyn. - * - * When a packet is received, its address fields are first masked - * with the mask defined for the rule, then hashed, then matched - * against the entries in the corresponding list. - * Dynamic rules can be used for different purposes: - * + stateful rules; - * + enforcing limits on the number of sessions; - * + in-kernel NAT (not implemented yet) - * - * The lifetime of dynamic rules is regulated by dyn_*_lifetime, - * measured in seconds and depending on the flags. - * - * The total number of dynamic rules is stored in dyn_count. - * The max number of dynamic rules is dyn_max. When we reach - * the maximum number of rules we do not create anymore. This is - * done to avoid consuming too much memory, but also too much - * time when searching on each packet (ideally, we should try instead - * to put a limit on the length of the list on each bucket...). - * - * Each dynamic rule holds a pointer to the parent ipfw rule so - * we know what action to perform. Dynamic rules are removed when - * the parent rule is deleted. XXX we should make them survive. - * - * There are some limitations with dynamic rules -- we do not - * obey the 'randomized match', and we do not do multiple - * passes through the firewall. XXX check the latter!!! - */ - -/* - * Static variables followed by global ones - */ -static VNET_DEFINE(ipfw_dyn_rule **, ipfw_dyn_v); -static VNET_DEFINE(u_int32_t, dyn_buckets); -static VNET_DEFINE(u_int32_t, curr_dyn_buckets); -static VNET_DEFINE(struct callout, ipfw_timeout); -#define V_ipfw_dyn_v VNET(ipfw_dyn_v) -#define V_dyn_buckets VNET(dyn_buckets) -#define V_curr_dyn_buckets VNET(curr_dyn_buckets) -#define V_ipfw_timeout VNET(ipfw_timeout) - -static uma_zone_t ipfw_dyn_rule_zone; -#ifndef __FreeBSD__ -DEFINE_SPINLOCK(ipfw_dyn_mtx); -#else -static struct mtx ipfw_dyn_mtx; /* mutex guarding dynamic rules */ -#endif - -#define IPFW_DYN_LOCK_INIT() \ - mtx_init(&ipfw_dyn_mtx, "IPFW dynamic rules", NULL, MTX_DEF) -#define IPFW_DYN_LOCK_DESTROY() mtx_destroy(&ipfw_dyn_mtx) -#define IPFW_DYN_LOCK() mtx_lock(&ipfw_dyn_mtx) -#define IPFW_DYN_UNLOCK() mtx_unlock(&ipfw_dyn_mtx) -#define IPFW_DYN_LOCK_ASSERT() mtx_assert(&ipfw_dyn_mtx, MA_OWNED) - -void -ipfw_dyn_unlock(void) -{ - IPFW_DYN_UNLOCK(); -} - -/* - * Timeouts for various events in handing dynamic rules. - */ -static VNET_DEFINE(u_int32_t, dyn_ack_lifetime); -static VNET_DEFINE(u_int32_t, dyn_syn_lifetime); -static VNET_DEFINE(u_int32_t, dyn_fin_lifetime); -static VNET_DEFINE(u_int32_t, dyn_rst_lifetime); -static VNET_DEFINE(u_int32_t, dyn_udp_lifetime); -static VNET_DEFINE(u_int32_t, dyn_short_lifetime); - -#define V_dyn_ack_lifetime VNET(dyn_ack_lifetime) -#define V_dyn_syn_lifetime VNET(dyn_syn_lifetime) -#define V_dyn_fin_lifetime VNET(dyn_fin_lifetime) -#define V_dyn_rst_lifetime VNET(dyn_rst_lifetime) -#define V_dyn_udp_lifetime VNET(dyn_udp_lifetime) -#define V_dyn_short_lifetime VNET(dyn_short_lifetime) - -/* - * Keepalives are sent if dyn_keepalive is set. They are sent every - * dyn_keepalive_period seconds, in the last dyn_keepalive_interval - * seconds of lifetime of a rule. - * dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower - * than dyn_keepalive_period. - */ - -static VNET_DEFINE(u_int32_t, dyn_keepalive_interval); -static VNET_DEFINE(u_int32_t, dyn_keepalive_period); -static VNET_DEFINE(u_int32_t, dyn_keepalive); - -#define V_dyn_keepalive_interval VNET(dyn_keepalive_interval) -#define V_dyn_keepalive_period VNET(dyn_keepalive_period) -#define V_dyn_keepalive VNET(dyn_keepalive) - -static VNET_DEFINE(u_int32_t, dyn_count); /* # of dynamic rules */ -static VNET_DEFINE(u_int32_t, dyn_max); /* max # of dynamic rules */ - -#define V_dyn_count VNET(dyn_count) -#define V_dyn_max VNET(dyn_max) - -#ifdef SYSCTL_NODE - -SYSBEGIN(f2) - -SYSCTL_DECL(_net_inet_ip_fw); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, - CTLFLAG_RW, &VNET_NAME(dyn_buckets), 0, - "Number of dyn. buckets"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets, - CTLFLAG_RD, &VNET_NAME(curr_dyn_buckets), 0, - "Current Number of dyn. buckets"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_count, - CTLFLAG_RD, &VNET_NAME(dyn_count), 0, - "Number of dyn. rules"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_max, - CTLFLAG_RW, &VNET_NAME(dyn_max), 0, - "Max number of dyn. rules"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime, - CTLFLAG_RW, &VNET_NAME(dyn_ack_lifetime), 0, - "Lifetime of dyn. rules for acks"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime, - CTLFLAG_RW, &VNET_NAME(dyn_syn_lifetime), 0, - "Lifetime of dyn. rules for syn"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime, - CTLFLAG_RW, &VNET_NAME(dyn_fin_lifetime), 0, - "Lifetime of dyn. rules for fin"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime, - CTLFLAG_RW, &VNET_NAME(dyn_rst_lifetime), 0, - "Lifetime of dyn. rules for rst"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime, - CTLFLAG_RW, &VNET_NAME(dyn_udp_lifetime), 0, - "Lifetime of dyn. rules for UDP"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, - CTLFLAG_RW, &VNET_NAME(dyn_short_lifetime), 0, - "Lifetime of dyn. rules for other situations"); -SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, - CTLFLAG_RW, &VNET_NAME(dyn_keepalive), 0, - "Enable keepalives for dyn. rules"); - -SYSEND - -#endif /* SYSCTL_NODE */ - - -static __inline int -hash_packet6(struct ipfw_flow_id *id) -{ - u_int32_t i; - i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ - (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ - (id->src_ip6.__u6_addr.__u6_addr32[2]) ^ - (id->src_ip6.__u6_addr.__u6_addr32[3]) ^ - (id->dst_port) ^ (id->src_port); - return i; -} - -/* - * IMPORTANT: the hash function for dynamic rules must be commutative - * in source and destination (ip,port), because rules are bidirectional - * and we want to find both in the same bucket. - */ -static __inline int -hash_packet(struct ipfw_flow_id *id) -{ - u_int32_t i; - -#ifdef INET6 - if (IS_IP6_FLOW_ID(id)) - i = hash_packet6(id); - else -#endif /* INET6 */ - i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); - i &= (V_curr_dyn_buckets - 1); - return i; -} - -static __inline void -unlink_dyn_rule_print(struct ipfw_flow_id *id) -{ - struct in_addr da; -#ifdef INET6 - char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; -#else - char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; -#endif - -#ifdef INET6 - if (IS_IP6_FLOW_ID(id)) { - ip6_sprintf(src, &id->src_ip6); - ip6_sprintf(dst, &id->dst_ip6); - } else -#endif - { - da.s_addr = htonl(id->src_ip); - inet_ntop(AF_INET, &da, src, sizeof(src)); - da.s_addr = htonl(id->dst_ip); - inet_ntop(AF_INET, &da, dst, sizeof(dst)); - } - printf("ipfw: unlink entry %s %d -> %s %d, %d left\n", - src, id->src_port, dst, id->dst_port, V_dyn_count - 1); -} - -/** - * unlink a dynamic rule from a chain. prev is a pointer to - * the previous one, q is a pointer to the rule to delete, - * head is a pointer to the head of the queue. - * Modifies q and potentially also head. - */ -#define UNLINK_DYN_RULE(prev, head, q) { \ - ipfw_dyn_rule *old_q = q; \ - \ - /* remove a refcount to the parent */ \ - if (q->dyn_type == O_LIMIT) \ - q->parent->count--; \ - DEB(unlink_dyn_rule_print(&q->id);) \ - if (prev != NULL) \ - prev->next = q = q->next; \ - else \ - head = q = q->next; \ - V_dyn_count--; \ - uma_zfree(ipfw_dyn_rule_zone, old_q); } - -#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) - -/** - * Remove dynamic rules pointing to "rule", or all of them if rule == NULL. - * - * If keep_me == NULL, rules are deleted even if not expired, - * otherwise only expired rules are removed. - * - * The value of the second parameter is also used to point to identify - * a rule we absolutely do not want to remove (e.g. because we are - * holding a reference to it -- this is the case with O_LIMIT_PARENT - * rules). The pointer is only used for comparison, so any non-null - * value will do. - */ -static void -remove_dyn_rule(struct ip_fw *rule, ipfw_dyn_rule *keep_me) -{ - static u_int32_t last_remove = 0; - -#define FORCE (keep_me == NULL) - - ipfw_dyn_rule *prev, *q; - int i, pass = 0, max_pass = 0; - - IPFW_DYN_LOCK_ASSERT(); - - if (V_ipfw_dyn_v == NULL || V_dyn_count == 0) - return; - /* do not expire more than once per second, it is useless */ - if (!FORCE && last_remove == time_uptime) - return; - last_remove = time_uptime; - - /* - * because O_LIMIT refer to parent rules, during the first pass only - * remove child and mark any pending LIMIT_PARENT, and remove - * them in a second pass. - */ -next_pass: - for (i = 0 ; i < V_curr_dyn_buckets ; i++) { - for (prev=NULL, q = V_ipfw_dyn_v[i] ; q ; ) { - /* - * Logic can become complex here, so we split tests. - */ - if (q == keep_me) - goto next; - if (rule != NULL && rule != q->rule) - goto next; /* not the one we are looking for */ - if (q->dyn_type == O_LIMIT_PARENT) { - /* - * handle parent in the second pass, - * record we need one. - */ - max_pass = 1; - if (pass == 0) - goto next; - if (FORCE && q->count != 0 ) { - /* XXX should not happen! */ - printf("ipfw: OUCH! cannot remove rule," - " count %d\n", q->count); - } - } else { - if (!FORCE && - !TIME_LEQ( q->expire, time_uptime )) - goto next; - } - if (q->dyn_type != O_LIMIT_PARENT || !q->count) { - UNLINK_DYN_RULE(prev, V_ipfw_dyn_v[i], q); - continue; - } -next: - prev=q; - q=q->next; - } - } - if (pass++ < max_pass) - goto next_pass; -} - -void -ipfw_remove_dyn_children(struct ip_fw *rule) -{ - IPFW_DYN_LOCK(); - remove_dyn_rule(rule, NULL /* force removal */); - IPFW_DYN_UNLOCK(); -} - -/* - * Lookup a dynamic rule, locked version. - */ -static ipfw_dyn_rule * -lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction, - struct tcphdr *tcp) -{ - /* - * Stateful ipfw extensions. - * Lookup into dynamic session queue. - */ -#define MATCH_REVERSE 0 -#define MATCH_FORWARD 1 -#define MATCH_NONE 2 -#define MATCH_UNKNOWN 3 - int i, dir = MATCH_NONE; - ipfw_dyn_rule *prev, *q = NULL; - - IPFW_DYN_LOCK_ASSERT(); - - if (V_ipfw_dyn_v == NULL) - goto done; /* not found */ - i = hash_packet(pkt); - for (prev = NULL, q = V_ipfw_dyn_v[i]; q != NULL;) { - if (q->dyn_type == O_LIMIT_PARENT && q->count) - goto next; - if (TIME_LEQ(q->expire, time_uptime)) { /* expire entry */ - UNLINK_DYN_RULE(prev, V_ipfw_dyn_v[i], q); - continue; - } - if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT) - goto next; - - if (IS_IP6_FLOW_ID(pkt)) { - if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.src_ip6) && - IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.dst_ip6) && - pkt->src_port == q->id.src_port && - pkt->dst_port == q->id.dst_port) { - dir = MATCH_FORWARD; - break; - } - if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.dst_ip6) && - IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.src_ip6) && - pkt->src_port == q->id.dst_port && - pkt->dst_port == q->id.src_port) { - dir = MATCH_REVERSE; - break; - } - } else { - if (pkt->src_ip == q->id.src_ip && - pkt->dst_ip == q->id.dst_ip && - pkt->src_port == q->id.src_port && - pkt->dst_port == q->id.dst_port) { - dir = MATCH_FORWARD; - break; - } - if (pkt->src_ip == q->id.dst_ip && - pkt->dst_ip == q->id.src_ip && - pkt->src_port == q->id.dst_port && - pkt->dst_port == q->id.src_port) { - dir = MATCH_REVERSE; - break; - } - } -next: - prev = q; - q = q->next; - } - if (q == NULL) - goto done; /* q = NULL, not found */ - - if (prev != NULL) { /* found and not in front */ - prev->next = q->next; - q->next = V_ipfw_dyn_v[i]; - V_ipfw_dyn_v[i] = q; - } - if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */ - uint32_t ack; - u_char flags = pkt->_flags & (TH_FIN | TH_SYN | TH_RST); - -#define BOTH_SYN (TH_SYN | (TH_SYN << 8)) -#define BOTH_FIN (TH_FIN | (TH_FIN << 8)) -#define TCP_FLAGS (TH_FLAGS | (TH_FLAGS << 8)) -#define ACK_FWD 0x10000 /* fwd ack seen */ -#define ACK_REV 0x20000 /* rev ack seen */ - - q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8); - switch (q->state & TCP_FLAGS) { - case TH_SYN: /* opening */ - q->expire = time_uptime + V_dyn_syn_lifetime; - break; - - case BOTH_SYN: /* move to established */ - case BOTH_SYN | TH_FIN: /* one side tries to close */ - case BOTH_SYN | (TH_FIN << 8): -#define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0) - if (tcp == NULL) - break; - - ack = ntohl(tcp->th_ack); - if (dir == MATCH_FORWARD) { - if (q->ack_fwd == 0 || - _SEQ_GE(ack, q->ack_fwd)) { - q->ack_fwd = ack; - q->state |= ACK_FWD; - } - } else { - if (q->ack_rev == 0 || - _SEQ_GE(ack, q->ack_rev)) { - q->ack_rev = ack; - q->state |= ACK_REV; - } - } - if ((q->state & (ACK_FWD | ACK_REV)) == - (ACK_FWD | ACK_REV)) { - q->expire = time_uptime + V_dyn_ack_lifetime; - q->state &= ~(ACK_FWD | ACK_REV); - } - break; - - case BOTH_SYN | BOTH_FIN: /* both sides closed */ - if (V_dyn_fin_lifetime >= V_dyn_keepalive_period) - V_dyn_fin_lifetime = V_dyn_keepalive_period - 1; - q->expire = time_uptime + V_dyn_fin_lifetime; - break; - - default: -#if 0 - /* - * reset or some invalid combination, but can also - * occur if we use keep-state the wrong way. - */ - if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0) - printf("invalid state: 0x%x\n", q->state); -#endif - if (V_dyn_rst_lifetime >= V_dyn_keepalive_period) - V_dyn_rst_lifetime = V_dyn_keepalive_period - 1; - q->expire = time_uptime + V_dyn_rst_lifetime; - break; - } - } else if (pkt->proto == IPPROTO_UDP) { - q->expire = time_uptime + V_dyn_udp_lifetime; - } else { - /* other protocols */ - q->expire = time_uptime + V_dyn_short_lifetime; - } -done: - if (match_direction != NULL) - *match_direction = dir; - return (q); -} - -ipfw_dyn_rule * -ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, - struct tcphdr *tcp) -{ - ipfw_dyn_rule *q; - - IPFW_DYN_LOCK(); - q = lookup_dyn_rule_locked(pkt, match_direction, tcp); - if (q == NULL) - IPFW_DYN_UNLOCK(); - /* NB: return table locked when q is not NULL */ - return q; -} - -static void -realloc_dynamic_table(void) -{ - IPFW_DYN_LOCK_ASSERT(); - - /* - * Try reallocation, make sure we have a power of 2 and do - * not allow more than 64k entries. In case of overflow, - * default to 1024. - */ - - if (V_dyn_buckets > 65536) - V_dyn_buckets = 1024; - if ((V_dyn_buckets & (V_dyn_buckets-1)) != 0) { /* not a power of 2 */ - V_dyn_buckets = V_curr_dyn_buckets; /* reset */ - return; - } - V_curr_dyn_buckets = V_dyn_buckets; - if (V_ipfw_dyn_v != NULL) - free(V_ipfw_dyn_v, M_IPFW); - for (;;) { - V_ipfw_dyn_v = malloc(V_curr_dyn_buckets * sizeof(ipfw_dyn_rule *), - M_IPFW, M_NOWAIT | M_ZERO); - if (V_ipfw_dyn_v != NULL || V_curr_dyn_buckets <= 2) - break; - V_curr_dyn_buckets /= 2; - } -} - -/** - * Install state of type 'type' for a dynamic session. - * The hash table contains two type of rules: - * - regular rules (O_KEEP_STATE) - * - rules for sessions with limited number of sess per user - * (O_LIMIT). When they are created, the parent is - * increased by 1, and decreased on delete. In this case, - * the third parameter is the parent rule and not the chain. - * - "parent" rules for the above (O_LIMIT_PARENT). - */ -static ipfw_dyn_rule * -add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule) -{ - ipfw_dyn_rule *r; - int i; - - IPFW_DYN_LOCK_ASSERT(); - - if (V_ipfw_dyn_v == NULL || - (V_dyn_count == 0 && V_dyn_buckets != V_curr_dyn_buckets)) { - realloc_dynamic_table(); - if (V_ipfw_dyn_v == NULL) - return NULL; /* failed ! */ - } - i = hash_packet(id); - - r = uma_zalloc(ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); - if (r == NULL) { - printf ("ipfw: sorry cannot allocate state\n"); - return NULL; - } - - /* increase refcount on parent, and set pointer */ - if (dyn_type == O_LIMIT) { - ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule; - if ( parent->dyn_type != O_LIMIT_PARENT) - panic("invalid parent"); - parent->count++; - r->parent = parent; - rule = parent->rule; - } - - r->id = *id; - r->expire = time_uptime + V_dyn_syn_lifetime; - r->rule = rule; - r->dyn_type = dyn_type; - r->pcnt = r->bcnt = 0; - r->count = 0; - - r->bucket = i; - r->next = V_ipfw_dyn_v[i]; - V_ipfw_dyn_v[i] = r; - V_dyn_count++; - DEB({ - struct in_addr da; -#ifdef INET6 - char src[INET6_ADDRSTRLEN]; - char dst[INET6_ADDRSTRLEN]; -#else - char src[INET_ADDRSTRLEN]; - char dst[INET_ADDRSTRLEN]; -#endif - -#ifdef INET6 - if (IS_IP6_FLOW_ID(&(r->id))) { - ip6_sprintf(src, &r->id.src_ip6); - ip6_sprintf(dst, &r->id.dst_ip6); - } else -#endif - { - da.s_addr = htonl(r->id.src_ip); - inet_ntop(AF_INET, &da, src, sizeof(src)); - da.s_addr = htonl(r->id.dst_ip); - inet_ntop(AF_INET, &da, dst, sizeof(dst)); - } - printf("ipfw: add dyn entry ty %d %s %d -> %s %d, total %d\n", - dyn_type, src, r->id.src_port, dst, r->id.dst_port, - V_dyn_count); - }) - return r; -} - -/** - * lookup dynamic parent rule using pkt and rule as search keys. - * If the lookup fails, then install one. - */ -static ipfw_dyn_rule * -lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule) -{ - ipfw_dyn_rule *q; - int i; - - IPFW_DYN_LOCK_ASSERT(); - - if (V_ipfw_dyn_v) { - int is_v6 = IS_IP6_FLOW_ID(pkt); - i = hash_packet( pkt ); - for (q = V_ipfw_dyn_v[i] ; q != NULL ; q=q->next) - if (q->dyn_type == O_LIMIT_PARENT && - rule== q->rule && - pkt->proto == q->id.proto && - pkt->src_port == q->id.src_port && - pkt->dst_port == q->id.dst_port && - ( - (is_v6 && - IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), - &(q->id.src_ip6)) && - IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), - &(q->id.dst_ip6))) || - (!is_v6 && - pkt->src_ip == q->id.src_ip && - pkt->dst_ip == q->id.dst_ip) - ) - ) { - q->expire = time_uptime + V_dyn_short_lifetime; - DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);) - return q; - } - } - return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); -} - -/** - * Install dynamic state for rule type cmd->o.opcode - * - * Returns 1 (failure) if state is not installed because of errors or because - * session limitations are enforced. - */ -int -ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, - struct ip_fw_args *args, uint32_t tablearg) -{ - static int last_log; - ipfw_dyn_rule *q; - struct in_addr da; -#ifdef INET6 - char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2]; -#else - char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; -#endif - - src[0] = '\0'; - dst[0] = '\0'; - - IPFW_DYN_LOCK(); - - DEB( -#ifdef INET6 - if (IS_IP6_FLOW_ID(&(args->f_id))) { - ip6_sprintf(src, &args->f_id.src_ip6); - ip6_sprintf(dst, &args->f_id.dst_ip6); - } else -#endif - { - da.s_addr = htonl(args->f_id.src_ip); - inet_ntop(AF_INET, &da, src, sizeof(src)); - da.s_addr = htonl(args->f_id.dst_ip); - inet_ntop(AF_INET, &da, dst, sizeof(dst)); - } - printf("ipfw: %s: type %d %s %u -> %s %u\n", - __func__, cmd->o.opcode, src, args->f_id.src_port, - dst, args->f_id.dst_port); - src[0] = '\0'; - dst[0] = '\0'; - ) - - q = lookup_dyn_rule_locked(&args->f_id, NULL, NULL); - - if (q != NULL) { /* should never occur */ - DEB( - if (last_log != time_uptime) { - last_log = time_uptime; - printf("ipfw: %s: entry already present, done\n", - __func__); - }) - IPFW_DYN_UNLOCK(); - return (0); - } - - if (V_dyn_count >= V_dyn_max) - /* Run out of slots, try to remove any expired rule. */ - remove_dyn_rule(NULL, (ipfw_dyn_rule *)1); - - if (V_dyn_count >= V_dyn_max) { - if (last_log != time_uptime) { - last_log = time_uptime; - printf("ipfw: %s: Too many dynamic rules\n", __func__); - } - IPFW_DYN_UNLOCK(); - return (1); /* cannot install, notify caller */ - } - - switch (cmd->o.opcode) { - case O_KEEP_STATE: /* bidir rule */ - add_dyn_rule(&args->f_id, O_KEEP_STATE, rule); - break; - - case O_LIMIT: { /* limit number of sessions */ - struct ipfw_flow_id id; - ipfw_dyn_rule *parent; - uint32_t conn_limit; - uint16_t limit_mask = cmd->limit_mask; - - conn_limit = (cmd->conn_limit == IP_FW_TABLEARG) ? - tablearg : cmd->conn_limit; - - DEB( - if (cmd->conn_limit == IP_FW_TABLEARG) - printf("ipfw: %s: O_LIMIT rule, conn_limit: %u " - "(tablearg)\n", __func__, conn_limit); - else - printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", - __func__, conn_limit); - ) - - id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0; - id.proto = args->f_id.proto; - id.addr_type = args->f_id.addr_type; - id.fib = M_GETFIB(args->m); - - if (IS_IP6_FLOW_ID (&(args->f_id))) { - if (limit_mask & DYN_SRC_ADDR) - id.src_ip6 = args->f_id.src_ip6; - if (limit_mask & DYN_DST_ADDR) - id.dst_ip6 = args->f_id.dst_ip6; - } else { - if (limit_mask & DYN_SRC_ADDR) - id.src_ip = args->f_id.src_ip; - if (limit_mask & DYN_DST_ADDR) - id.dst_ip = args->f_id.dst_ip; - } - if (limit_mask & DYN_SRC_PORT) - id.src_port = args->f_id.src_port; - if (limit_mask & DYN_DST_PORT) - id.dst_port = args->f_id.dst_port; - if ((parent = lookup_dyn_parent(&id, rule)) == NULL) { - printf("ipfw: %s: add parent failed\n", __func__); - IPFW_DYN_UNLOCK(); - return (1); - } - - if (parent->count >= conn_limit) { - /* See if we can remove some expired rule. */ - remove_dyn_rule(rule, parent); - if (parent->count >= conn_limit) { - if (V_fw_verbose && last_log != time_uptime) { - last_log = time_uptime; -#ifdef INET6 - /* - * XXX IPv6 flows are not - * supported yet. - */ - if (IS_IP6_FLOW_ID(&(args->f_id))) { - char ip6buf[INET6_ADDRSTRLEN]; - snprintf(src, sizeof(src), - "[%s]", ip6_sprintf(ip6buf, - &args->f_id.src_ip6)); - snprintf(dst, sizeof(dst), - "[%s]", ip6_sprintf(ip6buf, - &args->f_id.dst_ip6)); - } else -#endif - { - da.s_addr = - htonl(args->f_id.src_ip); - inet_ntop(AF_INET, &da, src, - sizeof(src)); - da.s_addr = - htonl(args->f_id.dst_ip); - inet_ntop(AF_INET, &da, dst, - sizeof(dst)); - } - log(LOG_SECURITY | LOG_DEBUG, - "ipfw: %d %s %s:%u -> %s:%u, %s\n", - parent->rule->rulenum, - "drop session", - src, (args->f_id.src_port), - dst, (args->f_id.dst_port), - "too many entries"); - } - IPFW_DYN_UNLOCK(); - return (1); - } - } - add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent); - break; - } - default: - printf("ipfw: %s: unknown dynamic rule type %u\n", - __func__, cmd->o.opcode); - IPFW_DYN_UNLOCK(); - return (1); - } - - /* XXX just set lifetime */ - lookup_dyn_rule_locked(&args->f_id, NULL, NULL); - - IPFW_DYN_UNLOCK(); - return (0); -} - -/* - * Generate a TCP packet, containing either a RST or a keepalive. - * When flags & TH_RST, we are sending a RST packet, because of a - * "reset" action matched the packet. - * Otherwise we are sending a keepalive, and flags & TH_ - * The 'replyto' mbuf is the mbuf being replied to, if any, and is required - * so that MAC can label the reply appropriately. - */ -struct mbuf * -ipfw_send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, - u_int32_t ack, int flags) -{ - struct mbuf *m = NULL; /* stupid compiler */ - int len, dir; - struct ip *h = NULL; /* stupid compiler */ -#ifdef INET6 - struct ip6_hdr *h6 = NULL; -#endif - struct tcphdr *th = NULL; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (NULL); - - M_SETFIB(m, id->fib); -#ifdef MAC - if (replyto != NULL) - mac_netinet_firewall_reply(replyto, m); - else - mac_netinet_firewall_send(m); -#else - (void)replyto; /* don't warn about unused arg */ -#endif - - switch (id->addr_type) { - case 4: - len = sizeof(struct ip) + sizeof(struct tcphdr); - break; -#ifdef INET6 - case 6: - len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); - break; -#endif - default: - /* XXX: log me?!? */ - FREE_PKT(m); - return (NULL); - } - dir = ((flags & (TH_SYN | TH_RST)) == TH_SYN); - - m->m_data += max_linkhdr; - m->m_flags |= M_SKIP_FIREWALL; - m->m_pkthdr.len = m->m_len = len; - m->m_pkthdr.rcvif = NULL; - bzero(m->m_data, len); - - switch (id->addr_type) { - case 4: - h = mtod(m, struct ip *); - - /* prepare for checksum */ - h->ip_p = IPPROTO_TCP; - h->ip_len = htons(sizeof(struct tcphdr)); - if (dir) { - h->ip_src.s_addr = htonl(id->src_ip); - h->ip_dst.s_addr = htonl(id->dst_ip); - } else { - h->ip_src.s_addr = htonl(id->dst_ip); - h->ip_dst.s_addr = htonl(id->src_ip); - } - - th = (struct tcphdr *)(h + 1); - break; -#ifdef INET6 - case 6: - h6 = mtod(m, struct ip6_hdr *); - - /* prepare for checksum */ - h6->ip6_nxt = IPPROTO_TCP; - h6->ip6_plen = htons(sizeof(struct tcphdr)); - if (dir) { - h6->ip6_src = id->src_ip6; - h6->ip6_dst = id->dst_ip6; - } else { - h6->ip6_src = id->dst_ip6; - h6->ip6_dst = id->src_ip6; - } - - th = (struct tcphdr *)(h6 + 1); - break; -#endif - } - - if (dir) { - th->th_sport = htons(id->src_port); - th->th_dport = htons(id->dst_port); - } else { - th->th_sport = htons(id->dst_port); - th->th_dport = htons(id->src_port); - } - th->th_off = sizeof(struct tcphdr) >> 2; - - if (flags & TH_RST) { - if (flags & TH_ACK) { - th->th_seq = htonl(ack); - th->th_flags = TH_RST; - } else { - if (flags & TH_SYN) - seq++; - th->th_ack = htonl(seq); - th->th_flags = TH_RST | TH_ACK; - } - } else { - /* - * Keepalive - use caller provided sequence numbers - */ - th->th_seq = htonl(seq); - th->th_ack = htonl(ack); - th->th_flags = TH_ACK; - } - - switch (id->addr_type) { - case 4: - th->th_sum = in_cksum(m, len); - - /* finish the ip header */ - h->ip_v = 4; - h->ip_hl = sizeof(*h) >> 2; - h->ip_tos = IPTOS_LOWDELAY; - h->ip_off = 0; - /* ip_len must be in host format for ip_output */ - h->ip_len = len; - h->ip_ttl = V_ip_defttl; - h->ip_sum = 0; - break; -#ifdef INET6 - case 6: - th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*h6), - sizeof(struct tcphdr)); - - /* finish the ip6 header */ - h6->ip6_vfc |= IPV6_VERSION; - h6->ip6_hlim = IPV6_DEFHLIM; - break; -#endif - } - - return (m); -} - -/* - * This procedure is only used to handle keepalives. It is invoked - * every dyn_keepalive_period - */ -static void -ipfw_tick(void * vnetx) -{ - struct mbuf *m0, *m, *mnext, **mtailp; -#ifdef INET6 - struct mbuf *m6, **m6_tailp; -#endif - int i; - ipfw_dyn_rule *q; -#ifdef VIMAGE - struct vnet *vp = vnetx; -#endif - - CURVNET_SET(vp); - if (V_dyn_keepalive == 0 || V_ipfw_dyn_v == NULL || V_dyn_count == 0) - goto done; - - /* - * We make a chain of packets to go out here -- not deferring - * until after we drop the IPFW dynamic rule lock would result - * in a lock order reversal with the normal packet input -> ipfw - * call stack. - */ - m0 = NULL; - mtailp = &m0; -#ifdef INET6 - m6 = NULL; - m6_tailp = &m6; -#endif - IPFW_DYN_LOCK(); - for (i = 0 ; i < V_curr_dyn_buckets ; i++) { - for (q = V_ipfw_dyn_v[i] ; q ; q = q->next ) { - if (q->dyn_type == O_LIMIT_PARENT) - continue; - if (q->id.proto != IPPROTO_TCP) - continue; - if ( (q->state & BOTH_SYN) != BOTH_SYN) - continue; - if (TIME_LEQ(time_uptime + V_dyn_keepalive_interval, - q->expire)) - continue; /* too early */ - if (TIME_LEQ(q->expire, time_uptime)) - continue; /* too late, rule expired */ - - m = (q->state & ACK_REV) ? NULL : - ipfw_send_pkt(NULL, &(q->id), q->ack_rev - 1, - q->ack_fwd, TH_SYN); - mnext = (q->state & ACK_FWD) ? NULL : - ipfw_send_pkt(NULL, &(q->id), q->ack_fwd - 1, - q->ack_rev, 0); - - switch (q->id.addr_type) { - case 4: - if (m != NULL) { - *mtailp = m; - mtailp = &(*mtailp)->m_nextpkt; - } - if (mnext != NULL) { - *mtailp = mnext; - mtailp = &(*mtailp)->m_nextpkt; - } - break; -#ifdef INET6 - case 6: - if (m != NULL) { - *m6_tailp = m; - m6_tailp = &(*m6_tailp)->m_nextpkt; - } - if (mnext != NULL) { - *m6_tailp = mnext; - m6_tailp = &(*m6_tailp)->m_nextpkt; - } - break; -#endif - } - } - } - IPFW_DYN_UNLOCK(); - for (m = m0; m != NULL; m = mnext) { - mnext = m->m_nextpkt; - m->m_nextpkt = NULL; - ip_output(m, NULL, NULL, 0, NULL, NULL); - } -#ifdef INET6 - for (m = m6; m != NULL; m = mnext) { - mnext = m->m_nextpkt; - m->m_nextpkt = NULL; - ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); - } -#endif -done: - callout_reset_on(&V_ipfw_timeout, V_dyn_keepalive_period * hz, - ipfw_tick, vnetx, 0); - CURVNET_RESTORE(); -} - -void -ipfw_dyn_attach(void) -{ - ipfw_dyn_rule_zone = uma_zcreate("IPFW dynamic rule", - sizeof(ipfw_dyn_rule), NULL, NULL, NULL, NULL, - UMA_ALIGN_PTR, 0); - - IPFW_DYN_LOCK_INIT(); -} - -void -ipfw_dyn_detach(void) -{ - uma_zdestroy(ipfw_dyn_rule_zone); - IPFW_DYN_LOCK_DESTROY(); -} - -void -ipfw_dyn_init(void) -{ - V_ipfw_dyn_v = NULL; - V_dyn_buckets = 256; /* must be power of 2 */ - V_curr_dyn_buckets = 256; /* must be power of 2 */ - - V_dyn_ack_lifetime = 300; - V_dyn_syn_lifetime = 20; - V_dyn_fin_lifetime = 1; - V_dyn_rst_lifetime = 1; - V_dyn_udp_lifetime = 10; - V_dyn_short_lifetime = 5; - - V_dyn_keepalive_interval = 20; - V_dyn_keepalive_period = 5; - V_dyn_keepalive = 1; /* do send keepalives */ - - V_dyn_max = 4096; /* max # of dynamic rules */ - callout_init(&V_ipfw_timeout, CALLOUT_MPSAFE); - callout_reset_on(&V_ipfw_timeout, hz, ipfw_tick, curvnet, 0); -} - -void -ipfw_dyn_uninit(int pass) -{ - if (pass == 0) - callout_drain(&V_ipfw_timeout); - else { - if (V_ipfw_dyn_v != NULL) - free(V_ipfw_dyn_v, M_IPFW); - } -} - -int -ipfw_dyn_len(void) -{ - return (V_ipfw_dyn_v == NULL) ? 0 : - (V_dyn_count * sizeof(ipfw_dyn_rule)); -} - -void -ipfw_get_dynamic(char **pbp, const char *ep) -{ - ipfw_dyn_rule *p, *last = NULL; - char *bp; - int i; - - if (V_ipfw_dyn_v == NULL) - return; - bp = *pbp; - - IPFW_DYN_LOCK(); - for (i = 0 ; i < V_curr_dyn_buckets; i++) - for (p = V_ipfw_dyn_v[i] ; p != NULL; p = p->next) { - if (bp + sizeof *p <= ep) { - ipfw_dyn_rule *dst = - (ipfw_dyn_rule *)bp; - bcopy(p, dst, sizeof *p); - bcopy(&(p->rule->rulenum), &(dst->rule), - sizeof(p->rule->rulenum)); - /* - * store set number into high word of - * dst->rule pointer. - */ - bcopy(&(p->rule->set), - (char *)&dst->rule + - sizeof(p->rule->rulenum), - sizeof(p->rule->set)); - /* - * store a non-null value in "next". - * The userland code will interpret a - * NULL here as a marker - * for the last dynamic rule. - */ - bcopy(&dst, &dst->next, sizeof(dst)); - last = dst; - dst->expire = - TIME_LEQ(dst->expire, time_uptime) ? - 0 : dst->expire - time_uptime ; - bp += sizeof(ipfw_dyn_rule); - } - } - IPFW_DYN_UNLOCK(); - if (last != NULL) /* mark last dynamic rule */ - bzero(&last->next, sizeof(last)); - *pbp = bp; -} -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw_log.c b/sys/netinet/ipfw/ip_fw_log.c deleted file mode 100644 index c0f8fcd76a831..0000000000000 --- a/sys/netinet/ipfw/ip_fw_log.c +++ /dev/null @@ -1,552 +0,0 @@ -/*- - * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa - * - * 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 -__FBSDID("$FreeBSD$"); - -/* - * Logging support for ipfw - */ - -#include "opt_ipfw.h" -#include "opt_inet.h" -#ifndef INET -#error IPFIREWALL requires INET. -#endif /* INET */ -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for ETHERTYPE_IP */ -#include -#include -#include -#include /* for IFT_PFLOG */ -#include /* for BPF */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#ifdef INET6 -#include /* ip6_sprintf() */ -#endif - -#ifdef MAC -#include -#endif - -/* - * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T - * Other macros just cast void * into the appropriate type - */ -#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) -#define TCP(p) ((struct tcphdr *)(p)) -#define SCTP(p) ((struct sctphdr *)(p)) -#define UDP(p) ((struct udphdr *)(p)) -#define ICMP(p) ((struct icmphdr *)(p)) -#define ICMP6(p) ((struct icmp6_hdr *)(p)) - -#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 -#define SNP(buf) buf, sizeof(buf) - -#ifdef WITHOUT_BPF -void -ipfw_log_bpf(int onoff) -{ -} -#else /* !WITHOUT_BPF */ -static struct ifnet *log_if; /* hook to attach to bpf */ -static struct rwlock log_if_lock; -#define LOGIF_LOCK_INIT(x) rw_init(&log_if_lock, "ipfw log_if lock") -#define LOGIF_LOCK_DESTROY(x) rw_destroy(&log_if_lock) -#define LOGIF_RLOCK(x) rw_rlock(&log_if_lock) -#define LOGIF_RUNLOCK(x) rw_runlock(&log_if_lock) -#define LOGIF_WLOCK(x) rw_wlock(&log_if_lock) -#define LOGIF_WUNLOCK(x) rw_wunlock(&log_if_lock) - -#define IPFWNAME "ipfw" - -/* we use this dummy function for all ifnet callbacks */ -static int -log_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr) -{ - return EINVAL; -} - -static int -ipfw_log_output(struct ifnet *ifp, struct mbuf *m, - struct sockaddr *dst, struct route *ro) -{ - if (m != NULL) - FREE_PKT(m); - return EINVAL; -} - -static void -ipfw_log_start(struct ifnet* ifp) -{ - panic("ipfw_log_start() must not be called"); -} - -static const u_char ipfwbroadcastaddr[6] = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -static int -ipfw_log_clone_match(struct if_clone *ifc, const char *name) -{ - - return (strncmp(name, IPFWNAME, sizeof(IPFWNAME) - 1) == 0); -} - -static int -ipfw_log_clone_create(struct if_clone *ifc, char *name, size_t len, - caddr_t params) -{ - int error; - int unit; - struct ifnet *ifp; - - error = ifc_name2unit(name, &unit); - if (error) - return (error); - - error = ifc_alloc_unit(ifc, &unit); - if (error) - return (error); - - ifp = if_alloc(IFT_PFLOG); - if (ifp == NULL) { - ifc_free_unit(ifc, unit); - return (ENOSPC); - } - ifp->if_dname = IPFWNAME; - ifp->if_dunit = unit; - snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", IPFWNAME, unit); - strlcpy(name, ifp->if_xname, len); - ifp->if_mtu = 65536; - ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_init = (void *)log_dummy; - ifp->if_ioctl = log_dummy; - ifp->if_start = ipfw_log_start; - ifp->if_output = ipfw_log_output; - ifp->if_addrlen = 6; - ifp->if_hdrlen = 14; - ifp->if_broadcastaddr = ipfwbroadcastaddr; - ifp->if_baudrate = IF_Mbps(10); - - LOGIF_WLOCK(); - if (log_if == NULL) - log_if = ifp; - else { - LOGIF_WUNLOCK(); - if_free(ifp); - ifc_free_unit(ifc, unit); - return (EEXIST); - } - LOGIF_WUNLOCK(); - if_attach(ifp); - bpfattach(ifp, DLT_EN10MB, 14); - - return (0); -} - -static int -ipfw_log_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) -{ - int unit; - - if (ifp == NULL) - return (0); - - LOGIF_WLOCK(); - if (log_if != NULL && ifp == log_if) - log_if = NULL; - else { - LOGIF_WUNLOCK(); - return (EINVAL); - } - LOGIF_WUNLOCK(); - - unit = ifp->if_dunit; - bpfdetach(ifp); - if_detach(ifp); - if_free(ifp); - ifc_free_unit(ifc, unit); - - return (0); -} - -static struct if_clone ipfw_log_cloner = IFC_CLONE_INITIALIZER( - IPFWNAME, NULL, IF_MAXUNIT, - NULL, ipfw_log_clone_match, ipfw_log_clone_create, ipfw_log_clone_destroy); - -void -ipfw_log_bpf(int onoff) -{ - - if (onoff) { - LOGIF_LOCK_INIT(); - if_clone_attach(&ipfw_log_cloner); - } else { - if_clone_detach(&ipfw_log_cloner); - LOGIF_LOCK_DESTROY(); - } -} -#endif /* !WITHOUT_BPF */ - -/* - * We enter here when we have a rule with O_LOG. - * XXX this function alone takes about 2Kbytes of code! - */ -void -ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, - struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg, - struct ip *ip) -{ - char *action; - int limit_reached = 0; - char action2[92], proto[128], fragment[32]; - - if (V_fw_verbose == 0) { -#ifndef WITHOUT_BPF - LOGIF_RLOCK(); - if (log_if == NULL || log_if->if_bpf == NULL) { - LOGIF_RUNLOCK(); - return; - } - - if (args->eh) /* layer2, use orig hdr */ - BPF_MTAP2(log_if, args->eh, ETHER_HDR_LEN, m); - else - /* Add fake header. Later we will store - * more info in the header. - */ - BPF_MTAP2(log_if, "DDDDDDSSSSSS\x08\x00", ETHER_HDR_LEN, m); - LOGIF_RUNLOCK(); -#endif /* !WITHOUT_BPF */ - return; - } - /* the old 'log' function */ - fragment[0] = '\0'; - proto[0] = '\0'; - - if (f == NULL) { /* bogus pkt */ - if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit) - return; - V_norule_counter++; - if (V_norule_counter == V_verbose_limit) - limit_reached = V_verbose_limit; - action = "Refuse"; - } else { /* O_LOG is the first action, find the real one */ - ipfw_insn *cmd = ACTION_PTR(f); - ipfw_insn_log *l = (ipfw_insn_log *)cmd; - - if (l->max_log != 0 && l->log_left == 0) - return; - l->log_left--; - if (l->log_left == 0) - limit_reached = l->max_log; - cmd += F_LEN(cmd); /* point to first action */ - if (cmd->opcode == O_ALTQ) { - ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; - - snprintf(SNPARGS(action2, 0), "Altq %d", - altq->qid); - cmd += F_LEN(cmd); - } - if (cmd->opcode == O_PROB) - cmd += F_LEN(cmd); - - if (cmd->opcode == O_TAG) - cmd += F_LEN(cmd); - - action = action2; - switch (cmd->opcode) { - case O_DENY: - action = "Deny"; - break; - - case O_REJECT: - if (cmd->arg1==ICMP_REJECT_RST) - action = "Reset"; - else if (cmd->arg1==ICMP_UNREACH_HOST) - action = "Reject"; - else - snprintf(SNPARGS(action2, 0), "Unreach %d", - cmd->arg1); - break; - - case O_UNREACH6: - if (cmd->arg1==ICMP6_UNREACH_RST) - action = "Reset"; - else - snprintf(SNPARGS(action2, 0), "Unreach %d", - cmd->arg1); - break; - - case O_ACCEPT: - action = "Accept"; - break; - case O_COUNT: - action = "Count"; - break; - case O_DIVERT: - snprintf(SNPARGS(action2, 0), "Divert %d", - cmd->arg1); - break; - case O_TEE: - snprintf(SNPARGS(action2, 0), "Tee %d", - cmd->arg1); - break; - case O_SETFIB: - snprintf(SNPARGS(action2, 0), "SetFib %d", - cmd->arg1); - break; - case O_SKIPTO: - snprintf(SNPARGS(action2, 0), "SkipTo %d", - cmd->arg1); - break; - case O_PIPE: - snprintf(SNPARGS(action2, 0), "Pipe %d", - cmd->arg1); - break; - case O_QUEUE: - snprintf(SNPARGS(action2, 0), "Queue %d", - cmd->arg1); - break; - case O_FORWARD_IP: { - ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd; - int len; - struct in_addr dummyaddr; - if (sa->sa.sin_addr.s_addr == INADDR_ANY) - dummyaddr.s_addr = htonl(tablearg); - else - dummyaddr.s_addr = sa->sa.sin_addr.s_addr; - - len = snprintf(SNPARGS(action2, 0), "Forward to %s", - inet_ntoa(dummyaddr)); - - if (sa->sa.sin_port) - snprintf(SNPARGS(action2, len), ":%d", - sa->sa.sin_port); - } - break; -#ifdef INET6 - case O_FORWARD_IP6: { - char buf[INET6_ADDRSTRLEN]; - ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd; - int len; - - len = snprintf(SNPARGS(action2, 0), "Forward to [%s]", - ip6_sprintf(buf, &sa->sa.sin6_addr)); - - if (sa->sa.sin6_port) - snprintf(SNPARGS(action2, len), ":%u", - sa->sa.sin6_port); - } - break; -#endif - case O_NETGRAPH: - snprintf(SNPARGS(action2, 0), "Netgraph %d", - cmd->arg1); - break; - case O_NGTEE: - snprintf(SNPARGS(action2, 0), "Ngtee %d", - cmd->arg1); - break; - case O_NAT: - action = "Nat"; - break; - case O_REASS: - action = "Reass"; - break; - case O_CALLRETURN: - if (cmd->len & F_NOT) - action = "Return"; - else - snprintf(SNPARGS(action2, 0), "Call %d", - cmd->arg1); - break; - default: - action = "UNKNOWN"; - break; - } - } - - if (hlen == 0) { /* non-ip */ - snprintf(SNPARGS(proto, 0), "MAC"); - - } else { - int len; -#ifdef INET6 - char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2]; -#else - char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; -#endif - struct icmphdr *icmp; - struct tcphdr *tcp; - struct udphdr *udp; -#ifdef INET6 - struct ip6_hdr *ip6 = NULL; - struct icmp6_hdr *icmp6; - u_short ip6f_mf; -#endif - src[0] = '\0'; - dst[0] = '\0'; -#ifdef INET6 - ip6f_mf = offset & IP6F_MORE_FRAG; - offset &= IP6F_OFF_MASK; - - if (IS_IP6_FLOW_ID(&(args->f_id))) { - char ip6buf[INET6_ADDRSTRLEN]; - snprintf(src, sizeof(src), "[%s]", - ip6_sprintf(ip6buf, &args->f_id.src_ip6)); - snprintf(dst, sizeof(dst), "[%s]", - ip6_sprintf(ip6buf, &args->f_id.dst_ip6)); - - ip6 = (struct ip6_hdr *)ip; - tcp = (struct tcphdr *)(((char *)ip) + hlen); - udp = (struct udphdr *)(((char *)ip) + hlen); - } else -#endif - { - tcp = L3HDR(struct tcphdr, ip); - udp = L3HDR(struct udphdr, ip); - - inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src)); - inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst)); - } - - switch (args->f_id.proto) { - case IPPROTO_TCP: - len = snprintf(SNPARGS(proto, 0), "TCP %s", src); - if (offset == 0) - snprintf(SNPARGS(proto, len), ":%d %s:%d", - ntohs(tcp->th_sport), - dst, - ntohs(tcp->th_dport)); - else - snprintf(SNPARGS(proto, len), " %s", dst); - break; - - case IPPROTO_UDP: - len = snprintf(SNPARGS(proto, 0), "UDP %s", src); - if (offset == 0) - snprintf(SNPARGS(proto, len), ":%d %s:%d", - ntohs(udp->uh_sport), - dst, - ntohs(udp->uh_dport)); - else - snprintf(SNPARGS(proto, len), " %s", dst); - break; - - case IPPROTO_ICMP: - icmp = L3HDR(struct icmphdr, ip); - if (offset == 0) - len = snprintf(SNPARGS(proto, 0), - "ICMP:%u.%u ", - icmp->icmp_type, icmp->icmp_code); - else - len = snprintf(SNPARGS(proto, 0), "ICMP "); - len += snprintf(SNPARGS(proto, len), "%s", src); - snprintf(SNPARGS(proto, len), " %s", dst); - break; -#ifdef INET6 - case IPPROTO_ICMPV6: - icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen); - if (offset == 0) - len = snprintf(SNPARGS(proto, 0), - "ICMPv6:%u.%u ", - icmp6->icmp6_type, icmp6->icmp6_code); - else - len = snprintf(SNPARGS(proto, 0), "ICMPv6 "); - len += snprintf(SNPARGS(proto, len), "%s", src); - snprintf(SNPARGS(proto, len), " %s", dst); - break; -#endif - default: - len = snprintf(SNPARGS(proto, 0), "P:%d %s", - args->f_id.proto, src); - snprintf(SNPARGS(proto, len), " %s", dst); - break; - } - -#ifdef INET6 - if (IS_IP6_FLOW_ID(&(args->f_id))) { - if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG)) - snprintf(SNPARGS(fragment, 0), - " (frag %08x:%d@%d%s)", - args->f_id.extra, - ntohs(ip6->ip6_plen) - hlen, - ntohs(offset) << 3, ip6f_mf ? "+" : ""); - } else -#endif - { - int ipoff, iplen; - ipoff = ntohs(ip->ip_off); - iplen = ntohs(ip->ip_len); - if (ipoff & (IP_MF | IP_OFFMASK)) - snprintf(SNPARGS(fragment, 0), - " (frag %d:%d@%d%s)", - ntohs(ip->ip_id), iplen - (ip->ip_hl << 2), - offset << 3, - (ipoff & IP_MF) ? "+" : ""); - } - } -#ifdef __FreeBSD__ - if (oif || m->m_pkthdr.rcvif) - log(LOG_SECURITY | LOG_INFO, - "ipfw: %d %s %s %s via %s%s\n", - f ? f->rulenum : -1, - action, proto, oif ? "out" : "in", - oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname, - fragment); - else -#endif - log(LOG_SECURITY | LOG_INFO, - "ipfw: %d %s %s [no if info]%s\n", - f ? f->rulenum : -1, - action, proto, fragment); - if (limit_reached) - log(LOG_SECURITY | LOG_NOTICE, - "ipfw: limit %d reached on entry %d\n", - limit_reached, f ? f->rulenum : -1); -} -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw_nat.c b/sys/netinet/ipfw/ip_fw_nat.c deleted file mode 100644 index dbeb254cbe7b0..0000000000000 --- a/sys/netinet/ipfw/ip_fw_nat.c +++ /dev/null @@ -1,661 +0,0 @@ -/*- - * Copyright (c) 2008 Paolo Pisati - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* XXX for in_cksum */ - -static VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); -#define V_ifaddr_event_tag VNET(ifaddr_event_tag) - -static void -ifaddr_change(void *arg __unused, struct ifnet *ifp) -{ - struct cfg_nat *ptr; - struct ifaddr *ifa; - struct ip_fw_chain *chain; - - chain = &V_layer3_chain; - IPFW_WLOCK(chain); - /* Check every nat entry... */ - LIST_FOREACH(ptr, &chain->nat, _next) { - /* ...using nic 'ifp->if_xname' as dynamic alias address. */ - if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) - continue; - if_addr_rlock(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr == NULL) - continue; - if (ifa->ifa_addr->sa_family != AF_INET) - continue; - ptr->ip = ((struct sockaddr_in *) - (ifa->ifa_addr))->sin_addr; - LibAliasSetAddress(ptr->lib, ptr->ip); - } - if_addr_runlock(ifp); - } - IPFW_WUNLOCK(chain); -} - -/* - * delete the pointers for nat entry ix, or all of them if ix < 0 - */ -static void -flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) -{ - int i; - ipfw_insn_nat *cmd; - - IPFW_WLOCK_ASSERT(chain); - for (i = 0; i < chain->n_rules; i++) { - cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); - /* XXX skip log and the like ? */ - if (cmd->o.opcode == O_NAT && cmd->nat != NULL && - (ix < 0 || cmd->nat->id == ix)) - cmd->nat = NULL; - } -} - -static void -del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) -{ - struct cfg_redir *r, *tmp_r; - struct cfg_spool *s, *tmp_s; - int i, num; - - LIST_FOREACH_SAFE(r, head, _next, tmp_r) { - num = 1; /* Number of alias_link to delete. */ - switch (r->mode) { - case REDIR_PORT: - num = r->pport_cnt; - /* FALLTHROUGH */ - case REDIR_ADDR: - case REDIR_PROTO: - /* Delete all libalias redirect entry. */ - for (i = 0; i < num; i++) - LibAliasRedirectDelete(n->lib, r->alink[i]); - /* Del spool cfg if any. */ - LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { - LIST_REMOVE(s, _next); - free(s, M_IPFW); - } - free(r->alink, M_IPFW); - LIST_REMOVE(r, _next); - free(r, M_IPFW); - break; - default: - printf("unknown redirect mode: %u\n", r->mode); - /* XXX - panic?!?!? */ - break; - } - } -} - -static void -add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) -{ - struct cfg_redir *r, *ser_r; - struct cfg_spool *s, *ser_s; - int cnt, off, i; - - for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { - ser_r = (struct cfg_redir *)&buf[off]; - r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); - memcpy(r, ser_r, SOF_REDIR); - LIST_INIT(&r->spool_chain); - off += SOF_REDIR; - r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, - M_IPFW, M_WAITOK | M_ZERO); - switch (r->mode) { - case REDIR_ADDR: - r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, - r->paddr); - break; - case REDIR_PORT: - for (i = 0 ; i < r->pport_cnt; i++) { - /* If remotePort is all ports, set it to 0. */ - u_short remotePortCopy = r->rport + i; - if (r->rport_cnt == 1 && r->rport == 0) - remotePortCopy = 0; - r->alink[i] = LibAliasRedirectPort(ptr->lib, - r->laddr, htons(r->lport + i), r->raddr, - htons(remotePortCopy), r->paddr, - htons(r->pport + i), r->proto); - if (r->alink[i] == NULL) { - r->alink[0] = NULL; - break; - } - } - break; - case REDIR_PROTO: - r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, - r->raddr, r->paddr, r->proto); - break; - default: - printf("unknown redirect mode: %u\n", r->mode); - break; - } - /* XXX perhaps return an error instead of panic ? */ - if (r->alink[0] == NULL) - panic("LibAliasRedirect* returned NULL"); - /* LSNAT handling. */ - for (i = 0; i < r->spool_cnt; i++) { - ser_s = (struct cfg_spool *)&buf[off]; - s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); - memcpy(s, ser_s, SOF_SPOOL); - LibAliasAddServer(ptr->lib, r->alink[0], - s->addr, htons(s->port)); - off += SOF_SPOOL; - /* Hook spool entry. */ - LIST_INSERT_HEAD(&r->spool_chain, s, _next); - } - /* And finally hook this redir entry. */ - LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); - } -} - -static int -ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) -{ - struct mbuf *mcl; - struct ip *ip; - /* XXX - libalias duct tape */ - int ldt, retval, found; - struct ip_fw_chain *chain; - char *c; - - ldt = 0; - retval = 0; - mcl = m_megapullup(m, m->m_pkthdr.len); - if (mcl == NULL) { - args->m = NULL; - return (IP_FW_DENY); - } - ip = mtod(mcl, struct ip *); - - /* - * XXX - Libalias checksum offload 'duct tape': - * - * locally generated packets have only pseudo-header checksum - * calculated and libalias will break it[1], so mark them for - * later fix. Moreover there are cases when libalias modifies - * tcp packet data[2], mark them for later fix too. - * - * [1] libalias was never meant to run in kernel, so it does - * not have any knowledge about checksum offloading, and - * expects a packet with a full internet checksum. - * Unfortunately, packets generated locally will have just the - * pseudo header calculated, and when libalias tries to adjust - * the checksum it will actually compute a wrong value. - * - * [2] when libalias modifies tcp's data content, full TCP - * checksum has to be recomputed: the problem is that - * libalias does not have any idea about checksum offloading. - * To work around this, we do not do checksumming in LibAlias, - * but only mark the packets in th_x2 field. If we receive a - * marked packet, we calculate correct checksum for it - * aware of offloading. Why such a terrible hack instead of - * recalculating checksum for each packet? - * Because the previous checksum was not checked! - * Recalculating checksums for EVERY packet will hide ALL - * transmission errors. Yes, marked packets still suffer from - * this problem. But, sigh, natd(8) has this problem, too. - * - * TODO: -make libalias mbuf aware (so - * it can handle delayed checksum and tso) - */ - - if (mcl->m_pkthdr.rcvif == NULL && - mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) - ldt = 1; - - c = mtod(mcl, char *); - - /* Check if this is 'global' instance */ - if (t == NULL) { - if (args->oif == NULL) { - /* Wrong direction, skip processing */ - args->m = mcl; - return (IP_FW_NAT); - } - - found = 0; - chain = &V_layer3_chain; - IPFW_RLOCK(chain); - /* Check every nat entry... */ - LIST_FOREACH(t, &chain->nat, _next) { - if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) - continue; - retval = LibAliasOutTry(t->lib, c, - mcl->m_len + M_TRAILINGSPACE(mcl), 0); - if (retval == PKT_ALIAS_OK) { - /* Nat instance recognises state */ - found = 1; - break; - } - } - IPFW_RUNLOCK(chain); - if (found != 1) { - /* No instance found, return ignore */ - args->m = mcl; - return (IP_FW_NAT); - } - } else { - if (args->oif == NULL) - retval = LibAliasIn(t->lib, c, - mcl->m_len + M_TRAILINGSPACE(mcl)); - else - retval = LibAliasOut(t->lib, c, - mcl->m_len + M_TRAILINGSPACE(mcl)); - } - - /* - * We drop packet when: - * 1. libalias returns PKT_ALIAS_ERROR; - * 2. For incoming packets: - * a) for unresolved fragments; - * b) libalias returns PKT_ALIAS_IGNORED and - * PKT_ALIAS_DENY_INCOMING flag is set. - */ - if (retval == PKT_ALIAS_ERROR || - (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || - (retval == PKT_ALIAS_IGNORED && - (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { - /* XXX - should i add some logging? */ - m_free(mcl); - args->m = NULL; - return (IP_FW_DENY); - } - - if (retval == PKT_ALIAS_RESPOND) - mcl->m_flags |= M_SKIP_FIREWALL; - mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); - - /* - * XXX - libalias checksum offload - * 'duct tape' (see above) - */ - - if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && - ip->ip_p == IPPROTO_TCP) { - struct tcphdr *th; - - th = (struct tcphdr *)(ip + 1); - if (th->th_x2) - ldt = 1; - } - - if (ldt) { - struct tcphdr *th; - struct udphdr *uh; - u_short cksum; - - ip->ip_len = ntohs(ip->ip_len); - cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, - htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))); - - switch (ip->ip_p) { - case IPPROTO_TCP: - th = (struct tcphdr *)(ip + 1); - /* - * Maybe it was set in - * libalias... - */ - th->th_x2 = 0; - th->th_sum = cksum; - mcl->m_pkthdr.csum_data = - offsetof(struct tcphdr, th_sum); - break; - case IPPROTO_UDP: - uh = (struct udphdr *)(ip + 1); - uh->uh_sum = cksum; - mcl->m_pkthdr.csum_data = - offsetof(struct udphdr, uh_sum); - break; - } - /* No hw checksum offloading: do it ourselves */ - if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { - in_delayed_cksum(mcl); - mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - ip->ip_len = htons(ip->ip_len); - } - args->m = mcl; - return (IP_FW_NAT); -} - -static struct cfg_nat * -lookup_nat(struct nat_list *l, int nat_id) -{ - struct cfg_nat *res; - - LIST_FOREACH(res, l, _next) { - if (res->id == nat_id) - break; - } - return res; -} - -static int -ipfw_nat_cfg(struct sockopt *sopt) -{ - struct cfg_nat *cfg, *ptr; - char *buf; - struct ip_fw_chain *chain = &V_layer3_chain; - size_t len; - int gencnt, error = 0; - - len = sopt->sopt_valsize; - buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO); - if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0) - goto out; - - cfg = (struct cfg_nat *)buf; - if (cfg->id < 0) { - error = EINVAL; - goto out; - } - - /* - * Find/create nat rule. - */ - IPFW_WLOCK(chain); - gencnt = chain->gencnt; - ptr = lookup_nat(&chain->nat, cfg->id); - if (ptr == NULL) { - IPFW_WUNLOCK(chain); - /* New rule: allocate and init new instance. */ - ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); - ptr->lib = LibAliasInit(NULL); - LIST_INIT(&ptr->redir_chain); - } else { - /* Entry already present: temporarily unhook it. */ - LIST_REMOVE(ptr, _next); - flush_nat_ptrs(chain, cfg->id); - IPFW_WUNLOCK(chain); - } - - /* - * Basic nat configuration. - */ - ptr->id = cfg->id; - /* - * XXX - what if this rule doesn't nat any ip and just - * redirect? - * do we set aliasaddress to 0.0.0.0? - */ - ptr->ip = cfg->ip; - ptr->redir_cnt = cfg->redir_cnt; - ptr->mode = cfg->mode; - LibAliasSetMode(ptr->lib, cfg->mode, cfg->mode); - LibAliasSetAddress(ptr->lib, ptr->ip); - memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE); - - /* - * Redir and LSNAT configuration. - */ - /* Delete old cfgs. */ - del_redir_spool_cfg(ptr, &ptr->redir_chain); - /* Add new entries. */ - add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); - - IPFW_WLOCK(chain); - /* Extra check to avoid race with another ipfw_nat_cfg() */ - if (gencnt != chain->gencnt && - ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL)) - LIST_REMOVE(cfg, _next); - LIST_INSERT_HEAD(&chain->nat, ptr, _next); - chain->gencnt++; - IPFW_WUNLOCK(chain); - -out: - free(buf, M_TEMP); - return (error); -} - -static int -ipfw_nat_del(struct sockopt *sopt) -{ - struct cfg_nat *ptr; - struct ip_fw_chain *chain = &V_layer3_chain; - int i; - - sooptcopyin(sopt, &i, sizeof i, sizeof i); - /* XXX validate i */ - IPFW_WLOCK(chain); - ptr = lookup_nat(&chain->nat, i); - if (ptr == NULL) { - IPFW_WUNLOCK(chain); - return (EINVAL); - } - LIST_REMOVE(ptr, _next); - flush_nat_ptrs(chain, i); - IPFW_WUNLOCK(chain); - del_redir_spool_cfg(ptr, &ptr->redir_chain); - LibAliasUninit(ptr->lib); - free(ptr, M_IPFW); - return (0); -} - -static int -ipfw_nat_get_cfg(struct sockopt *sopt) -{ - struct ip_fw_chain *chain = &V_layer3_chain; - struct cfg_nat *n; - struct cfg_redir *r; - struct cfg_spool *s; - char *data; - int gencnt, nat_cnt, len, error; - - nat_cnt = 0; - len = sizeof(nat_cnt); - - IPFW_RLOCK(chain); -retry: - gencnt = chain->gencnt; - /* Estimate memory amount */ - LIST_FOREACH(n, &chain->nat, _next) { - nat_cnt++; - len += sizeof(struct cfg_nat); - LIST_FOREACH(r, &n->redir_chain, _next) { - len += sizeof(struct cfg_redir); - LIST_FOREACH(s, &r->spool_chain, _next) - len += sizeof(struct cfg_spool); - } - } - IPFW_RUNLOCK(chain); - - data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); - bcopy(&nat_cnt, data, sizeof(nat_cnt)); - - nat_cnt = 0; - len = sizeof(nat_cnt); - - IPFW_RLOCK(chain); - if (gencnt != chain->gencnt) { - free(data, M_TEMP); - goto retry; - } - /* Serialize all the data. */ - LIST_FOREACH(n, &chain->nat, _next) { - bcopy(n, &data[len], sizeof(struct cfg_nat)); - len += sizeof(struct cfg_nat); - LIST_FOREACH(r, &n->redir_chain, _next) { - bcopy(r, &data[len], sizeof(struct cfg_redir)); - len += sizeof(struct cfg_redir); - LIST_FOREACH(s, &r->spool_chain, _next) { - bcopy(s, &data[len], sizeof(struct cfg_spool)); - len += sizeof(struct cfg_spool); - } - } - } - IPFW_RUNLOCK(chain); - - error = sooptcopyout(sopt, data, len); - free(data, M_TEMP); - - return (error); -} - -static int -ipfw_nat_get_log(struct sockopt *sopt) -{ - uint8_t *data; - struct cfg_nat *ptr; - int i, size; - struct ip_fw_chain *chain; - - chain = &V_layer3_chain; - - IPFW_RLOCK(chain); - /* one pass to count, one to copy the data */ - i = 0; - LIST_FOREACH(ptr, &chain->nat, _next) { - if (ptr->lib->logDesc == NULL) - continue; - i++; - } - size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); - data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); - if (data == NULL) { - IPFW_RUNLOCK(chain); - return (ENOSPC); - } - i = 0; - LIST_FOREACH(ptr, &chain->nat, _next) { - if (ptr->lib->logDesc == NULL) - continue; - bcopy(&ptr->id, &data[i], sizeof(int)); - i += sizeof(int); - bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); - i += LIBALIAS_BUF_SIZE; - } - IPFW_RUNLOCK(chain); - sooptcopyout(sopt, data, size); - free(data, M_IPFW); - return(0); -} - -static void -ipfw_nat_init(void) -{ - - IPFW_WLOCK(&V_layer3_chain); - /* init ipfw hooks */ - ipfw_nat_ptr = ipfw_nat; - lookup_nat_ptr = lookup_nat; - ipfw_nat_cfg_ptr = ipfw_nat_cfg; - ipfw_nat_del_ptr = ipfw_nat_del; - ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; - ipfw_nat_get_log_ptr = ipfw_nat_get_log; - IPFW_WUNLOCK(&V_layer3_chain); - V_ifaddr_event_tag = EVENTHANDLER_REGISTER( - ifaddr_event, ifaddr_change, - NULL, EVENTHANDLER_PRI_ANY); -} - -static void -ipfw_nat_destroy(void) -{ - struct cfg_nat *ptr, *ptr_temp; - struct ip_fw_chain *chain; - - chain = &V_layer3_chain; - IPFW_WLOCK(chain); - LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { - LIST_REMOVE(ptr, _next); - del_redir_spool_cfg(ptr, &ptr->redir_chain); - LibAliasUninit(ptr->lib); - free(ptr, M_IPFW); - } - EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); - flush_nat_ptrs(chain, -1 /* flush all */); - /* deregister ipfw_nat */ - ipfw_nat_ptr = NULL; - lookup_nat_ptr = NULL; - ipfw_nat_cfg_ptr = NULL; - ipfw_nat_del_ptr = NULL; - ipfw_nat_get_cfg_ptr = NULL; - ipfw_nat_get_log_ptr = NULL; - IPFW_WUNLOCK(chain); -} - -static int -ipfw_nat_modevent(module_t mod, int type, void *unused) -{ - int err = 0; - - switch (type) { - case MOD_LOAD: - ipfw_nat_init(); - break; - - case MOD_UNLOAD: - ipfw_nat_destroy(); - break; - - default: - return EOPNOTSUPP; - break; - } - return err; -} - -static moduledata_t ipfw_nat_mod = { - "ipfw_nat", - ipfw_nat_modevent, - 0 -}; - -DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); -MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); -MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); -MODULE_VERSION(ipfw_nat, 1); -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw_pfil.c b/sys/netinet/ipfw/ip_fw_pfil.c deleted file mode 100644 index 8d429e78b3c57..0000000000000 --- a/sys/netinet/ipfw/ip_fw_pfil.c +++ /dev/null @@ -1,588 +0,0 @@ -/*- - * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_ipfw.h" -#include "opt_inet.h" -#include "opt_inet6.h" -#ifndef INET -#error IPFIREWALL requires INET. -#endif /* INET */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#ifdef INET6 -#include -#include -#endif -#include -#include - -#include - -static VNET_DEFINE(int, fw_enable) = 1; -#define V_fw_enable VNET(fw_enable) - -#ifdef INET6 -static VNET_DEFINE(int, fw6_enable) = 1; -#define V_fw6_enable VNET(fw6_enable) -#endif - -static VNET_DEFINE(int, fwlink_enable) = 0; -#define V_fwlink_enable VNET(fwlink_enable) - -int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); - -/* Forward declarations. */ -static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int); -static int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int, - struct inpcb *); -static int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int, - struct inpcb *); - -#ifdef SYSCTL_NODE - -SYSBEGIN(f1) - -SYSCTL_DECL(_net_inet_ip_fw); -SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, enable, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_enable), 0, - ipfw_chg_hook, "I", "Enable ipfw"); -#ifdef INET6 -SYSCTL_DECL(_net_inet6_ip6_fw); -SYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_AUTO, enable, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw6_enable), 0, - ipfw_chg_hook, "I", "Enable ipfw+6"); -#endif /* INET6 */ - -SYSCTL_DECL(_net_link_ether); -SYSCTL_VNET_PROC(_net_link_ether, OID_AUTO, ipfw, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fwlink_enable), 0, - ipfw_chg_hook, "I", "Pass ether pkts through firewall"); - -SYSEND - -#endif /* SYSCTL_NODE */ - -/* - * The pfilter hook to pass packets to ipfw_chk and then to - * dummynet, divert, netgraph or other modules. - * The packet may be consumed. - */ -static int -ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, - struct inpcb *inp) -{ - struct ip_fw_args args; - struct m_tag *tag; - int ipfw; - int ret; - - /* all the processing now uses ip_len in net format */ - if (mtod(*m0, struct ip *)->ip_v == 4) - SET_NET_IPLEN(mtod(*m0, struct ip *)); - - /* convert dir to IPFW values */ - dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; - bzero(&args, sizeof(args)); - -again: - /* - * extract and remove the tag if present. If we are left - * with onepass, optimize the outgoing path. - */ - tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); - if (tag != NULL) { - args.rule = *((struct ipfw_rule_ref *)(tag+1)); - m_tag_delete(*m0, tag); - if (args.rule.info & IPFW_ONEPASS) { - if (mtod(*m0, struct ip *)->ip_v == 4) - SET_HOST_IPLEN(mtod(*m0, struct ip *)); - return (0); - } - } - - args.m = *m0; - args.oif = dir == DIR_OUT ? ifp : NULL; - args.inp = inp; - - ipfw = ipfw_chk(&args); - *m0 = args.m; - - KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", - __func__)); - - /* breaking out of the switch means drop */ - ret = 0; /* default return value for pass */ - switch (ipfw) { - case IP_FW_PASS: - /* next_hop may be set by ipfw_chk */ - if (args.next_hop == NULL && args.next_hop6 == NULL) - break; /* pass */ -#if !defined(IPFIREWALL_FORWARD) || (!defined(INET6) && !defined(INET)) - ret = EACCES; -#else - { - struct m_tag *fwd_tag; - size_t len; - - KASSERT(args.next_hop == NULL || args.next_hop6 == NULL, - ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__, - args.next_hop, args.next_hop6)); -#ifdef INET6 - if (args.next_hop6 != NULL) - len = sizeof(struct sockaddr_in6); -#endif -#ifdef INET - if (args.next_hop != NULL) - len = sizeof(struct sockaddr_in); -#endif - - /* Incoming packets should not be tagged so we do not - * m_tag_find. Outgoing packets may be tagged, so we - * reuse the tag if present. - */ - fwd_tag = (dir == DIR_IN) ? NULL : - m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); - if (fwd_tag != NULL) { - m_tag_unlink(*m0, fwd_tag); - } else { - fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, - M_NOWAIT); - if (fwd_tag == NULL) { - ret = EACCES; - break; /* i.e. drop */ - } - } -#ifdef INET6 - if (args.next_hop6 != NULL) { - bcopy(args.next_hop6, (fwd_tag+1), len); - if (in6_localip(&args.next_hop6->sin6_addr)) - (*m0)->m_flags |= M_FASTFWD_OURS; - } -#endif -#ifdef INET - if (args.next_hop != NULL) { - bcopy(args.next_hop, (fwd_tag+1), len); - if (in_localip(args.next_hop->sin_addr)) - (*m0)->m_flags |= M_FASTFWD_OURS; - } -#endif - m_tag_prepend(*m0, fwd_tag); - } -#endif /* IPFIREWALL_FORWARD */ - break; - - case IP_FW_DENY: - ret = EACCES; - break; /* i.e. drop */ - - case IP_FW_DUMMYNET: - ret = EACCES; - if (ip_dn_io_ptr == NULL) - break; /* i.e. drop */ - if (mtod(*m0, struct ip *)->ip_v == 4) - ret = ip_dn_io_ptr(m0, dir, &args); - else if (mtod(*m0, struct ip *)->ip_v == 6) - ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); - else - break; /* drop it */ - /* - * XXX should read the return value. - * dummynet normally eats the packet and sets *m0=NULL - * unless the packet can be sent immediately. In this - * case args is updated and we should re-run the - * check without clearing args. - */ - if (*m0 != NULL) - goto again; - break; - - case IP_FW_TEE: - case IP_FW_DIVERT: - if (ip_divert_ptr == NULL) { - ret = EACCES; - break; /* i.e. drop */ - } - ret = ipfw_divert(m0, dir, &args.rule, - (ipfw == IP_FW_TEE) ? 1 : 0); - /* continue processing for the original packet (tee). */ - if (*m0) - goto again; - break; - - case IP_FW_NGTEE: - case IP_FW_NETGRAPH: - if (ng_ipfw_input_p == NULL) { - ret = EACCES; - break; /* i.e. drop */ - } - ret = ng_ipfw_input_p(m0, dir, &args, - (ipfw == IP_FW_NGTEE) ? 1 : 0); - if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ - goto again; /* continue with packet */ - break; - - case IP_FW_NAT: - /* honor one-pass in case of successful nat */ - if (V_fw_one_pass) - break; /* ret is already 0 */ - goto again; - - case IP_FW_REASS: - goto again; /* continue with packet */ - - default: - KASSERT(0, ("%s: unknown retval", __func__)); - } - - if (ret != 0) { - if (*m0) - FREE_PKT(*m0); - *m0 = NULL; - } - if (*m0 && mtod(*m0, struct ip *)->ip_v == 4) - SET_HOST_IPLEN(mtod(*m0, struct ip *)); - return ret; -} - -/* - * ipfw processing for ethernet packets (in and out). - * Inteface is NULL from ether_demux, and ifp from - * ether_output_frame. - */ -static int -ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, - struct inpcb *inp) -{ - struct ether_header *eh; - struct ether_header save_eh; - struct mbuf *m; - int i, ret; - struct ip_fw_args args; - struct m_tag *mtag; - - /* fetch start point from rule, if any */ - mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); - if (mtag == NULL) { - args.rule.slot = 0; - } else { - /* dummynet packet, already partially processed */ - struct ipfw_rule_ref *r; - - /* XXX can we free it after use ? */ - mtag->m_tag_id = PACKET_TAG_NONE; - r = (struct ipfw_rule_ref *)(mtag + 1); - if (r->info & IPFW_ONEPASS) - return (0); - args.rule = *r; - } - - /* I need some amt of data to be contiguous */ - m = *m0; - i = min(m->m_pkthdr.len, max_protohdr); - if (m->m_len < i) { - m = m_pullup(m, i); - if (m == NULL) { - *m0 = m; - return (0); - } - } - eh = mtod(m, struct ether_header *); - save_eh = *eh; /* save copy for restore below */ - m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ - - args.m = m; /* the packet we are looking at */ - args.oif = dst; /* destination, if any */ - args.next_hop = NULL; /* we do not support forward yet */ - args.next_hop6 = NULL; /* we do not support forward yet */ - args.eh = &save_eh; /* MAC header for bridged/MAC packets */ - args.inp = NULL; /* used by ipfw uid/gid/jail rules */ - i = ipfw_chk(&args); - m = args.m; - if (m != NULL) { - /* - * Restore Ethernet header, as needed, in case the - * mbuf chain was replaced by ipfw. - */ - M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); - if (m == NULL) { - *m0 = NULL; - return (0); - } - if (eh != mtod(m, struct ether_header *)) - bcopy(&save_eh, mtod(m, struct ether_header *), - ETHER_HDR_LEN); - } - *m0 = m; - - ret = 0; - /* Check result of ipfw_chk() */ - switch (i) { - case IP_FW_PASS: - break; - - case IP_FW_DENY: - ret = EACCES; - break; /* i.e. drop */ - - case IP_FW_DUMMYNET: - ret = EACCES; - int dir; - - if (ip_dn_io_ptr == NULL) - break; /* i.e. drop */ - - *m0 = NULL; - dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN); - ip_dn_io_ptr(&m, dir, &args); - return 0; - - default: - KASSERT(0, ("%s: unknown retval", __func__)); - } - - if (ret != 0) { - if (*m0) - FREE_PKT(*m0); - *m0 = NULL; - } - - return ret; -} - -/* do the divert, return 1 on error 0 on success */ -static int -ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, - int tee) -{ - /* - * ipfw_chk() has already tagged the packet with the divert tag. - * If tee is set, copy packet and return original. - * If not tee, consume packet and send it to divert socket. - */ - struct mbuf *clone; - struct ip *ip = mtod(*m0, struct ip *); - struct m_tag *tag; - - /* Cloning needed for tee? */ - if (tee == 0) { - clone = *m0; /* use the original mbuf */ - *m0 = NULL; - } else { - clone = m_dup(*m0, M_DONTWAIT); - /* If we cannot duplicate the mbuf, we sacrifice the divert - * chain and continue with the tee-ed packet. - */ - if (clone == NULL) - return 1; - } - - /* - * Divert listeners can normally handle non-fragmented packets, - * but we can only reass in the non-tee case. - * This means that listeners on a tee rule may get fragments, - * and have to live with that. - * Note that we now have the 'reass' ipfw option so if we care - * we can do it before a 'tee'. - */ - if (!tee) switch (ip->ip_v) { - case IPVERSION: - if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { - int hlen; - struct mbuf *reass; - - SET_HOST_IPLEN(ip); /* ip_reass wants host order */ - reass = ip_reass(clone); /* Reassemble packet. */ - if (reass == NULL) - return 0; /* not an error */ - /* if reass = NULL then it was consumed by ip_reass */ - /* - * IP header checksum fixup after reassembly and leave header - * in network byte order. - */ - ip = mtod(reass, struct ip *); - hlen = ip->ip_hl << 2; - SET_NET_IPLEN(ip); - ip->ip_sum = 0; - if (hlen == sizeof(struct ip)) - ip->ip_sum = in_cksum_hdr(ip); - else - ip->ip_sum = in_cksum(reass, hlen); - clone = reass; - } - break; -#ifdef INET6 - case IPV6_VERSION >> 4: - { - struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *); - - if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { - int nxt, off; - - off = sizeof(struct ip6_hdr); - nxt = frag6_input(&clone, &off, 0); - if (nxt == IPPROTO_DONE) - return (0); - } - break; - } -#endif - } - - /* attach a tag to the packet with the reinject info */ - tag = m_tag_alloc(MTAG_IPFW_RULE, 0, - sizeof(struct ipfw_rule_ref), M_NOWAIT); - if (tag == NULL) { - FREE_PKT(clone); - return 1; - } - *((struct ipfw_rule_ref *)(tag+1)) = *rule; - m_tag_prepend(clone, tag); - - /* Do the dirty job... */ - ip_divert_ptr(clone, incoming); - return 0; -} - -/* - * attach or detach hooks for a given protocol family - */ -static int -ipfw_hook(int onoff, int pf) -{ - struct pfil_head *pfh; - void *hook_func; - - pfh = pfil_head_get(PFIL_TYPE_AF, pf); - if (pfh == NULL) - return ENOENT; - - hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet; - - (void) (onoff ? pfil_add_hook : pfil_remove_hook) - (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); - - return 0; -} - -int -ipfw_attach_hooks(int arg) -{ - int error = 0; - - if (arg == 0) /* detach */ - ipfw_hook(0, AF_INET); - else if (V_fw_enable && ipfw_hook(1, AF_INET) != 0) { - error = ENOENT; /* see ip_fw_pfil.c::ipfw_hook() */ - printf("ipfw_hook() error\n"); - } -#ifdef INET6 - if (arg == 0) /* detach */ - ipfw_hook(0, AF_INET6); - else if (V_fw6_enable && ipfw_hook(1, AF_INET6) != 0) { - error = ENOENT; - printf("ipfw6_hook() error\n"); - } -#endif - if (arg == 0) /* detach */ - ipfw_hook(0, AF_LINK); - else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) { - error = ENOENT; - printf("ipfw_link_hook() error\n"); - } - return error; -} - -int -ipfw_chg_hook(SYSCTL_HANDLER_ARGS) -{ - int *enable; - int newval; - int error; - int af; - - if (arg1 == &VNET_NAME(fw_enable)) { - enable = &V_fw_enable; - af = AF_INET; - } -#ifdef INET6 - else if (arg1 == &VNET_NAME(fw6_enable)) { - enable = &V_fw6_enable; - af = AF_INET6; - } -#endif - else if (arg1 == &VNET_NAME(fwlink_enable)) { - enable = &V_fwlink_enable; - af = AF_LINK; - } - else - return (EINVAL); - - newval = *enable; - - /* Handle sysctl change */ - error = sysctl_handle_int(oidp, &newval, 0, req); - - if (error) - return (error); - - /* Formalize new value */ - newval = (newval) ? 1 : 0; - - if (*enable == newval) - return (0); - - error = ipfw_hook(newval, af); - if (error) - return (error); - *enable = newval; - - return (0); -} -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw_private.h b/sys/netinet/ipfw/ip_fw_private.h deleted file mode 100644 index fb13a7228d9eb..0000000000000 --- a/sys/netinet/ipfw/ip_fw_private.h +++ /dev/null @@ -1,309 +0,0 @@ -/*- - * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa - * - * 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. - * - * $FreeBSD$ - */ - -#ifndef _IPFW2_PRIVATE_H -#define _IPFW2_PRIVATE_H - -/* - * Internal constants and data structures used by ipfw components - * and not meant to be exported outside the kernel. - */ - -#ifdef _KERNEL - -/* - * For platforms that do not have SYSCTL support, we wrap the - * SYSCTL_* into a function (one per file) to collect the values - * into an array at module initialization. The wrapping macros, - * SYSBEGIN() and SYSEND, are empty in the default case. - */ -#ifndef SYSBEGIN -#define SYSBEGIN(x) -#endif -#ifndef SYSEND -#define SYSEND -#endif - -/* Return values from ipfw_chk() */ -enum { - IP_FW_PASS = 0, - IP_FW_DENY, - IP_FW_DIVERT, - IP_FW_TEE, - IP_FW_DUMMYNET, - IP_FW_NETGRAPH, - IP_FW_NGTEE, - IP_FW_NAT, - IP_FW_REASS, -}; - -/* - * Structure for collecting parameters to dummynet for ip6_output forwarding - */ -struct _ip6dn_args { - struct ip6_pktopts *opt_or; - struct route_in6 ro_or; - int flags_or; - struct ip6_moptions *im6o_or; - struct ifnet *origifp_or; - struct ifnet *ifp_or; - struct sockaddr_in6 dst_or; - u_long mtu_or; - struct route_in6 ro_pmtu_or; -}; - - -/* - * Arguments for calling ipfw_chk() and dummynet_io(). We put them - * all into a structure because this way it is easier and more - * efficient to pass variables around and extend the interface. - */ -struct ip_fw_args { - struct mbuf *m; /* the mbuf chain */ - struct ifnet *oif; /* output interface */ - struct sockaddr_in *next_hop; /* forward address */ - struct sockaddr_in6 *next_hop6; /* ipv6 forward address */ - - /* - * On return, it points to the matching rule. - * On entry, rule.slot > 0 means the info is valid and - * contains the starting rule for an ipfw search. - * If chain_id == chain->id && slot >0 then jump to that slot. - * Otherwise, we locate the first rule >= rulenum:rule_id - */ - struct ipfw_rule_ref rule; /* match/restart info */ - - struct ether_header *eh; /* for bridged packets */ - - struct ipfw_flow_id f_id; /* grabbed from IP header */ - //uint32_t cookie; /* a cookie depending on rule action */ - struct inpcb *inp; - - struct _ip6dn_args dummypar; /* dummynet->ip6_output */ - struct sockaddr_in hopstore; /* store here if cannot use a pointer */ -}; - -MALLOC_DECLARE(M_IPFW); - -/* - * Hooks sometime need to know the direction of the packet - * (divert, dummynet, netgraph, ...) - * We use a generic definition here, with bit0-1 indicating the - * direction, bit 2 indicating layer2 or 3, bit 3-4 indicating the - * specific protocol - * indicating the protocol (if necessary) - */ -enum { - DIR_MASK = 0x3, - DIR_OUT = 0, - DIR_IN = 1, - DIR_FWD = 2, - DIR_DROP = 3, - PROTO_LAYER2 = 0x4, /* set for layer 2 */ - /* PROTO_DEFAULT = 0, */ - PROTO_IPV4 = 0x08, - PROTO_IPV6 = 0x10, - PROTO_IFB = 0x0c, /* layer2 + ifbridge */ - /* PROTO_OLDBDG = 0x14, unused, old bridge */ -}; - -/* wrapper for freeing a packet, in case we need to do more work */ -#ifndef FREE_PKT -#if defined(__linux__) || defined(_WIN32) -#define FREE_PKT(m) netisr_dispatch(-1, m) -#else -#define FREE_PKT(m) m_freem(m) -#endif -#endif /* !FREE_PKT */ - -/* - * Function definitions. - */ - -/* attach (arg = 1) or detach (arg = 0) hooks */ -int ipfw_attach_hooks(int); -#ifdef NOTYET -void ipfw_nat_destroy(void); -#endif - -/* In ip_fw_log.c */ -struct ip; -void ipfw_log_bpf(int); -void ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, - struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg, - struct ip *ip); -VNET_DECLARE(u_int64_t, norule_counter); -#define V_norule_counter VNET(norule_counter) -VNET_DECLARE(int, verbose_limit); -#define V_verbose_limit VNET(verbose_limit) - -/* In ip_fw_dynamic.c */ - -enum { /* result for matching dynamic rules */ - MATCH_REVERSE = 0, - MATCH_FORWARD, - MATCH_NONE, - MATCH_UNKNOWN, -}; - -/* - * The lock for dynamic rules is only used once outside the file, - * and only to release the result of lookup_dyn_rule(). - * Eventually we may implement it with a callback on the function. - */ -void ipfw_dyn_unlock(void); - -struct tcphdr; -struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *, - u_int32_t, u_int32_t, int); -int ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, - struct ip_fw_args *args, uint32_t tablearg); -ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, - int *match_direction, struct tcphdr *tcp); -void ipfw_remove_dyn_children(struct ip_fw *rule); -void ipfw_get_dynamic(char **bp, const char *ep); - -void ipfw_dyn_attach(void); /* uma_zcreate .... */ -void ipfw_dyn_detach(void); /* uma_zdestroy ... */ -void ipfw_dyn_init(void); /* per-vnet initialization */ -void ipfw_dyn_uninit(int); /* per-vnet deinitialization */ -int ipfw_dyn_len(void); - -/* common variables */ -VNET_DECLARE(int, fw_one_pass); -#define V_fw_one_pass VNET(fw_one_pass) - -VNET_DECLARE(int, fw_verbose); -#define V_fw_verbose VNET(fw_verbose) - -VNET_DECLARE(struct ip_fw_chain, layer3_chain); -#define V_layer3_chain VNET(layer3_chain) - -VNET_DECLARE(u_int32_t, set_disable); -#define V_set_disable VNET(set_disable) - -VNET_DECLARE(int, autoinc_step); -#define V_autoinc_step VNET(autoinc_step) - -VNET_DECLARE(unsigned int, fw_tables_max); -#define V_fw_tables_max VNET(fw_tables_max) - -struct ip_fw_chain { - struct ip_fw *rules; /* list of rules */ - struct ip_fw *reap; /* list of rules to reap */ - struct ip_fw *default_rule; - int n_rules; /* number of static rules */ - int static_len; /* total len of static rules */ - struct ip_fw **map; /* array of rule ptrs to ease lookup */ - LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ - struct radix_node_head **tables; /* IPv4 tables */ - struct radix_node_head **xtables; /* extended tables */ - uint8_t *tabletype; /* Array of table types */ -#if defined( __linux__ ) || defined( _WIN32 ) - spinlock_t rwmtx; - spinlock_t uh_lock; -#else - struct rwlock rwmtx; - struct rwlock uh_lock; /* lock for upper half */ -#endif - uint32_t id; /* ruleset id */ - uint32_t gencnt; /* generation count */ -}; - -struct sockopt; /* used by tcp_var.h */ - -/* - * The lock is heavily used by ip_fw2.c (the main file) and ip_fw_nat.c - * so the variable and the macros must be here. - */ - -#define IPFW_LOCK_INIT(_chain) do { \ - rw_init(&(_chain)->rwmtx, "IPFW static rules"); \ - rw_init(&(_chain)->uh_lock, "IPFW UH lock"); \ - } while (0) - -#define IPFW_LOCK_DESTROY(_chain) do { \ - rw_destroy(&(_chain)->rwmtx); \ - rw_destroy(&(_chain)->uh_lock); \ - } while (0) - -#define IPFW_WLOCK_ASSERT(_chain) rw_assert(&(_chain)->rwmtx, RA_WLOCKED) - -#define IPFW_RLOCK(p) rw_rlock(&(p)->rwmtx) -#define IPFW_RUNLOCK(p) rw_runlock(&(p)->rwmtx) -#define IPFW_WLOCK(p) rw_wlock(&(p)->rwmtx) -#define IPFW_WUNLOCK(p) rw_wunlock(&(p)->rwmtx) - -#define IPFW_UH_RLOCK(p) rw_rlock(&(p)->uh_lock) -#define IPFW_UH_RUNLOCK(p) rw_runlock(&(p)->uh_lock) -#define IPFW_UH_WLOCK(p) rw_wlock(&(p)->uh_lock) -#define IPFW_UH_WUNLOCK(p) rw_wunlock(&(p)->uh_lock) - -/* In ip_fw_sockopt.c */ -int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id); -int ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule); -int ipfw_ctl(struct sockopt *sopt); -int ipfw_chk(struct ip_fw_args *args); -void ipfw_reap_rules(struct ip_fw *head); - -/* In ip_fw_table.c */ -struct radix_node; -int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint32_t *val); -int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint32_t *val, int type); -int ipfw_init_tables(struct ip_fw_chain *ch); -void ipfw_destroy_tables(struct ip_fw_chain *ch); -int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl); -int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value); -int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type); -int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt); -int ipfw_dump_table_entry(struct radix_node *rn, void *arg); -int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl); -int ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt); -int ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl); -int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables); - -/* In ip_fw_nat.c -- XXX to be moved to ip_var.h */ - -extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); - -typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *); -typedef int ipfw_nat_cfg_t(struct sockopt *); - -extern ipfw_nat_t *ipfw_nat_ptr; -#define IPFW_NAT_LOADED (ipfw_nat_ptr != NULL) - -extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; -extern ipfw_nat_cfg_t *ipfw_nat_del_ptr; -extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; -extern ipfw_nat_cfg_t *ipfw_nat_get_log_ptr; - -#endif /* _KERNEL */ -#endif /* _IPFW2_PRIVATE_H */ diff --git a/sys/netinet/ipfw/ip_fw_sockopt.c b/sys/netinet/ipfw/ip_fw_sockopt.c deleted file mode 100644 index 2a5f4e7c93fd3..0000000000000 --- a/sys/netinet/ipfw/ip_fw_sockopt.c +++ /dev/null @@ -1,1448 +0,0 @@ -/*- - * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa - * - * Supported by: Valeria Paoli - * - * 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 -__FBSDID("$FreeBSD$"); - -/* - * Sockopt support for ipfw. The routines here implement - * the upper half of the ipfw code. - */ - -#include "opt_ipfw.h" -#include "opt_inet.h" -#ifndef INET -#error IPFIREWALL requires INET. -#endif /* INET */ -#include "opt_inet6.h" - -#include -#include -#include -#include /* struct m_tag used by nested headers */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include /* hooks */ -#include -#include - -#ifdef MAC -#include -#endif - -MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); - -/* - * static variables followed by global ones (none in this file) - */ - -/* - * Find the smallest rule >= key, id. - * We could use bsearch but it is so simple that we code it directly - */ -int -ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id) -{ - int i, lo, hi; - struct ip_fw *r; - - for (lo = 0, hi = chain->n_rules - 1; lo < hi;) { - i = (lo + hi) / 2; - r = chain->map[i]; - if (r->rulenum < key) - lo = i + 1; /* continue from the next one */ - else if (r->rulenum > key) - hi = i; /* this might be good */ - else if (r->id < id) - lo = i + 1; /* continue from the next one */ - else /* r->id >= id */ - hi = i; /* this might be good */ - }; - return hi; -} - -/* - * allocate a new map, returns the chain locked. extra is the number - * of entries to add or delete. - */ -static struct ip_fw ** -get_map(struct ip_fw_chain *chain, int extra, int locked) -{ - - for (;;) { - struct ip_fw **map; - int i; - - i = chain->n_rules + extra; - map = malloc(i * sizeof(struct ip_fw *), M_IPFW, - locked ? M_NOWAIT : M_WAITOK); - if (map == NULL) { - printf("%s: cannot allocate map\n", __FUNCTION__); - return NULL; - } - if (!locked) - IPFW_UH_WLOCK(chain); - if (i >= chain->n_rules + extra) /* good */ - return map; - /* otherwise we lost the race, free and retry */ - if (!locked) - IPFW_UH_WUNLOCK(chain); - free(map, M_IPFW); - } -} - -/* - * swap the maps. It is supposed to be called with IPFW_UH_WLOCK - */ -static struct ip_fw ** -swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len) -{ - struct ip_fw **old_map; - - IPFW_WLOCK(chain); - chain->id++; - chain->n_rules = new_len; - old_map = chain->map; - chain->map = new_map; - IPFW_WUNLOCK(chain); - return old_map; -} - -/* - * Add a new rule to the list. Copy the rule into a malloc'ed area, then - * possibly create a rule number and add the rule to the list. - * Update the rule_number in the input struct so the caller knows it as well. - * XXX DO NOT USE FOR THE DEFAULT RULE. - * Must be called without IPFW_UH held - */ -int -ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) -{ - struct ip_fw *rule; - int i, l, insert_before; - struct ip_fw **map; /* the new array of pointers */ - - if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) - return (EINVAL); - - l = RULESIZE(input_rule); - rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO); - /* get_map returns with IPFW_UH_WLOCK if successful */ - map = get_map(chain, 1, 0 /* not locked */); - if (map == NULL) { - free(rule, M_IPFW); - return ENOSPC; - } - - bcopy(input_rule, rule, l); - /* clear fields not settable from userland */ - rule->x_next = NULL; - rule->next_rule = NULL; - rule->pcnt = 0; - rule->bcnt = 0; - rule->timestamp = 0; - - if (V_autoinc_step < 1) - V_autoinc_step = 1; - else if (V_autoinc_step > 1000) - V_autoinc_step = 1000; - /* find the insertion point, we will insert before */ - insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE; - i = ipfw_find_rule(chain, insert_before, 0); - /* duplicate first part */ - if (i > 0) - bcopy(chain->map, map, i * sizeof(struct ip_fw *)); - map[i] = rule; - /* duplicate remaining part, we always have the default rule */ - bcopy(chain->map + i, map + i + 1, - sizeof(struct ip_fw *) *(chain->n_rules - i)); - if (rule->rulenum == 0) { - /* write back the number */ - rule->rulenum = i > 0 ? map[i-1]->rulenum : 0; - if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) - rule->rulenum += V_autoinc_step; - input_rule->rulenum = rule->rulenum; - } - - rule->id = chain->id + 1; - map = swap_map(chain, map, chain->n_rules + 1); - chain->static_len += l; - IPFW_UH_WUNLOCK(chain); - if (map) - free(map, M_IPFW); - return (0); -} - -/* - * Reclaim storage associated with a list of rules. This is - * typically the list created using remove_rule. - * A NULL pointer on input is handled correctly. - */ -void -ipfw_reap_rules(struct ip_fw *head) -{ - struct ip_fw *rule; - - while ((rule = head) != NULL) { - head = head->x_next; - free(rule, M_IPFW); - } -} - -/* - * Used by del_entry() to check if a rule should be kept. - * Returns 1 if the rule must be kept, 0 otherwise. - * - * Called with cmd = {0,1,5}. - * cmd == 0 matches on rule numbers, excludes rules in RESVD_SET if n == 0 ; - * cmd == 1 matches on set numbers only, rule numbers are ignored; - * cmd == 5 matches on rule and set numbers. - * - * n == 0 is a wildcard for rule numbers, there is no wildcard for sets. - * - * Rules to keep are - * (default || reserved || !match_set || !match_number) - * where - * default ::= (rule->rulenum == IPFW_DEFAULT_RULE) - * // the default rule is always protected - * - * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET) - * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush") - * - * match_set ::= (cmd == 0 || rule->set == set) - * // set number is ignored for cmd == 0 - * - * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum) - * // number is ignored for cmd == 1 or n == 0 - * - */ -static int -keep_rule(struct ip_fw *rule, uint8_t cmd, uint8_t set, uint32_t n) -{ - return - (rule->rulenum == IPFW_DEFAULT_RULE) || - (cmd == 0 && n == 0 && rule->set == RESVD_SET) || - !(cmd == 0 || rule->set == set) || - !(cmd == 1 || n == 0 || n == rule->rulenum); -} - -/** - * Remove all rules with given number, or do set manipulation. - * Assumes chain != NULL && *chain != NULL. - * - * The argument is an uint32_t. The low 16 bit are the rule or set number; - * the next 8 bits are the new set; the top 8 bits indicate the command: - * - * 0 delete rules numbered "rulenum" - * 1 delete rules in set "rulenum" - * 2 move rules "rulenum" to set "new_set" - * 3 move rules from set "rulenum" to set "new_set" - * 4 swap sets "rulenum" and "new_set" - * 5 delete rules "rulenum" and set "new_set" - */ -static int -del_entry(struct ip_fw_chain *chain, uint32_t arg) -{ - struct ip_fw *rule; - uint32_t num; /* rule number or old_set */ - uint8_t cmd, new_set; - int start, end, i, ofs, n; - struct ip_fw **map = NULL; - int error = 0; - - num = arg & 0xffff; - cmd = (arg >> 24) & 0xff; - new_set = (arg >> 16) & 0xff; - - if (cmd > 5 || new_set > RESVD_SET) - return EINVAL; - if (cmd == 0 || cmd == 2 || cmd == 5) { - if (num >= IPFW_DEFAULT_RULE) - return EINVAL; - } else { - if (num > RESVD_SET) /* old_set */ - return EINVAL; - } - - IPFW_UH_WLOCK(chain); /* arbitrate writers */ - chain->reap = NULL; /* prepare for deletions */ - - switch (cmd) { - case 0: /* delete rules "num" (num == 0 matches all) */ - case 1: /* delete all rules in set N */ - case 5: /* delete rules with number N and set "new_set". */ - - /* - * Locate first rule to delete (start), the rule after - * the last one to delete (end), and count how many - * rules to delete (n). Always use keep_rule() to - * determine which rules to keep. - */ - n = 0; - if (cmd == 1) { - /* look for a specific set including RESVD_SET. - * Must scan the entire range, ignore num. - */ - new_set = num; - for (start = -1, end = i = 0; i < chain->n_rules; i++) { - if (keep_rule(chain->map[i], cmd, new_set, 0)) - continue; - if (start < 0) - start = i; - end = i; - n++; - } - end++; /* first non-matching */ - } else { - /* Optimized search on rule numbers */ - start = ipfw_find_rule(chain, num, 0); - for (end = start; end < chain->n_rules; end++) { - rule = chain->map[end]; - if (num > 0 && rule->rulenum != num) - break; - if (!keep_rule(rule, cmd, new_set, num)) - n++; - } - } - - if (n == 0) { - /* A flush request (arg == 0 or cmd == 1) on empty - * ruleset returns with no error. On the contrary, - * if there is no match on a specific request, - * we return EINVAL. - */ - if (arg != 0 && cmd != 1) - error = EINVAL; - break; - } - - /* We have something to delete. Allocate the new map */ - map = get_map(chain, -n, 1 /* locked */); - if (map == NULL) { - error = EINVAL; - break; - } - - /* 1. bcopy the initial part of the map */ - if (start > 0) - bcopy(chain->map, map, start * sizeof(struct ip_fw *)); - /* 2. copy active rules between start and end */ - for (i = ofs = start; i < end; i++) { - rule = chain->map[i]; - if (keep_rule(rule, cmd, new_set, num)) - map[ofs++] = rule; - } - /* 3. copy the final part of the map */ - bcopy(chain->map + end, map + ofs, - (chain->n_rules - end) * sizeof(struct ip_fw *)); - /* 4. swap the maps (under BH_LOCK) */ - map = swap_map(chain, map, chain->n_rules - n); - /* 5. now remove the rules deleted from the old map */ - for (i = start; i < end; i++) { - int l; - rule = map[i]; - if (keep_rule(rule, cmd, new_set, num)) - continue; - l = RULESIZE(rule); - chain->static_len -= l; - ipfw_remove_dyn_children(rule); - rule->x_next = chain->reap; - chain->reap = rule; - } - break; - - /* - * In the next 3 cases the loop stops at (n_rules - 1) - * because the default rule is never eligible.. - */ - - case 2: /* move rules with given RULE number to new set */ - for (i = 0; i < chain->n_rules - 1; i++) { - rule = chain->map[i]; - if (rule->rulenum == num) - rule->set = new_set; - } - break; - - case 3: /* move rules with given SET number to new set */ - for (i = 0; i < chain->n_rules - 1; i++) { - rule = chain->map[i]; - if (rule->set == num) - rule->set = new_set; - } - break; - - case 4: /* swap two sets */ - for (i = 0; i < chain->n_rules - 1; i++) { - rule = chain->map[i]; - if (rule->set == num) - rule->set = new_set; - else if (rule->set == new_set) - rule->set = num; - } - break; - } - - rule = chain->reap; - chain->reap = NULL; - IPFW_UH_WUNLOCK(chain); - ipfw_reap_rules(rule); - if (map) - free(map, M_IPFW); - return error; -} - -/* - * Clear counters for a specific rule. - * Normally run under IPFW_UH_RLOCK, but these are idempotent ops - * so we only care that rules do not disappear. - */ -static void -clear_counters(struct ip_fw *rule, int log_only) -{ - ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule); - - if (log_only == 0) { - rule->bcnt = rule->pcnt = 0; - rule->timestamp = 0; - } - if (l->o.opcode == O_LOG) - l->log_left = l->max_log; -} - -/** - * Reset some or all counters on firewall rules. - * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, - * the next 8 bits are the set number, the top 8 bits are the command: - * 0 work with rules from all set's; - * 1 work with rules only from specified set. - * Specified rule number is zero if we want to clear all entries. - * log_only is 1 if we only want to reset logs, zero otherwise. - */ -static int -zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) -{ - struct ip_fw *rule; - char *msg; - int i; - - uint16_t rulenum = arg & 0xffff; - uint8_t set = (arg >> 16) & 0xff; - uint8_t cmd = (arg >> 24) & 0xff; - - if (cmd > 1) - return (EINVAL); - if (cmd == 1 && set > RESVD_SET) - return (EINVAL); - - IPFW_UH_RLOCK(chain); - if (rulenum == 0) { - V_norule_counter = 0; - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - /* Skip rules not in our set. */ - if (cmd == 1 && rule->set != set) - continue; - clear_counters(rule, log_only); - } - msg = log_only ? "All logging counts reset" : - "Accounting cleared"; - } else { - int cleared = 0; - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - if (rule->rulenum == rulenum) { - if (cmd == 0 || rule->set == set) - clear_counters(rule, log_only); - cleared = 1; - } - if (rule->rulenum > rulenum) - break; - } - if (!cleared) { /* we did not find any matching rules */ - IPFW_UH_RUNLOCK(chain); - return (EINVAL); - } - msg = log_only ? "logging count reset" : "cleared"; - } - IPFW_UH_RUNLOCK(chain); - - if (V_fw_verbose) { - int lev = LOG_SECURITY | LOG_NOTICE; - - if (rulenum) - log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); - else - log(lev, "ipfw: %s.\n", msg); - } - return (0); -} - -/* - * Check validity of the structure before insert. - * Rules are simple, so this mostly need to check rule sizes. - */ -static int -check_ipfw_struct(struct ip_fw *rule, int size) -{ - int l, cmdlen = 0; - int have_action=0; - ipfw_insn *cmd; - - if (size < sizeof(*rule)) { - printf("ipfw: rule too short\n"); - return (EINVAL); - } - /* first, check for valid size */ - l = RULESIZE(rule); - if (l != size) { - printf("ipfw: size mismatch (have %d want %d)\n", size, l); - return (EINVAL); - } - if (rule->act_ofs >= rule->cmd_len) { - printf("ipfw: bogus action offset (%u > %u)\n", - rule->act_ofs, rule->cmd_len - 1); - return (EINVAL); - } - /* - * Now go for the individual checks. Very simple ones, basically only - * instruction sizes. - */ - for (l = rule->cmd_len, cmd = rule->cmd ; - l > 0 ; l -= cmdlen, cmd += cmdlen) { - cmdlen = F_LEN(cmd); - if (cmdlen > l) { - printf("ipfw: opcode %d size truncated\n", - cmd->opcode); - return EINVAL; - } - switch (cmd->opcode) { - case O_PROBE_STATE: - case O_KEEP_STATE: - case O_PROTO: - case O_IP_SRC_ME: - case O_IP_DST_ME: - case O_LAYER2: - case O_IN: - case O_FRAG: - case O_DIVERTED: - case O_IPOPT: - case O_IPTOS: - case O_IPPRECEDENCE: - case O_IPVER: - case O_SOCKARG: - case O_TCPFLAGS: - case O_TCPOPTS: - case O_ESTAB: - case O_VERREVPATH: - case O_VERSRCREACH: - case O_ANTISPOOF: - case O_IPSEC: -#ifdef INET6 - case O_IP6_SRC_ME: - case O_IP6_DST_ME: - case O_EXT_HDR: - case O_IP6: -#endif - case O_IP4: - case O_TAG: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - break; - - case O_FIB: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - if (cmd->arg1 >= rt_numfibs) { - printf("ipfw: invalid fib number %d\n", - cmd->arg1); - return EINVAL; - } - break; - - case O_SETFIB: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - if ((cmd->arg1 != IP_FW_TABLEARG) && - (cmd->arg1 >= rt_numfibs)) { - printf("ipfw: invalid fib number %d\n", - cmd->arg1); - return EINVAL; - } - goto check_action; - - case O_UID: - case O_GID: - case O_JAIL: - case O_IP_SRC: - case O_IP_DST: - case O_TCPSEQ: - case O_TCPACK: - case O_PROB: - case O_ICMPTYPE: - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - break; - - case O_LIMIT: - if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) - goto bad_size; - break; - - case O_LOG: - if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) - goto bad_size; - - ((ipfw_insn_log *)cmd)->log_left = - ((ipfw_insn_log *)cmd)->max_log; - - break; - - case O_IP_SRC_MASK: - case O_IP_DST_MASK: - /* only odd command lengths */ - if ( !(cmdlen & 1) || cmdlen > 31) - goto bad_size; - break; - - case O_IP_SRC_SET: - case O_IP_DST_SET: - if (cmd->arg1 == 0 || cmd->arg1 > 256) { - printf("ipfw: invalid set size %d\n", - cmd->arg1); - return EINVAL; - } - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + - (cmd->arg1+31)/32 ) - goto bad_size; - break; - - case O_IP_SRC_LOOKUP: - case O_IP_DST_LOOKUP: - if (cmd->arg1 >= IPFW_TABLES_MAX) { - printf("ipfw: invalid table number %d\n", - cmd->arg1); - return (EINVAL); - } - if (cmdlen != F_INSN_SIZE(ipfw_insn) && - cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && - cmdlen != F_INSN_SIZE(ipfw_insn_u32)) - goto bad_size; - break; - case O_MACADDR2: - if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) - goto bad_size; - break; - - case O_NOP: - case O_IPID: - case O_IPTTL: - case O_IPLEN: - case O_TCPDATALEN: - case O_TCPWIN: - case O_TAGGED: - if (cmdlen < 1 || cmdlen > 31) - goto bad_size; - break; - - case O_MAC_TYPE: - case O_IP_SRCPORT: - case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ - if (cmdlen < 2 || cmdlen > 31) - goto bad_size; - break; - - case O_RECV: - case O_XMIT: - case O_VIA: - if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) - goto bad_size; - break; - - case O_ALTQ: - if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) - goto bad_size; - break; - - case O_PIPE: - case O_QUEUE: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; - goto check_action; - - case O_FORWARD_IP: -#ifdef IPFIREWALL_FORWARD - if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) - goto bad_size; - goto check_action; -#else - return EINVAL; -#endif - -#ifdef INET6 - case O_FORWARD_IP6: -#ifdef IPFIREWALL_FORWARD - if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) - goto bad_size; - goto check_action; -#else - return (EINVAL); -#endif -#endif /* INET6 */ - - case O_DIVERT: - case O_TEE: - if (ip_divert_ptr == NULL) - return EINVAL; - else - goto check_size; - case O_NETGRAPH: - case O_NGTEE: - if (ng_ipfw_input_p == NULL) - return EINVAL; - else - goto check_size; - case O_NAT: - if (!IPFW_NAT_LOADED) - return EINVAL; - if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) - goto bad_size; - goto check_action; - case O_FORWARD_MAC: /* XXX not implemented yet */ - case O_CHECK_STATE: - case O_COUNT: - case O_ACCEPT: - case O_DENY: - case O_REJECT: -#ifdef INET6 - case O_UNREACH6: -#endif - case O_SKIPTO: - case O_REASS: - case O_CALLRETURN: -check_size: - if (cmdlen != F_INSN_SIZE(ipfw_insn)) - goto bad_size; -check_action: - if (have_action) { - printf("ipfw: opcode %d, multiple actions" - " not allowed\n", - cmd->opcode); - return EINVAL; - } - have_action = 1; - if (l != cmdlen) { - printf("ipfw: opcode %d, action must be" - " last opcode\n", - cmd->opcode); - return EINVAL; - } - break; -#ifdef INET6 - case O_IP6_SRC: - case O_IP6_DST: - if (cmdlen != F_INSN_SIZE(struct in6_addr) + - F_INSN_SIZE(ipfw_insn)) - goto bad_size; - break; - - case O_FLOW6ID: - if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + - ((ipfw_insn_u32 *)cmd)->o.arg1) - goto bad_size; - break; - - case O_IP6_SRC_MASK: - case O_IP6_DST_MASK: - if ( !(cmdlen & 1) || cmdlen > 127) - goto bad_size; - break; - case O_ICMP6TYPE: - if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) - goto bad_size; - break; -#endif - - default: - switch (cmd->opcode) { -#ifndef INET6 - case O_IP6_SRC_ME: - case O_IP6_DST_ME: - case O_EXT_HDR: - case O_IP6: - case O_UNREACH6: - case O_IP6_SRC: - case O_IP6_DST: - case O_FLOW6ID: - case O_IP6_SRC_MASK: - case O_IP6_DST_MASK: - case O_ICMP6TYPE: - printf("ipfw: no IPv6 support in kernel\n"); - return EPROTONOSUPPORT; -#endif - default: - printf("ipfw: opcode %d, unknown opcode\n", - cmd->opcode); - return EINVAL; - } - } - } - if (have_action == 0) { - printf("ipfw: missing action\n"); - return EINVAL; - } - return 0; - -bad_size: - printf("ipfw: opcode %d size %d wrong\n", - cmd->opcode, cmdlen); - return EINVAL; -} - - -/* - * Translation of requests for compatibility with FreeBSD 7.2/8. - * a static variable tells us if we have an old client from userland, - * and if necessary we translate requests and responses between the - * two formats. - */ -static int is7 = 0; - -struct ip_fw7 { - struct ip_fw7 *next; /* linked list of rules */ - struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ - /* 'next_rule' is used to pass up 'set_disable' status */ - - uint16_t act_ofs; /* offset of action in 32-bit units */ - uint16_t cmd_len; /* # of 32-bit words in cmd */ - uint16_t rulenum; /* rule number */ - uint8_t set; /* rule set (0..31) */ - // #define RESVD_SET 31 /* set for default and persistent rules */ - uint8_t _pad; /* padding */ - // uint32_t id; /* rule id, only in v.8 */ - /* These fields are present in all rules. */ - uint64_t pcnt; /* Packet counter */ - uint64_t bcnt; /* Byte counter */ - uint32_t timestamp; /* tv_sec of last match */ - - ipfw_insn cmd[1]; /* storage for commands */ -}; - - int convert_rule_to_7(struct ip_fw *rule); -int convert_rule_to_8(struct ip_fw *rule); - -#ifndef RULESIZE7 -#define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ - ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) -#endif - - -/* - * Copy the static and dynamic rules to the supplied buffer - * and return the amount of space actually used. - * Must be run under IPFW_UH_RLOCK - */ -static size_t -ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) -{ - char *bp = buf; - char *ep = bp + space; - struct ip_fw *rule, *dst; - int l, i; - time_t boot_seconds; - - boot_seconds = boottime.tv_sec; - for (i = 0; i < chain->n_rules; i++) { - rule = chain->map[i]; - - if (is7) { - /* Convert rule to FreeBSd 7.2 format */ - l = RULESIZE7(rule); - if (bp + l + sizeof(uint32_t) <= ep) { - int error; - bcopy(rule, bp, l + sizeof(uint32_t)); - error = convert_rule_to_7((struct ip_fw *) bp); - if (error) - return 0; /*XXX correct? */ - /* - * XXX HACK. Store the disable mask in the "next" - * pointer in a wild attempt to keep the ABI the same. - * Why do we do this on EVERY rule? - */ - bcopy(&V_set_disable, - &(((struct ip_fw7 *)bp)->next_rule), - sizeof(V_set_disable)); - if (((struct ip_fw7 *)bp)->timestamp) - ((struct ip_fw7 *)bp)->timestamp += boot_seconds; - bp += l; - } - continue; /* go to next rule */ - } - - /* normal mode, don't touch rules */ - l = RULESIZE(rule); - if (bp + l > ep) { /* should not happen */ - printf("overflow dumping static rules\n"); - break; - } - dst = (struct ip_fw *)bp; - bcopy(rule, dst, l); - /* - * XXX HACK. Store the disable mask in the "next" - * pointer in a wild attempt to keep the ABI the same. - * Why do we do this on EVERY rule? - */ - bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); - if (dst->timestamp) - dst->timestamp += boot_seconds; - bp += l; - } - ipfw_get_dynamic(&bp, ep); /* protected by the dynamic lock */ - return (bp - (char *)buf); -} - - -#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader)) -/** - * {set|get}sockopt parser. - */ -int -ipfw_ctl(struct sockopt *sopt) -{ -#define RULE_MAXSIZE (256*sizeof(u_int32_t)) - int error; - size_t size, len, valsize; - struct ip_fw *buf, *rule; - struct ip_fw_chain *chain; - u_int32_t rulenum[2]; - uint32_t opt; - char xbuf[128]; - ip_fw3_opheader *op3 = NULL; - - error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); - if (error) - return (error); - - /* - * Disallow modifications in really-really secure mode, but still allow - * the logging counters to be reset. - */ - if (sopt->sopt_name == IP_FW_ADD || - (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) { - error = securelevel_ge(sopt->sopt_td->td_ucred, 3); - if (error) - return (error); - } - - chain = &V_layer3_chain; - error = 0; - - /* Save original valsize before it is altered via sooptcopyin() */ - valsize = sopt->sopt_valsize; - if ((opt = sopt->sopt_name) == IP_FW3) { - /* - * Copy not less than sizeof(ip_fw3_opheader). - * We hope any IP_FW3 command will fit into 128-byte buffer. - */ - if ((error = sooptcopyin(sopt, xbuf, sizeof(xbuf), - sizeof(ip_fw3_opheader))) != 0) - return (error); - op3 = (ip_fw3_opheader *)xbuf; - opt = op3->opcode; - } - - switch (opt) { - case IP_FW_GET: - /* - * pass up a copy of the current rules. Static rules - * come first (the last of which has number IPFW_DEFAULT_RULE), - * followed by a possibly empty list of dynamic rule. - * The last dynamic rule has NULL in the "next" field. - * - * Note that the calculated size is used to bound the - * amount of data returned to the user. The rule set may - * change between calculating the size and returning the - * data in which case we'll just return what fits. - */ - for (;;) { - int len = 0, want; - - size = chain->static_len; - size += ipfw_dyn_len(); - if (size >= sopt->sopt_valsize) - break; - buf = malloc(size, M_TEMP, M_WAITOK); - IPFW_UH_RLOCK(chain); - /* check again how much space we need */ - want = chain->static_len + ipfw_dyn_len(); - if (size >= want) - len = ipfw_getrules(chain, buf, size); - IPFW_UH_RUNLOCK(chain); - if (size >= want) - error = sooptcopyout(sopt, buf, len); - free(buf, M_TEMP); - if (size >= want) - break; - } - break; - - case IP_FW_FLUSH: - /* locking is done within del_entry() */ - error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ - break; - - case IP_FW_ADD: - rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, rule, RULE_MAXSIZE, - sizeof(struct ip_fw7) ); - - /* - * If the size of commands equals RULESIZE7 then we assume - * a FreeBSD7.2 binary is talking to us (set is7=1). - * is7 is persistent so the next 'ipfw list' command - * will use this format. - * NOTE: If wrong version is guessed (this can happen if - * the first ipfw command is 'ipfw [pipe] list') - * the ipfw binary may crash or loop infinitly... - */ - if (sopt->sopt_valsize == RULESIZE7(rule)) { - is7 = 1; - error = convert_rule_to_8(rule); - if (error) - return error; - if (error == 0) - error = check_ipfw_struct(rule, RULESIZE(rule)); - } else { - is7 = 0; - if (error == 0) - error = check_ipfw_struct(rule, sopt->sopt_valsize); - } - if (error == 0) { - /* locking is done within ipfw_add_rule() */ - error = ipfw_add_rule(chain, rule); - size = RULESIZE(rule); - if (!error && sopt->sopt_dir == SOPT_GET) { - if (is7) { - error = convert_rule_to_7(rule); - size = RULESIZE7(rule); - if (error) - return error; - } - error = sooptcopyout(sopt, rule, size); - } - } - free(rule, M_TEMP); - break; - - case IP_FW_DEL: - /* - * IP_FW_DEL is used for deleting single rules or sets, - * and (ab)used to atomically manipulate sets. Argument size - * is used to distinguish between the two: - * sizeof(u_int32_t) - * delete single rule or set of rules, - * or reassign rules (or sets) to a different set. - * 2*sizeof(u_int32_t) - * atomic disable/enable sets. - * first u_int32_t contains sets to be disabled, - * second u_int32_t contains sets to be enabled. - */ - error = sooptcopyin(sopt, rulenum, - 2*sizeof(u_int32_t), sizeof(u_int32_t)); - if (error) - break; - size = sopt->sopt_valsize; - if (size == sizeof(u_int32_t) && rulenum[0] != 0) { - /* delete or reassign, locking done in del_entry() */ - error = del_entry(chain, rulenum[0]); - } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ - IPFW_UH_WLOCK(chain); - V_set_disable = - (V_set_disable | rulenum[0]) & ~rulenum[1] & - ~(1<sopt_val != 0) { - error = sooptcopyin(sopt, rulenum, - sizeof(u_int32_t), sizeof(u_int32_t)); - if (error) - break; - } - error = zero_entry(chain, rulenum[0], - sopt->sopt_name == IP_FW_RESETLOG); - break; - - /*--- TABLE manipulations are protected by the IPFW_LOCK ---*/ - case IP_FW_TABLE_ADD: - { - ipfw_table_entry ent; - - error = sooptcopyin(sopt, &ent, - sizeof(ent), sizeof(ent)); - if (error) - break; - error = ipfw_add_table_entry(chain, ent.tbl, - &ent.addr, sizeof(ent.addr), ent.masklen, - IPFW_TABLE_CIDR, ent.value); - } - break; - - case IP_FW_TABLE_DEL: - { - ipfw_table_entry ent; - - error = sooptcopyin(sopt, &ent, - sizeof(ent), sizeof(ent)); - if (error) - break; - error = ipfw_del_table_entry(chain, ent.tbl, - &ent.addr, sizeof(ent.addr), ent.masklen, IPFW_TABLE_CIDR); - } - break; - - case IP_FW_TABLE_XADD: /* IP_FW3 */ - case IP_FW_TABLE_XDEL: /* IP_FW3 */ - { - ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); - - /* Check minimum header size */ - if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { - error = EINVAL; - break; - } - - /* Check if len field is valid */ - if (xent->len > sizeof(ipfw_table_xentry)) { - error = EINVAL; - break; - } - - len = xent->len - offsetof(ipfw_table_xentry, k); - - error = (opt == IP_FW_TABLE_XADD) ? - ipfw_add_table_entry(chain, xent->tbl, &xent->k, - len, xent->masklen, xent->type, xent->value) : - ipfw_del_table_entry(chain, xent->tbl, &xent->k, - len, xent->masklen, xent->type); - } - break; - - case IP_FW_TABLE_FLUSH: - { - u_int16_t tbl; - - error = sooptcopyin(sopt, &tbl, - sizeof(tbl), sizeof(tbl)); - if (error) - break; - error = ipfw_flush_table(chain, tbl); - } - break; - - case IP_FW_TABLE_GETSIZE: - { - u_int32_t tbl, cnt; - - if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), - sizeof(tbl)))) - break; - IPFW_RLOCK(chain); - error = ipfw_count_table(chain, tbl, &cnt); - IPFW_RUNLOCK(chain); - if (error) - break; - error = sooptcopyout(sopt, &cnt, sizeof(cnt)); - } - break; - - case IP_FW_TABLE_LIST: - { - ipfw_table *tbl; - - if (sopt->sopt_valsize < sizeof(*tbl)) { - error = EINVAL; - break; - } - size = sopt->sopt_valsize; - tbl = malloc(size, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); - if (error) { - free(tbl, M_TEMP); - break; - } - tbl->size = (size - sizeof(*tbl)) / - sizeof(ipfw_table_entry); - IPFW_RLOCK(chain); - error = ipfw_dump_table(chain, tbl); - IPFW_RUNLOCK(chain); - if (error) { - free(tbl, M_TEMP); - break; - } - error = sooptcopyout(sopt, tbl, size); - free(tbl, M_TEMP); - } - break; - - case IP_FW_TABLE_XGETSIZE: /* IP_FW3 */ - { - uint32_t *tbl; - - if (IP_FW3_OPLENGTH(sopt) < sizeof(uint32_t)) { - error = EINVAL; - break; - } - - tbl = (uint32_t *)(op3 + 1); - - IPFW_RLOCK(chain); - error = ipfw_count_xtable(chain, *tbl, tbl); - IPFW_RUNLOCK(chain); - if (error) - break; - error = sooptcopyout(sopt, op3, sopt->sopt_valsize); - } - break; - - case IP_FW_TABLE_XLIST: /* IP_FW3 */ - { - ipfw_xtable *tbl; - - if ((size = valsize) < sizeof(ipfw_xtable)) { - error = EINVAL; - break; - } - - tbl = malloc(size, M_TEMP, M_ZERO | M_WAITOK); - memcpy(tbl, op3, sizeof(ipfw_xtable)); - - /* Get maximum number of entries we can store */ - tbl->size = (size - sizeof(ipfw_xtable)) / - sizeof(ipfw_table_xentry); - IPFW_RLOCK(chain); - error = ipfw_dump_xtable(chain, tbl); - IPFW_RUNLOCK(chain); - if (error) { - free(tbl, M_TEMP); - break; - } - - /* Revert size field back to bytes */ - tbl->size = tbl->size * sizeof(ipfw_table_xentry) + - sizeof(ipfw_table); - /* - * Since we call sooptcopyin() with small buffer, sopt_valsize is - * decreased to reflect supplied buffer size. Set it back to original value - */ - sopt->sopt_valsize = valsize; - error = sooptcopyout(sopt, tbl, size); - free(tbl, M_TEMP); - } - break; - - /*--- NAT operations are protected by the IPFW_LOCK ---*/ - case IP_FW_NAT_CFG: - if (IPFW_NAT_LOADED) - error = ipfw_nat_cfg_ptr(sopt); - else { - printf("IP_FW_NAT_CFG: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - case IP_FW_NAT_DEL: - if (IPFW_NAT_LOADED) - error = ipfw_nat_del_ptr(sopt); - else { - printf("IP_FW_NAT_DEL: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - case IP_FW_NAT_GET_CONFIG: - if (IPFW_NAT_LOADED) - error = ipfw_nat_get_cfg_ptr(sopt); - else { - printf("IP_FW_NAT_GET_CFG: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - case IP_FW_NAT_GET_LOG: - if (IPFW_NAT_LOADED) - error = ipfw_nat_get_log_ptr(sopt); - else { - printf("IP_FW_NAT_GET_LOG: %s\n", - "ipfw_nat not present, please load it"); - error = EINVAL; - } - break; - - default: - printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); - error = EINVAL; - } - - return (error); -#undef RULE_MAXSIZE -} - - -#define RULE_MAXSIZE (256*sizeof(u_int32_t)) - -/* Functions to convert rules 7.2 <==> 8.0 */ -int -convert_rule_to_7(struct ip_fw *rule) -{ - /* Used to modify original rule */ - struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; - /* copy of original rule, version 8 */ - struct ip_fw *tmp; - - /* Used to copy commands */ - ipfw_insn *ccmd, *dst; - int ll = 0, ccmdlen = 0; - - tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); - if (tmp == NULL) { - return 1; //XXX error - } - bcopy(rule, tmp, RULE_MAXSIZE); - - /* Copy fields */ - rule7->_pad = tmp->_pad; - rule7->set = tmp->set; - rule7->rulenum = tmp->rulenum; - rule7->cmd_len = tmp->cmd_len; - rule7->act_ofs = tmp->act_ofs; - rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; - rule7->next = (struct ip_fw7 *)tmp->x_next; - rule7->cmd_len = tmp->cmd_len; - rule7->pcnt = tmp->pcnt; - rule7->bcnt = tmp->bcnt; - rule7->timestamp = tmp->timestamp; - - /* Copy commands */ - for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; - ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { - ccmdlen = F_LEN(ccmd); - - bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); - - if (dst->opcode > O_NAT) - /* O_REASS doesn't exists in 7.2 version, so - * decrement opcode if it is after O_REASS - */ - dst->opcode--; - - if (ccmdlen > ll) { - printf("ipfw: opcode %d size truncated\n", - ccmd->opcode); - return EINVAL; - } - } - free(tmp, M_TEMP); - - return 0; -} - -int -convert_rule_to_8(struct ip_fw *rule) -{ - /* Used to modify original rule */ - struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; - - /* Used to copy commands */ - ipfw_insn *ccmd, *dst; - int ll = 0, ccmdlen = 0; - - /* Copy of original rule */ - struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); - if (tmp == NULL) { - return 1; //XXX error - } - - bcopy(rule7, tmp, RULE_MAXSIZE); - - for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; - ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { - ccmdlen = F_LEN(ccmd); - - bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); - - if (dst->opcode > O_NAT) - /* O_REASS doesn't exists in 7.2 version, so - * increment opcode if it is after O_REASS - */ - dst->opcode++; - - if (ccmdlen > ll) { - printf("ipfw: opcode %d size truncated\n", - ccmd->opcode); - return EINVAL; - } - } - - rule->_pad = tmp->_pad; - rule->set = tmp->set; - rule->rulenum = tmp->rulenum; - rule->cmd_len = tmp->cmd_len; - rule->act_ofs = tmp->act_ofs; - rule->next_rule = (struct ip_fw *)tmp->next_rule; - rule->x_next = (struct ip_fw *)tmp->next; - rule->cmd_len = tmp->cmd_len; - rule->id = 0; /* XXX see if is ok = 0 */ - rule->pcnt = tmp->pcnt; - rule->bcnt = tmp->bcnt; - rule->timestamp = tmp->timestamp; - - free (tmp, M_TEMP); - return 0; -} - -/* end of file */ diff --git a/sys/netinet/ipfw/ip_fw_table.c b/sys/netinet/ipfw/ip_fw_table.c deleted file mode 100644 index 68a6220195cbb..0000000000000 --- a/sys/netinet/ipfw/ip_fw_table.c +++ /dev/null @@ -1,761 +0,0 @@ -/*- - * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. - * - * 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 -__FBSDID("$FreeBSD$"); - -/* - * Lookup table support for ipfw - * - * Lookup tables are implemented (at the moment) using the radix - * tree used for routing tables. Tables store key-value entries, where - * keys are network prefixes (addr/masklen), and values are integers. - * As a degenerate case we can interpret keys as 32-bit integers - * (with a /32 mask). - * - * The table is protected by the IPFW lock even for manipulation coming - * from userland, because operations are typically fast. - */ - -#include "opt_ipfw.h" -#include "opt_inet.h" -#ifndef INET -#error IPFIREWALL requires INET. -#endif /* INET */ -#include "opt_inet6.h" - -#include -#include -#include -#include -#include -#include -#include -#include /* ip_fw.h requires IFNAMSIZ */ -#include -#include -#include - -#include -#include /* struct ipfw_rule_ref */ -#include -#include /* LIST_HEAD */ -#include - -#ifdef MAC -#include -#endif - -static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); - -struct table_entry { - struct radix_node rn[2]; - struct sockaddr_in addr, mask; - u_int32_t value; -}; - -struct xaddr_iface { - uint8_t if_len; /* length of this struct */ - uint8_t pad[7]; /* Align name */ - char ifname[IF_NAMESIZE]; /* Interface name */ -}; - -struct table_xentry { - struct radix_node rn[2]; - union { -#ifdef INET6 - struct sockaddr_in6 addr6; -#endif - struct xaddr_iface iface; - } a; - union { -#ifdef INET6 - struct sockaddr_in6 mask6; -#endif - struct xaddr_iface ifmask; - } m; - u_int32_t value; -}; - -/* - * The radix code expects addr and mask to be array of bytes, - * with the first byte being the length of the array. rn_inithead - * is called with the offset in bits of the lookup key within the - * array. If we use a sockaddr_in as the underlying type, - * sin_len is conveniently located at offset 0, sin_addr is at - * offset 4 and normally aligned. - * But for portability, let's avoid assumption and make the code explicit - */ -#define KEY_LEN(v) *((uint8_t *)&(v)) -#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) -/* - * Do not require radix to compare more than actual IPv4/IPv6 address - */ -#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) -#define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr)) -#define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname)) - -#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) -#define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr)) -#define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) - - -static inline void -ipv6_writemask(struct in6_addr *addr6, uint8_t mask) -{ - uint32_t *cp; - - for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) - *cp++ = 0xFFFFFFFF; - *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); -} - -int -ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) -{ - struct radix_node_head *rnh, **rnh_ptr; - struct table_entry *ent; - struct table_xentry *xent; - struct radix_node *rn; - in_addr_t addr; - int offset; - void *ent_ptr; - struct sockaddr *addr_ptr, *mask_ptr; - char c; - - if (tbl >= V_fw_tables_max) - return (EINVAL); - - switch (type) { - case IPFW_TABLE_CIDR: - if (plen == sizeof(in_addr_t)) { -#ifdef INET - /* IPv4 case */ - if (mlen > 32) - return (EINVAL); - ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); - ent->value = value; - /* Set 'total' structure length */ - KEY_LEN(ent->addr) = KEY_LEN_INET; - KEY_LEN(ent->mask) = KEY_LEN_INET; - /* Set offset of IPv4 address in bits */ - offset = OFF_LEN_INET; - ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); - addr = *((in_addr_t *)paddr); - ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; - /* Set pointers */ - rnh_ptr = &ch->tables[tbl]; - ent_ptr = ent; - addr_ptr = (struct sockaddr *)&ent->addr; - mask_ptr = (struct sockaddr *)&ent->mask; -#endif -#ifdef INET6 - } else if (plen == sizeof(struct in6_addr)) { - /* IPv6 case */ - if (mlen > 128) - return (EINVAL); - xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); - xent->value = value; - /* Set 'total' structure length */ - KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; - KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; - /* Set offset of IPv6 address in bits */ - offset = OFF_LEN_INET6; - ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); - memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); - APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); - /* Set pointers */ - rnh_ptr = &ch->xtables[tbl]; - ent_ptr = xent; - addr_ptr = (struct sockaddr *)&xent->a.addr6; - mask_ptr = (struct sockaddr *)&xent->m.mask6; -#endif - } else { - /* Unknown CIDR type */ - return (EINVAL); - } - break; - - case IPFW_TABLE_INTERFACE: - /* Check if string is terminated */ - c = ((char *)paddr)[IF_NAMESIZE - 1]; - ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; - if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) - return (EINVAL); - - /* Include last \0 into comparison */ - mlen++; - - xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); - xent->value = value; - /* Set 'total' structure length */ - KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; - KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; - /* Set offset of interface name in bits */ - offset = OFF_LEN_IFACE; - memcpy(xent->a.iface.ifname, paddr, mlen); - /* Assume direct match */ - /* TODO: Add interface pattern matching */ -#if 0 - memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); - mask_ptr = (struct sockaddr *)&xent->m.ifmask; -#endif - /* Set pointers */ - rnh_ptr = &ch->xtables[tbl]; - ent_ptr = xent; - addr_ptr = (struct sockaddr *)&xent->a.iface; - mask_ptr = NULL; - break; - - default: - return (EINVAL); - } - - IPFW_WLOCK(ch); - - /* Check if tabletype is valid */ - if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { - IPFW_WUNLOCK(ch); - free(ent_ptr, M_IPFW_TBL); - return (EINVAL); - } - - /* Check if radix tree exists */ - if ((rnh = *rnh_ptr) == NULL) { - IPFW_WUNLOCK(ch); - /* Create radix for a new table */ - if (!rn_inithead((void **)&rnh, offset)) { - free(ent_ptr, M_IPFW_TBL); - return (ENOMEM); - } - - IPFW_WLOCK(ch); - if (*rnh_ptr != NULL) { - /* Tree is already attached by other thread */ - rn_detachhead((void **)&rnh); - rnh = *rnh_ptr; - /* Check table type another time */ - if (ch->tabletype[tbl] != type) { - IPFW_WUNLOCK(ch); - free(ent_ptr, M_IPFW_TBL); - return (EINVAL); - } - } else { - *rnh_ptr = rnh; - /* - * Set table type. It can be set already - * (if we have IPv6-only table) but setting - * it another time does not hurt - */ - ch->tabletype[tbl] = type; - } - } - - rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); - IPFW_WUNLOCK(ch); - - if (rn == NULL) { - free(ent_ptr, M_IPFW_TBL); - return (EEXIST); - } - return (0); -} - -int -ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type) -{ - struct radix_node_head *rnh, **rnh_ptr; - struct table_entry *ent; - in_addr_t addr; - struct sockaddr_in sa, mask; - struct sockaddr *sa_ptr, *mask_ptr; - char c; - - if (tbl >= V_fw_tables_max) - return (EINVAL); - - switch (type) { - case IPFW_TABLE_CIDR: - if (plen == sizeof(in_addr_t)) { - /* Set 'total' structure length */ - KEY_LEN(sa) = KEY_LEN_INET; - KEY_LEN(mask) = KEY_LEN_INET; - mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); - addr = *((in_addr_t *)paddr); - sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; - rnh_ptr = &ch->tables[tbl]; - sa_ptr = (struct sockaddr *)&sa; - mask_ptr = (struct sockaddr *)&mask; -#ifdef INET6 - } else if (plen == sizeof(struct in6_addr)) { - /* IPv6 case */ - if (mlen > 128) - return (EINVAL); - struct sockaddr_in6 sa6, mask6; - memset(&sa6, 0, sizeof(struct sockaddr_in6)); - memset(&mask6, 0, sizeof(struct sockaddr_in6)); - /* Set 'total' structure length */ - KEY_LEN(sa6) = KEY_LEN_INET6; - KEY_LEN(mask6) = KEY_LEN_INET6; - ipv6_writemask(&mask6.sin6_addr, mlen); - memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); - APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); - rnh_ptr = &ch->xtables[tbl]; - sa_ptr = (struct sockaddr *)&sa6; - mask_ptr = (struct sockaddr *)&mask6; -#endif - } else { - /* Unknown CIDR type */ - return (EINVAL); - } - break; - - case IPFW_TABLE_INTERFACE: - /* Check if string is terminated */ - c = ((char *)paddr)[IF_NAMESIZE - 1]; - ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; - if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) - return (EINVAL); - - struct xaddr_iface ifname, ifmask; - memset(&ifname, 0, sizeof(ifname)); - - /* Include last \0 into comparison */ - mlen++; - - /* Set 'total' structure length */ - KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; - KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; - /* Assume direct match */ - /* FIXME: Add interface pattern matching */ -#if 0 - memset(ifmask.ifname, 0xFF, IF_NAMESIZE); - mask_ptr = (struct sockaddr *)&ifmask; -#endif - mask_ptr = NULL; - memcpy(ifname.ifname, paddr, mlen); - /* Set pointers */ - rnh_ptr = &ch->xtables[tbl]; - sa_ptr = (struct sockaddr *)&ifname; - - break; - - default: - return (EINVAL); - } - - IPFW_WLOCK(ch); - if ((rnh = *rnh_ptr) == NULL) { - IPFW_WUNLOCK(ch); - return (ESRCH); - } - - if (ch->tabletype[tbl] != type) { - IPFW_WUNLOCK(ch); - return (EINVAL); - } - - ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); - IPFW_WUNLOCK(ch); - - if (ent == NULL) - return (ESRCH); - - free(ent, M_IPFW_TBL); - return (0); -} - -static int -flush_table_entry(struct radix_node *rn, void *arg) -{ - struct radix_node_head * const rnh = arg; - struct table_entry *ent; - - ent = (struct table_entry *) - rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); - if (ent != NULL) - free(ent, M_IPFW_TBL); - return (0); -} - -int -ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) -{ - struct radix_node_head *rnh, *xrnh; - - if (tbl >= V_fw_tables_max) - return (EINVAL); - - /* - * We free both (IPv4 and extended) radix trees and - * clear table type here to permit table to be reused - * for different type without module reload - */ - - IPFW_WLOCK(ch); - /* Set IPv4 table pointer to zero */ - if ((rnh = ch->tables[tbl]) != NULL) - ch->tables[tbl] = NULL; - /* Set extended table pointer to zero */ - if ((xrnh = ch->xtables[tbl]) != NULL) - ch->xtables[tbl] = NULL; - /* Zero table type */ - ch->tabletype[tbl] = 0; - IPFW_WUNLOCK(ch); - - if (rnh != NULL) { - rnh->rnh_walktree(rnh, flush_table_entry, rnh); - rn_detachhead((void **)&rnh); - } - - if (xrnh != NULL) { - xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); - rn_detachhead((void **)&xrnh); - } - - return (0); -} - -void -ipfw_destroy_tables(struct ip_fw_chain *ch) -{ - uint16_t tbl; - - /* Flush all tables */ - for (tbl = 0; tbl < V_fw_tables_max; tbl++) - ipfw_flush_table(ch, tbl); - - /* Free pointers itself */ - free(ch->tables, M_IPFW); - free(ch->xtables, M_IPFW); - free(ch->tabletype, M_IPFW); -} - -int -ipfw_init_tables(struct ip_fw_chain *ch) -{ - /* Allocate pointers */ - ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); - ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); - ch->tabletype = malloc(V_fw_tables_max * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); - return (0); -} - -int -ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) -{ - struct radix_node_head **tables, **xtables, *rnh; - struct radix_node_head **tables_old, **xtables_old; - uint8_t *tabletype, *tabletype_old; - unsigned int ntables_old, tbl; - - /* Check new value for validity */ - if (ntables > IPFW_TABLES_MAX) - ntables = IPFW_TABLES_MAX; - - /* Allocate new pointers */ - tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); - xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); - tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); - - IPFW_WLOCK(ch); - - tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; - - /* Copy old table pointers */ - memcpy(tables, ch->tables, sizeof(void *) * tbl); - memcpy(xtables, ch->xtables, sizeof(void *) * tbl); - memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl); - - /* Change pointers and number of tables */ - tables_old = ch->tables; - xtables_old = ch->xtables; - tabletype_old = ch->tabletype; - ch->tables = tables; - ch->xtables = xtables; - ch->tabletype = tabletype; - - ntables_old = V_fw_tables_max; - V_fw_tables_max = ntables; - - IPFW_WUNLOCK(ch); - - /* Check if we need to destroy radix trees */ - if (ntables < ntables_old) { - for (tbl = ntables; tbl < ntables_old; tbl++) { - if ((rnh = tables_old[tbl]) != NULL) { - rnh->rnh_walktree(rnh, flush_table_entry, rnh); - rn_detachhead((void **)&rnh); - } - - if ((rnh = xtables_old[tbl]) != NULL) { - rnh->rnh_walktree(rnh, flush_table_entry, rnh); - rn_detachhead((void **)&rnh); - } - } - } - - /* Free old pointers */ - free(tables_old, M_IPFW); - free(xtables_old, M_IPFW); - free(tabletype_old, M_IPFW); - - return (0); -} - -int -ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint32_t *val) -{ - struct radix_node_head *rnh; - struct table_entry *ent; - struct sockaddr_in sa; - - if (tbl >= V_fw_tables_max) - return (0); - if ((rnh = ch->tables[tbl]) == NULL) - return (0); - KEY_LEN(sa) = KEY_LEN_INET; - sa.sin_addr.s_addr = addr; - ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); - if (ent != NULL) { - *val = ent->value; - return (1); - } - return (0); -} - -int -ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint32_t *val, int type) -{ - struct radix_node_head *rnh; - struct table_xentry *xent; - struct sockaddr_in6 sa6; - struct xaddr_iface iface; - - if (tbl >= V_fw_tables_max) - return (0); - if ((rnh = ch->xtables[tbl]) == NULL) - return (0); - - switch (type) { - case IPFW_TABLE_CIDR: - KEY_LEN(sa6) = KEY_LEN_INET6; - memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); - xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); - break; - - case IPFW_TABLE_INTERFACE: - KEY_LEN(iface) = KEY_LEN_IFACE + - strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; - /* Assume direct match */ - /* FIXME: Add interface pattern matching */ - xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); - break; - - default: - return (0); - } - - if (xent != NULL) { - *val = xent->value; - return (1); - } - return (0); -} - -static int -count_table_entry(struct radix_node *rn, void *arg) -{ - u_int32_t * const cnt = arg; - - (*cnt)++; - return (0); -} - -int -ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) -{ - struct radix_node_head *rnh; - - if (tbl >= V_fw_tables_max) - return (EINVAL); - *cnt = 0; - if ((rnh = ch->tables[tbl]) == NULL) - return (0); - rnh->rnh_walktree(rnh, count_table_entry, cnt); - return (0); -} - -static int -dump_table_entry(struct radix_node *rn, void *arg) -{ - struct table_entry * const n = (struct table_entry *)rn; - ipfw_table * const tbl = arg; - ipfw_table_entry *ent; - - if (tbl->cnt == tbl->size) - return (1); - ent = &tbl->ent[tbl->cnt]; - ent->tbl = tbl->tbl; - if (in_nullhost(n->mask.sin_addr)) - ent->masklen = 0; - else - ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); - ent->addr = n->addr.sin_addr.s_addr; - ent->value = n->value; - tbl->cnt++; - return (0); -} - -int -ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) -{ - struct radix_node_head *rnh; - - if (tbl->tbl >= V_fw_tables_max) - return (EINVAL); - tbl->cnt = 0; - if ((rnh = ch->tables[tbl->tbl]) == NULL) - return (0); - rnh->rnh_walktree(rnh, dump_table_entry, tbl); - return (0); -} - -static int -count_table_xentry(struct radix_node *rn, void *arg) -{ - uint32_t * const cnt = arg; - - (*cnt) += sizeof(ipfw_table_xentry); - return (0); -} - -int -ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) -{ - struct radix_node_head *rnh; - - if (tbl >= V_fw_tables_max) - return (EINVAL); - *cnt = 0; - if ((rnh = ch->tables[tbl]) != NULL) - rnh->rnh_walktree(rnh, count_table_xentry, cnt); - if ((rnh = ch->xtables[tbl]) != NULL) - rnh->rnh_walktree(rnh, count_table_xentry, cnt); - /* Return zero if table is empty */ - if (*cnt > 0) - (*cnt) += sizeof(ipfw_xtable); - return (0); -} - - -static int -dump_table_xentry_base(struct radix_node *rn, void *arg) -{ - struct table_entry * const n = (struct table_entry *)rn; - ipfw_xtable * const tbl = arg; - ipfw_table_xentry *xent; - - /* Out of memory, returning */ - if (tbl->cnt == tbl->size) - return (1); - xent = &tbl->xent[tbl->cnt]; - xent->len = sizeof(ipfw_table_xentry); - xent->tbl = tbl->tbl; - if (in_nullhost(n->mask.sin_addr)) - xent->masklen = 0; - else - xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); - /* Save IPv4 address as deprecated IPv6 compatible */ - xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; - xent->value = n->value; - tbl->cnt++; - return (0); -} - -static int -dump_table_xentry_extended(struct radix_node *rn, void *arg) -{ - struct table_xentry * const n = (struct table_xentry *)rn; - ipfw_xtable * const tbl = arg; - ipfw_table_xentry *xent; -#ifdef INET6 - int i; - uint32_t *v; -#endif - /* Out of memory, returning */ - if (tbl->cnt == tbl->size) - return (1); - xent = &tbl->xent[tbl->cnt]; - xent->len = sizeof(ipfw_table_xentry); - xent->tbl = tbl->tbl; - - switch (tbl->type) { -#ifdef INET6 - case IPFW_TABLE_CIDR: - /* Count IPv6 mask */ - v = (uint32_t *)&n->m.mask6.sin6_addr; - for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++) - xent->masklen += bitcount32(*v); - memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr)); - break; -#endif - case IPFW_TABLE_INTERFACE: - /* Assume exact mask */ - xent->masklen = 8 * IF_NAMESIZE; - memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); - break; - - default: - /* unknown, skip entry */ - return (0); - } - - xent->value = n->value; - tbl->cnt++; - return (0); -} - -int -ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl) -{ - struct radix_node_head *rnh; - - if (tbl->tbl >= V_fw_tables_max) - return (EINVAL); - tbl->cnt = 0; - tbl->type = ch->tabletype[tbl->tbl]; - if ((rnh = ch->tables[tbl->tbl]) != NULL) - rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl); - if ((rnh = ch->xtables[tbl->tbl]) != NULL) - rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl); - return (0); -} - -/* end of file */ diff --git a/sys/netinet/ipfw/test/Makefile b/sys/netinet/ipfw/test/Makefile deleted file mode 100644 index c556a4bf3d517..0000000000000 --- a/sys/netinet/ipfw/test/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# -# $FreeBSD$ -# -# Makefile for building userland tests -# this is written in a form compatible with gmake - -SCHED_SRCS = test_dn_sched.c -SCHED_SRCS += dn_sched_fifo.c -SCHED_SRCS += dn_sched_prio.c -SCHED_SRCS += dn_sched_qfq.c -SCHED_SRCS += dn_sched_rr.c -SCHED_SRCS += dn_sched_wf2q.c -SCHED_SRCS += dn_heap.c -SCHED_SRCS += main.c - -SCHED_OBJS=$(SCHED_SRCS:.c=.o) - -HEAP_SRCS = dn_heap.c test_dn_heap.c -HEAP_OBJS=$(HEAP_SRCS:.c=.o) - -VPATH= .:.. - -CFLAGS = -I.. -I. -Wall -Werror -O3 -DIPFW -TARGETS= test_sched # no test_heap by default - -all: $(TARGETS) - -test_heap : $(HEAP_OBJS) - $(CC) -o $@ $(HEAP_OBJS) - -test_sched : $(SCHED_OBJS) - $(CC) -o $@ $(SCHED_OBJS) - -$(SCHED_OBJS): dn_test.h -main.o: mylist.h - -clean: - - rm *.o $(TARGETS) *.core - -ALLSRCS = $(SCHED_SRCS) dn_test.h mylist.h \ - dn_sched.h dn_heap.h ip_dn_private.h Makefile -TMPBASE = /tmp/testXYZ -TMPDIR = $(TMPBASE)/test - -tgz: - -rm -rf $(TMPDIR) - mkdir -p $(TMPDIR) - -cp -p $(ALLSRCS) $(TMPDIR) - -(cd ..; cp -p $(ALLSRCS) $(TMPDIR)) - ls -la $(TMPDIR) - (cd $(TMPBASE); tar cvzf /tmp/test.tgz test) diff --git a/sys/netinet/ipfw/test/dn_test.h b/sys/netinet/ipfw/test/dn_test.h deleted file mode 100644 index 4e079bc4d68b8..0000000000000 --- a/sys/netinet/ipfw/test/dn_test.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * $FreeBSD$ - * - * userspace compatibility code for dummynet schedulers - */ - -#ifndef _DN_TEST_H -#define _DN_TEST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include /* bzero, ffs, ... */ -#include /* strcmp */ -#include -#include -#include - -extern int debug; -#define ND(fmt, args...) do {} while (0) -#define D1(fmt, args...) do {} while (0) -#define D(fmt, args...) fprintf(stderr, "%-8s " fmt "\n", \ - __FUNCTION__, ## args) -#define DX(lev, fmt, args...) do { \ - if (debug > lev) D(fmt, ## args); } while (0) - - -#ifndef offsetof -#define offsetof(t,m) (int)((&((t *)0L)->m)) -#endif - -#include - -/* prevent include of other system headers */ -#define _NETINET_IP_VAR_H_ /* ip_fw_args */ -#define _IPFW2_H -#define _SYS_MBUF_H_ - -enum { - DN_QUEUE, -}; - -enum { - DN_SCHED_FIFO, - DN_SCHED_WF2QP, -}; - -struct dn_id { - int type, subtype, len, id; -}; - -struct dn_fs { - int par[4]; /* flowset parameters */ - - /* simulation entries. - * 'index' is not strictly necessary - * y is used for the inverse mapping , - */ - int index; - int y; /* inverse mapping */ - int base_y; /* inverse mapping */ - int next_y; /* inverse mapping */ - int n_flows; - int first_flow; - int next_flow; /* first_flow + n_flows */ - /* - * when generating, let 'cur' go from 0 to n_flows-1, - * then point to flow first_flow + cur - */ - int cur; -}; - -struct dn_sch { -}; - -struct dn_flow { - struct dn_id oid; - int length; - int len_bytes; - int drops; - uint64_t tot_bytes; - uint32_t flow_id; - struct list_head h; /* used by the generator */ -}; - -struct dn_link { -}; - -struct ip_fw_args { -}; - -struct mbuf { - struct { - int len; - } m_pkthdr; - struct mbuf *m_nextpkt; - int flow_id; /* for testing, index of a flow */ - //int flowset_id; /* for testing, index of a flowset */ - void *cfg; /* config args */ -}; - -#define MALLOC_DECLARE(x) -#define KASSERT(x, y) do { if (!(x)) printf y ; exit(0); } while (0) -struct ipfw_flow_id { -}; - -typedef void * module_t; - -struct _md_t { - const char *name; - int (*f)(module_t, int, void *); - void *p; -}; - -typedef struct _md_t moduledata_t; - -#define DECLARE_MODULE(name, b, c, d) \ - moduledata_t *_g_##name = & b -#define MODULE_DEPEND(a, b, c, d, e) - -#ifdef IPFW -#include -#include -#include -#else -struct dn_queue { - struct dn_fsk *fs; /* parent flowset. */ - struct dn_sch_inst *_si; /* parent sched instance. */ -}; -struct dn_schk { -}; -struct dn_fsk { - struct dn_fs fs; - struct dn_schk *sched; -}; -struct dn_sch_inst { - struct dn_schk *sched; -}; -struct dn_alg { - int type; - const char *name; - void *enqueue, *dequeue; - int q_datalen, si_datalen, schk_datalen; - int (*config)(struct dn_schk *); - int (*new_sched)(struct dn_sch_inst *); - int (*new_fsk)(struct dn_fsk *); - int (*new_queue)(struct dn_queue *q); -}; - -#endif - -#ifndef __FreeBSD__ -int fls(int); -#endif - -static inline void -mq_append(struct mq *q, struct mbuf *m) -{ - if (q->head == NULL) - q->head = m; - else - q->tail->m_nextpkt = m; - q->tail = m; - m->m_nextpkt = NULL; -} - -#ifdef __cplusplus -} -#endif - -#endif /* _DN_TEST_H */ diff --git a/sys/netinet/ipfw/test/main.c b/sys/netinet/ipfw/test/main.c deleted file mode 100644 index be9fdf53612c3..0000000000000 --- a/sys/netinet/ipfw/test/main.c +++ /dev/null @@ -1,636 +0,0 @@ -/* - * $FreeBSD$ - * - * Testing program for schedulers - * - * The framework include a simple controller which, at each - * iteration, decides whether we can enqueue and/or dequeue. - * Then the mainloop runs the required number of tests, - * keeping track of statistics. - */ - -#include "dn_test.h" - -struct q_list { - struct list_head h; -}; - -struct cfg_s { - int ac; - char * const *av; - - const char *name; - int loops; - struct timeval time; - - /* running counters */ - uint32_t _enqueue; - uint32_t drop; - uint32_t pending; - uint32_t dequeue; - - /* generator parameters */ - int th_min, th_max; - int maxburst; - int lmin, lmax; /* packet len */ - int flows; /* number of flows */ - int flowsets; /* number of flowsets */ - int wsum; /* sum of weights of all flows */ - int max_y; /* max random number in the generation */ - int cur_y, cur_fs; /* used in generation, between 0 and max_y - 1 */ - const char *fs_config; /* flowset config */ - int can_dequeue; - int burst; /* count of packets sent in a burst */ - struct mbuf *tosend; /* packet to send -- also flag to enqueue */ - - struct mbuf *freelist; - - struct mbuf *head, *tail; /* a simple tailq */ - - /* scheduler hooks */ - int (*enq)(struct dn_sch_inst *, struct dn_queue *, - struct mbuf *); - struct mbuf * (*deq)(struct dn_sch_inst *); - /* size of the three fields including sched-specific areas */ - int schk_len; - int q_len; /* size of a queue including sched-fields */ - int si_len; /* size of a sch_inst including sched-fields */ - char *q; /* array of flow queues */ - /* use a char* because size is variable */ - struct dn_fsk *fs; /* array of flowsets */ - struct dn_sch_inst *si; - struct dn_schk *sched; - - /* generator state */ - int state; /* 0 = going up, 1: going down */ - - /* - * We keep lists for each backlog level, and always serve - * the one with shortest backlog. llmask contains a bitmap - * of lists, and ll are the heads of the lists. The last - * entry (BACKLOG) contains all entries considered 'full' - * XXX to optimize things, entry i could contain queues with - * 2^{i-1}+1 .. 2^i entries. - */ -#define BACKLOG 30 - uint32_t llmask; - struct list_head ll[BACKLOG + 10]; -}; - -/* FI2Q and Q2FI converts from flow_id to dn_queue and back. - * We cannot easily use pointer arithmetic because it is variable size. - */ -#define FI2Q(c, i) ((struct dn_queue *)((c)->q + (c)->q_len * (i))) -#define Q2FI(c, q) (((char *)(q) - (c)->q)/(c)->q_len) - -int debug = 0; - -struct dn_parms dn_cfg; - -static void controller(struct cfg_s *c); - -/* release a packet: put the mbuf in the freelist, and the queue in - * the bucket. - */ -int -drop(struct cfg_s *c, struct mbuf *m) -{ - struct dn_queue *q; - int i; - - c->drop++; - q = FI2Q(c, m->flow_id); - i = q->ni.length; // XXX or ffs... - - ND("q %p id %d current length %d", q, m->flow_id, i); - if (i < BACKLOG) { - struct list_head *h = &q->ni.h; - c->llmask &= ~(1<<(i+1)); - c->llmask |= (1<<(i)); - list_del(h); - list_add_tail(h, &c->ll[i]); - } - m->m_nextpkt = c->freelist; - c->freelist = m; - return 0; -} - -/* dequeue returns NON-NULL when a packet is dropped */ -static int -enqueue(struct cfg_s *c, void *_m) -{ - struct mbuf *m = _m; - if (c->enq) - return c->enq(c->si, FI2Q(c, m->flow_id), m); - if (c->head == NULL) - c->head = m; - else - c->tail->m_nextpkt = m; - c->tail = m; - return 0; /* default - success */ -} - -/* dequeue returns NON-NULL when a packet is available */ -static void * -dequeue(struct cfg_s *c) -{ - struct mbuf *m; - if (c->deq) - return c->deq(c->si); - if ((m = c->head)) { - m = c->head; - c->head = m->m_nextpkt; - m->m_nextpkt = NULL; - } - return m; -} - -static int -mainloop(struct cfg_s *c) -{ - int i; - struct mbuf *m; - - for (i=0; i < c->loops; i++) { - /* implement histeresis */ - controller(c); - DX(3, "loop %d enq %d send %p rx %d", - i, c->_enqueue, c->tosend, c->can_dequeue); - if ( (m = c->tosend) ) { - c->_enqueue++; - if (enqueue(c, m)) { - drop(c, m); - ND("loop %d enqueue fail", i ); - } else { - ND("enqueue ok"); - c->pending++; - } - } - if (c->can_dequeue) { - c->dequeue++; - if ((m = dequeue(c))) { - c->pending--; - drop(c, m); - c->drop--; /* compensate */ - } - } - } - DX(1, "mainloop ends %d", i); - return 0; -} - -int -dump(struct cfg_s *c) -{ - int i; - struct dn_queue *q; - - for (i=0; i < c->flows; i++) { - q = FI2Q(c, i); - DX(1, "queue %4d tot %10lld", i, q->ni.tot_bytes); - } - DX(1, "done %d loops\n", c->loops); - return 0; -} - -/* interpret a number in human form */ -static long -getnum(const char *s, char **next, const char *key) -{ - char *end = NULL; - long l; - - if (next) /* default */ - *next = NULL; - if (s && *s) { - DX(3, "token is <%s> %s", s, key ? key : "-"); - l = strtol(s, &end, 0); - } else { - DX(3, "empty string"); - l = -1; - } - if (l < 0) { - DX(2, "invalid %s for %s", s ? s : "NULL", (key ? key : "") ); - return 0; // invalid - } - if (!end || !*end) - return l; - if (*end == 'n') - l = -l; /* multiply by n */ - else if (*end == 'K') - l = l*1000; - else if (*end == 'M') - l = l*1000000; - else if (*end == 'k') - l = l*1024; - else if (*end == 'm') - l = l*1024*1024; - else if (*end == 'w') - ; - else {/* not recognized */ - D("suffix %s for %s, next %p", end, key, next); - end--; - } - end++; - DX(3, "suffix now %s for %s, next %p", end, key, next); - if (next && *end) { - DX(3, "setting next to %s for %s", end, key); - *next = end; - } - return l; -} - -/* - * flowsets are a comma-separated list of - * weight:maxlen:flows - * indicating how many flows are hooked to that fs. - * Both weight and range can be min-max-steps. - * In a first pass we just count the number of flowsets and flows, - * in a second pass we complete the setup. - */ -static void -parse_flowsets(struct cfg_s *c, const char *fs, int pass) -{ - char *s, *cur, *next; - int n_flows = 0, n_fs = 0, wsum = 0; - int i, j; - struct dn_fs *prev = NULL; - - DX(3, "--- pass %d flows %d flowsets %d", pass, c->flows, c->flowsets); - if (pass == 0) - c->fs_config = fs; - s = c->fs_config ? strdup(c->fs_config) : NULL; - if (s == NULL) { - if (pass == 0) - D("no fsconfig"); - return; - } - for (next = s; (cur = strsep(&next, ","));) { - char *p = NULL; - int w, w_h, w_steps, wi; - int len, len_h, l_steps, li; - int flows; - - w = getnum(strsep(&cur, ":"), &p, "weight"); - if (w <= 0) - w = 1; - w_h = p ? getnum(p+1, &p, "weight_max") : w; - w_steps = p ? getnum(p+1, &p, "w_steps") : (w_h == w ?1:2); - len = getnum(strsep(&cur, ":"), &p, "len"); - if (len <= 0) - len = 1000; - len_h = p ? getnum(p+1, &p, "len_max") : len; - l_steps = p ? getnum(p+1, &p, "l_steps") : (len_h == len ? 1 : 2); - flows = getnum(strsep(&cur, ":"), NULL, "flows"); - if (flows == 0) - flows = 1; - DX(4, "weight %d..%d (%d) len %d..%d (%d) flows %d", - w, w_h, w_steps, len, len_h, l_steps, flows); - if (w == 0 || w_h < w || len == 0 || len_h < len || - flows == 0) { - DX(4,"wrong parameters %s", fs); - return; - } - n_flows += flows * w_steps * l_steps; - for (i = 0; i < w_steps; i++) { - wi = w + ((w_h - w)* i)/(w_steps == 1 ? 1 : (w_steps-1)); - for (j = 0; j < l_steps; j++, n_fs++) { - struct dn_fs *fs = &c->fs[n_fs].fs; // tentative - int x; - - li = len + ((len_h - len)* j)/(l_steps == 1 ? 1 : (l_steps-1)); - x = (wi*2048)/li; - DX(3, "----- fs %4d weight %4d lmax %4d X %4d flows %d", - n_fs, wi, li, x, flows); - if (pass == 0) - continue; - if (c->fs == NULL || c->flowsets <= n_fs) { - D("error in number of flowsets"); - return; - } - wsum += wi * flows; - fs->par[0] = wi; - fs->par[1] = li; - fs->index = n_fs; - fs->n_flows = flows; - fs->cur = fs->first_flow = prev==NULL ? 0 : prev->next_flow; - fs->next_flow = fs->first_flow + fs->n_flows; - fs->y = x * flows; - fs->base_y = (prev == NULL) ? 0 : prev->next_y; - fs->next_y = fs->base_y + fs->y; - prev = fs; - } - } - } - c->max_y = prev ? prev->base_y + prev->y : 0; - c->flows = n_flows; - c->flowsets = n_fs; - c->wsum = wsum; - if (pass == 0) - return; - - /* now link all flows to their parent flowsets */ - DX(1,"%d flows on %d flowsets max_y %d", c->flows, c->flowsets, c->max_y); - for (i=0; i < c->flowsets; i++) { - struct dn_fs *fs = &c->fs[i].fs; - DX(1, "fs %3d w %5d l %4d flow %5d .. %5d y %6d .. %6d", - i, fs->par[0], fs->par[1], - fs->first_flow, fs->next_flow, - fs->base_y, fs->next_y); - for (j = fs->first_flow; j < fs->next_flow; j++) { - struct dn_queue *q = FI2Q(c, j); - q->fs = &c->fs[i]; - } - } -} - -static int -init(struct cfg_s *c) -{ - int i; - int ac = c->ac; - char * const *av = c->av; - - c->si_len = sizeof(struct dn_sch_inst); - c->q_len = sizeof(struct dn_queue); - moduledata_t *mod = NULL; - struct dn_alg *p = NULL; - - c->th_min = 0; - c->th_max = -20;/* 20 packets per flow */ - c->lmin = c->lmax = 1280; /* packet len */ - c->flows = 1; - c->flowsets = 1; - c->name = "null"; - ac--; av++; - while (ac > 1) { - if (!strcmp(*av, "-n")) { - c->loops = getnum(av[1], NULL, av[0]); - } else if (!strcmp(*av, "-d")) { - debug = atoi(av[1]); - } else if (!strcmp(*av, "-alg")) { - extern moduledata_t *_g_dn_fifo; - extern moduledata_t *_g_dn_wf2qp; - extern moduledata_t *_g_dn_rr; - extern moduledata_t *_g_dn_qfq; -#ifdef WITH_KPS - extern moduledata_t *_g_dn_kps; -#endif - if (!strcmp(av[1], "rr")) - mod = _g_dn_rr; - else if (!strcmp(av[1], "wf2qp")) - mod = _g_dn_wf2qp; - else if (!strcmp(av[1], "fifo")) - mod = _g_dn_fifo; - else if (!strcmp(av[1], "qfq")) - mod = _g_dn_qfq; -#ifdef WITH_KPS - else if (!strcmp(av[1], "kps")) - mod = _g_dn_kps; -#endif - else - mod = NULL; - c->name = mod ? mod->name : "NULL"; - DX(3, "using scheduler %s", c->name); - } else if (!strcmp(*av, "-len")) { - c->lmin = getnum(av[1], NULL, av[0]); - c->lmax = c->lmin; - DX(3, "setting max to %d", c->th_max); - } else if (!strcmp(*av, "-burst")) { - c->maxburst = getnum(av[1], NULL, av[0]); - DX(3, "setting max to %d", c->th_max); - } else if (!strcmp(*av, "-qmax")) { - c->th_max = getnum(av[1], NULL, av[0]); - DX(3, "setting max to %d", c->th_max); - } else if (!strcmp(*av, "-qmin")) { - c->th_min = getnum(av[1], NULL, av[0]); - DX(3, "setting min to %d", c->th_min); - } else if (!strcmp(*av, "-flows")) { - c->flows = getnum(av[1], NULL, av[0]); - DX(3, "setting flows to %d", c->flows); - } else if (!strcmp(*av, "-flowsets")) { - parse_flowsets(c, av[1], 0); - DX(3, "setting flowsets to %d", c->flowsets); - } else { - D("option %s not recognised, ignore", *av); - } - ac -= 2; av += 2; - } - if (c->maxburst <= 0) - c->maxburst = 1; - if (c->loops <= 0) - c->loops = 1; - if (c->flows <= 0) - c->flows = 1; - if (c->flowsets <= 0) - c->flowsets = 1; - if (c->lmin <= 0) - c->lmin = 1; - if (c->lmax <= 0) - c->lmax = 1; - /* multiply by N */ - if (c->th_min < 0) - c->th_min = c->flows * -c->th_min; - if (c->th_max < 0) - c->th_max = c->flows * -c->th_max; - if (c->th_max <= c->th_min) - c->th_max = c->th_min + 1; - if (mod) { - p = mod->p; - DX(3, "using module %s f %p p %p", mod->name, mod->f, mod->p); - DX(3, "modname %s ty %d", p->name, p->type); - c->enq = p->enqueue; - c->deq = p->dequeue; - c->si_len += p->si_datalen; - c->q_len += p->q_datalen; - c->schk_len += p->schk_datalen; - } - /* allocate queues, flowsets and one scheduler */ - c->q = calloc(c->flows, c->q_len); - c->fs = calloc(c->flowsets, sizeof(struct dn_fsk)); - c->si = calloc(1, c->si_len); - c->sched = calloc(c->flows, c->schk_len); - if (c->q == NULL || c->fs == NULL) { - D("error allocating memory for flows"); - exit(1); - } - c->si->sched = c->sched; - if (p) { - if (p->config) - p->config(c->sched); - if (p->new_sched) - p->new_sched(c->si); - } - /* parse_flowsets links queues to their flowsets */ - parse_flowsets(c, av[1], 1); - /* complete the work calling new_fsk */ - for (i = 0; i < c->flowsets; i++) { - if (c->fs[i].fs.par[1] == 0) - c->fs[i].fs.par[1] = 1000; /* default pkt len */ - c->fs[i].sched = c->sched; - if (p && p->new_fsk) - p->new_fsk(&c->fs[i]); - } - - /* initialize the lists for the generator, and put - * all flows in the list for backlog = 0 - */ - for (i=0; i <= BACKLOG+5; i++) - INIT_LIST_HEAD(&c->ll[i]); - - for (i = 0; i < c->flows; i++) { - struct dn_queue *q = FI2Q(c, i); - if (q->fs == NULL) - q->fs = &c->fs[0]; /* XXX */ - q->_si = c->si; - if (p && p->new_queue) - p->new_queue(q); - INIT_LIST_HEAD(&q->ni.h); - list_add_tail(&q->ni.h, &c->ll[0]); - } - c->llmask = 1; - return 0; -} - - -int -main(int ac, char *av[]) -{ - struct cfg_s c; - struct timeval end; - double ll; - int i; - char msg[40]; - - bzero(&c, sizeof(c)); - c.ac = ac; - c.av = av; - init(&c); - gettimeofday(&c.time, NULL); - mainloop(&c); - gettimeofday(&end, NULL); - end.tv_sec -= c.time.tv_sec; - end.tv_usec -= c.time.tv_usec; - if (end.tv_usec < 0) { - end.tv_usec += 1000000; - end.tv_sec--; - } - c.time = end; - ll = end.tv_sec*1000000 + end.tv_usec; - ll *= 1000; /* convert to nanoseconds */ - ll /= c._enqueue; - sprintf(msg, "1::%d", c.flows); - D("%-8s n %d %d time %d.%06d %8.3f qlen %d %d flows %s drops %d", - c.name, c._enqueue, c.loops, - (int)c.time.tv_sec, (int)c.time.tv_usec, ll, - c.th_min, c.th_max, - c.fs_config ? c.fs_config : msg, c.drop); - dump(&c); - DX(1, "done ac %d av %p", ac, av); - for (i=0; i < ac; i++) - DX(1, "arg %d %s", i, av[i]); - return 0; -} - -/* - * The controller decides whether in this iteration we should send - * (the packet is in c->tosend) and/or receive (flag c->can_dequeue) - */ -static void -controller(struct cfg_s *c) -{ - struct mbuf *m; - struct dn_fs *fs; - int flow_id; - - /* histeresis between max and min */ - if (c->state == 0 && c->pending >= c->th_max) - c->state = 1; - else if (c->state == 1 && c->pending <= c->th_min) - c->state = 0; - ND(1, "state %d pending %2d", c->state, c->pending); - c->can_dequeue = c->state; - c->tosend = NULL; - if (c->state) - return; - - if (1) { - int i; - struct dn_queue *q; - struct list_head *h; - - i = ffs(c->llmask) - 1; - if (i < 0) { - DX(2, "no candidate"); - c->can_dequeue = 1; - return; - } - h = &c->ll[i]; - ND(1, "backlog %d p %p prev %p next %p", i, h, h->prev, h->next); - q = list_first_entry(h, struct dn_queue, ni.h); - list_del(&q->ni.h); - flow_id = Q2FI(c, q); - DX(2, "extracted flow %p %d backlog %d", q, flow_id, i); - if (list_empty(h)) { - ND(2, "backlog %d empty", i); - c->llmask &= ~(1<ni.h, h+1); - ND(1, " after %d p %p prev %p next %p", i+1, h+1, h[1].prev, h[1].next); - if (i < BACKLOG) { - ND(2, "backlog %d full", i+1); - c->llmask |= 1<<(1+i); - } - fs = &q->fs->fs; - c->cur_fs = q->fs - c->fs; - fs->cur = flow_id; - } else { - /* XXX this does not work ? */ - /* now decide whom to send the packet, and the length */ - /* lookup in the flow table */ - if (c->cur_y >= c->max_y) { /* handle wraparound */ - c->cur_y = 0; - c->cur_fs = 0; - } - fs = &c->fs[c->cur_fs].fs; - flow_id = fs->cur++; - if (fs->cur >= fs->next_flow) - fs->cur = fs->first_flow; - c->cur_y++; - if (c->cur_y >= fs->next_y) - c->cur_fs++; - } - - /* construct a packet */ - if (c->freelist) { - m = c->tosend = c->freelist; - c->freelist = c->freelist->m_nextpkt; - } else { - m = c->tosend = calloc(1, sizeof(struct mbuf)); - } - if (m == NULL) - return; - - m->cfg = c; - m->m_nextpkt = NULL; - m->m_pkthdr.len = fs->par[1]; // XXX maxlen - m->flow_id = flow_id; - - ND(2,"y %6d flow %5d fs %3d weight %4d len %4d", - c->cur_y, m->flow_id, c->cur_fs, - fs->par[0], m->m_pkthdr.len); - -} - -/* -Packet allocation: -to achieve a distribution that matches weights, for each X=w/lmax class -we should generate a number of packets proportional to Y = X times the number -of flows in the class. -So we construct an array with the cumulative distribution of Y's, -and use it to identify the flow via inverse mapping (if the Y's are -not too many we can use an array for the lookup). In practice, -each flow will have X entries [virtually] pointing to it. - -*/ diff --git a/sys/netinet/ipfw/test/mylist.h b/sys/netinet/ipfw/test/mylist.h deleted file mode 100644 index 6247f32ea4e44..0000000000000 --- a/sys/netinet/ipfw/test/mylist.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * $FreeBSD$ - * - * linux-like bidirectional lists - */ - -#ifndef _MYLIST_H -#define _MYLIST_H -struct list_head { - struct list_head *prev, *next; -}; - -#define INIT_LIST_HEAD(l) do { (l)->prev = (l)->next = (l); } while (0) -#define list_empty(l) ( (l)->next == l ) -static inline void -__list_add(struct list_head *o, struct list_head *prev, - struct list_head *next) -{ - next->prev = o; - o->next = next; - o->prev = prev; - prev->next = o; -} - -static inline void -list_add_tail(struct list_head *o, struct list_head *head) -{ - __list_add(o, head->prev, head); -} - -#define list_first_entry(pL, ty, member) \ - (ty *)((char *)((pL)->next) - offsetof(ty, member)) - -static inline void -__list_del(struct list_head *prev, struct list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -static inline void -list_del(struct list_head *entry) -{ - ND("called on %p", entry); - __list_del(entry->prev, entry->next); - entry->next = entry->prev = NULL; -} - -#endif /* _MYLIST_H */ diff --git a/sys/netinet/ipfw/test/test_dn_heap.c b/sys/netinet/ipfw/test/test_dn_heap.c deleted file mode 100644 index d460cf2ff36b7..0000000000000 --- a/sys/netinet/ipfw/test/test_dn_heap.c +++ /dev/null @@ -1,162 +0,0 @@ -/*- - * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa - * 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. - */ - -/* - * Userland code for testing binary heaps and hash tables - * - * $FreeBSD$ - */ - -#include -#include - -#include -#include -#include - -#include "dn_heap.h" -#define log(x, arg...) fprintf(stderr, ## arg) -#define panic(x...) fprintf(stderr, ## x), exit(1) - -#include - -struct x { - struct x *ht_link; - char buf[0]; -}; - -uint32_t hf(uintptr_t key, int flags, void *arg) -{ - return (flags & DNHT_KEY_IS_OBJ) ? - ((struct x *)key)->buf[0] : *(char *)key; -} - -int matchf(void *obj, uintptr_t key, int flags, void *arg) -{ - char *s = (flags & DNHT_KEY_IS_OBJ) ? - ((struct x *)key)->buf : (char *)key; - return (strcmp(((struct x *)obj)->buf, s) == 0); -} - -void *newfn(uintptr_t key, int flags, void *arg) -{ - char *s = (char *)key; - struct x *p = malloc(sizeof(*p) + 1 + strlen(s)); - if (p) - strcpy(p->buf, s); - return p; -} - -char *strings[] = { - "undici", "unico", "doppio", "devoto", - "uno", "due", "tre", "quattro", "cinque", "sei", - "uno", "due", "tre", "quattro", "cinque", "sei", - NULL, -}; - -int doprint(void *_x, void *arg) -{ - struct x *x = _x; - printf("found element <%s>\n", x->buf); - return (int)arg; -} - -static void -test_hash() -{ - char **p; - struct dn_ht *h; - uintptr_t x = 0; - uintptr_t x1 = 0; - - /* first, find and allocate */ - h = dn_ht_init(NULL, 10, 0, hf, matchf, newfn); - - for (p = strings; *p; p++) { - dn_ht_find(h, (uintptr_t)*p, DNHT_INSERT, NULL); - } - dn_ht_scan(h, doprint, 0); - printf("/* second -- find without allocate */\n"); - h = dn_ht_init(NULL, 10, 0, hf, matchf, NULL); - for (p = strings; *p; p++) { - void **y = newfn((uintptr_t)*p, 0, NULL); - if (x == 0) - x = (uintptr_t)y; - else { - if (x1 == 0) - x1 = (uintptr_t)*p; - } - dn_ht_find(h, (uintptr_t)y, DNHT_INSERT | DNHT_KEY_IS_OBJ, NULL); - } - dn_ht_scan(h, doprint, 0); - printf("remove %p gives %p\n", (void *)x, - dn_ht_find(h, x, DNHT_KEY_IS_OBJ | DNHT_REMOVE, NULL)); - printf("remove %p gives %p\n", (void *)x, - dn_ht_find(h, x, DNHT_KEY_IS_OBJ | DNHT_REMOVE, NULL)); - printf("remove %p gives %p\n", (void *)x, - dn_ht_find(h, x1, DNHT_REMOVE, NULL)); - printf("remove %p gives %p\n", (void *)x, - dn_ht_find(h, x1, DNHT_REMOVE, NULL)); - dn_ht_scan(h, doprint, 0); -} - -int -main(int argc, char *argv[]) -{ - struct dn_heap h; - int i, n, n2, n3; - - test_hash(); - return 0; - - /* n = elements, n2 = cycles */ - n = (argc > 1) ? atoi(argv[1]) : 0; - if (n <= 0 || n > 1000000) - n = 100; - n2 = (argc > 2) ? atoi(argv[2]) : 0; - if (n2 <= 0) - n = 1000000; - n3 = (argc > 3) ? atoi(argv[3]) : 0; - bzero(&h, sizeof(h)); - heap_init(&h, n, -1); - while (n2-- > 0) { - uint64_t prevk = 0; - for (i=0; i < n; i++) - heap_insert(&h, n3 ? n-i: random(), (void *)(100+i)); - - for (i=0; h.elements > 0; i++) { - uint64_t k = h.p[0].key; - if (k < prevk) - panic("wrong sequence\n"); - prevk = k; - if (0) - printf("%d key %llu, val %p\n", - i, h.p[0].key, h.p[0].object); - heap_extract(&h, NULL); - } - } - return 0; -} diff --git a/sys/netinet/ipfw/test/test_dn_sched.c b/sys/netinet/ipfw/test/test_dn_sched.c deleted file mode 100644 index ee46c95ed8689..0000000000000 --- a/sys/netinet/ipfw/test/test_dn_sched.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * $FreeBSD$ - * - * library functions for userland testing of dummynet schedulers - */ - -#include "dn_test.h" - -void -m_freem(struct mbuf *m) -{ - printf("free %p\n", m); -} - -int -dn_sched_modevent(module_t mod, int cmd, void *arg) -{ - return 0; -} - -void -dn_free_pkts(struct mbuf *m) -{ - struct mbuf *x; - while ( (x = m) ) { - m = m->m_nextpkt; - m_freem(x); - } -} - -int -dn_delete_queue(void *_q, void *do_free) -{ - struct dn_queue *q = _q; - if (q->mq.head) - dn_free_pkts(q->mq.head); - free(q); - return 0; -} - -/* - * This is a simplified function for testing purposes, which does - * not implement statistics or random loss. - * Enqueue a packet in q, subject to space and queue management policy - * (whose parameters are in q->fs). - * Update stats for the queue and the scheduler. - * Return 0 on success, 1 on drop. The packet is consumed anyways. - */ -int -dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) -{ - if (drop) - goto drop; - if (q->ni.length >= 200) - goto drop; - mq_append(&q->mq, m); - q->ni.length++; - q->ni.tot_bytes += m->m_pkthdr.len; - return 0; - -drop: - q->ni.drops++; - return 1; -} - -int -ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg) -{ - if (*v < lo) { - *v = dflt; - } else if (*v > hi) { - *v = hi; - } - return *v; -} - -#ifndef __FreeBSD__ -int -fls(int mask) -{ - int bit; - - if (mask == 0) - return (0); - for (bit = 1; mask != 1; bit++) - mask = (unsigned int)mask >> 1; - return (bit); -} -#endif diff --git a/sys/netpfil/ipfw/dn_heap.c b/sys/netpfil/ipfw/dn_heap.c new file mode 100644 index 0000000000000..1d585116d0752 --- /dev/null +++ b/sys/netpfil/ipfw/dn_heap.c @@ -0,0 +1,552 @@ +/*- + * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa + * 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. + */ + +/* + * Binary heap and hash tables, used in dummynet + * + * $FreeBSD$ + */ + +#include +#include +#ifdef _KERNEL +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#ifndef log +#define log(x, arg...) +#endif + +#else /* !_KERNEL */ + +#include +#include +#include +#include + +#include "dn_heap.h" +#define log(x, arg...) fprintf(stderr, ## arg) +#define panic(x...) fprintf(stderr, ## x), exit(1) +#define MALLOC_DEFINE(a, b, c) +static void *my_malloc(int s) { return malloc(s); } +static void my_free(void *p) { free(p); } +#define malloc(s, t, w) my_malloc(s) +#define free(p, t) my_free(p) +#endif /* !_KERNEL */ + +static MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap"); + +/* + * Heap management functions. + * + * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2. + * Some macros help finding parent/children so we can optimize them. + * + * heap_init() is called to expand the heap when needed. + * Increment size in blocks of 16 entries. + * Returns 1 on error, 0 on success + */ +#define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 ) +#define HEAP_LEFT(x) ( (x)+(x) + 1 ) +#define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; } +#define HEAP_INCREMENT 15 + +static int +heap_resize(struct dn_heap *h, unsigned int new_size) +{ + struct dn_heap_entry *p; + + if (h->size >= new_size ) /* have enough room */ + return 0; +#if 1 /* round to the next power of 2 */ + new_size |= new_size >> 1; + new_size |= new_size >> 2; + new_size |= new_size >> 4; + new_size |= new_size >> 8; + new_size |= new_size >> 16; +#else + new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT; +#endif + p = malloc(new_size * sizeof(*p), M_DN_HEAP, M_NOWAIT); + if (p == NULL) { + printf("--- %s, resize %d failed\n", __func__, new_size ); + return 1; /* error */ + } + if (h->size > 0) { + bcopy(h->p, p, h->size * sizeof(*p) ); + free(h->p, M_DN_HEAP); + } + h->p = p; + h->size = new_size; + return 0; +} + +int +heap_init(struct dn_heap *h, int size, int ofs) +{ + if (heap_resize(h, size)) + return 1; + h->elements = 0; + h->ofs = ofs; + return 0; +} + +/* + * Insert element in heap. Normally, p != NULL, we insert p in + * a new position and bubble up. If p == NULL, then the element is + * already in place, and key is the position where to start the + * bubble-up. + * Returns 1 on failure (cannot allocate new heap entry) + * + * If ofs > 0 the position (index, int) of the element in the heap is + * also stored in the element itself at the given offset in bytes. + */ +#define SET_OFFSET(h, i) do { \ + if (h->ofs > 0) \ + *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i; \ + } while (0) +/* + * RESET_OFFSET is used for sanity checks. It sets ofs + * to an invalid value. + */ +#define RESET_OFFSET(h, i) do { \ + if (h->ofs > 0) \ + *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16; \ + } while (0) + +int +heap_insert(struct dn_heap *h, uint64_t key1, void *p) +{ + int son = h->elements; + + //log("%s key %llu p %p\n", __FUNCTION__, key1, p); + if (p == NULL) { /* data already there, set starting point */ + son = key1; + } else { /* insert new element at the end, possibly resize */ + son = h->elements; + if (son == h->size) /* need resize... */ + // XXX expand by 16 or so + if (heap_resize(h, h->elements+16) ) + return 1; /* failure... */ + h->p[son].object = p; + h->p[son].key = key1; + h->elements++; + } + /* make sure that son >= father along the path */ + while (son > 0) { + int father = HEAP_FATHER(son); + struct dn_heap_entry tmp; + + if (DN_KEY_LT( h->p[father].key, h->p[son].key ) ) + break; /* found right position */ + /* son smaller than father, swap and repeat */ + HEAP_SWAP(h->p[son], h->p[father], tmp); + SET_OFFSET(h, son); + son = father; + } + SET_OFFSET(h, son); + return 0; +} + +/* + * remove top element from heap, or obj if obj != NULL + */ +void +heap_extract(struct dn_heap *h, void *obj) +{ + int child, father, max = h->elements - 1; + + if (max < 0) { + printf("--- %s: empty heap 0x%p\n", __FUNCTION__, h); + return; + } + if (obj == NULL) + father = 0; /* default: move up smallest child */ + else { /* extract specific element, index is at offset */ + if (h->ofs <= 0) + panic("%s: extract from middle not set on %p\n", + __FUNCTION__, h); + father = *((int *)((char *)obj + h->ofs)); + if (father < 0 || father >= h->elements) { + panic("%s: father %d out of bound 0..%d\n", + __FUNCTION__, father, h->elements); + } + } + /* + * below, father is the index of the empty element, which + * we replace at each step with the smallest child until we + * reach the bottom level. + */ + // XXX why removing RESET_OFFSET increases runtime by 10% ? + RESET_OFFSET(h, father); + while ( (child = HEAP_LEFT(father)) <= max ) { + if (child != max && + DN_KEY_LT(h->p[child+1].key, h->p[child].key) ) + child++; /* take right child, otherwise left */ + h->p[father] = h->p[child]; + SET_OFFSET(h, father); + father = child; + } + h->elements--; + if (father != max) { + /* + * Fill hole with last entry and bubble up, + * reusing the insert code + */ + h->p[father] = h->p[max]; + heap_insert(h, father, NULL); + } +} + +#if 0 +/* + * change object position and update references + * XXX this one is never used! + */ +static void +heap_move(struct dn_heap *h, uint64_t new_key, void *object) +{ + int temp, i, max = h->elements-1; + struct dn_heap_entry *p, buf; + + if (h->ofs <= 0) + panic("cannot move items on this heap"); + p = h->p; /* shortcut */ + + i = *((int *)((char *)object + h->ofs)); + if (DN_KEY_LT(new_key, p[i].key) ) { /* must move up */ + p[i].key = new_key; + for (; i>0 && + DN_KEY_LT(new_key, p[(temp = HEAP_FATHER(i))].key); + i = temp ) { /* bubble up */ + HEAP_SWAP(p[i], p[temp], buf); + SET_OFFSET(h, i); + } + } else { /* must move down */ + p[i].key = new_key; + while ( (temp = HEAP_LEFT(i)) <= max ) { + /* found left child */ + if (temp != max && + DN_KEY_LT(p[temp+1].key, p[temp].key)) + temp++; /* select child with min key */ + if (DN_KEY_LT(>p[temp].key, new_key)) { + /* go down */ + HEAP_SWAP(p[i], p[temp], buf); + SET_OFFSET(h, i); + } else + break; + i = temp; + } + } + SET_OFFSET(h, i); +} +#endif /* heap_move, unused */ + +/* + * heapify() will reorganize data inside an array to maintain the + * heap property. It is needed when we delete a bunch of entries. + */ +static void +heapify(struct dn_heap *h) +{ + int i; + + for (i = 0; i < h->elements; i++ ) + heap_insert(h, i , NULL); +} + +int +heap_scan(struct dn_heap *h, int (*fn)(void *, uintptr_t), + uintptr_t arg) +{ + int i, ret, found; + + for (i = found = 0 ; i < h->elements ;) { + ret = fn(h->p[i].object, arg); + if (ret & HEAP_SCAN_DEL) { + h->elements-- ; + h->p[i] = h->p[h->elements] ; + found++ ; + } else + i++ ; + if (ret & HEAP_SCAN_END) + break; + } + if (found) + heapify(h); + return found; +} + +/* + * cleanup the heap and free data structure + */ +void +heap_free(struct dn_heap *h) +{ + if (h->size >0 ) + free(h->p, M_DN_HEAP); + bzero(h, sizeof(*h) ); +} + +/* + * hash table support. + */ + +struct dn_ht { + int buckets; /* how many buckets, really buckets - 1*/ + int entries; /* how many entries */ + int ofs; /* offset of link field */ + uint32_t (*hash)(uintptr_t, int, void *arg); + int (*match)(void *_el, uintptr_t key, int, void *); + void *(*newh)(uintptr_t, int, void *); + void **ht; /* bucket heads */ +}; +/* + * Initialize, allocating bucket pointers inline. + * Recycle previous record if possible. + * If the 'newh' function is not supplied, we assume that the + * key passed to ht_find is the same object to be stored in. + */ +struct dn_ht * +dn_ht_init(struct dn_ht *ht, int buckets, int ofs, + uint32_t (*h)(uintptr_t, int, void *), + int (*match)(void *, uintptr_t, int, void *), + void *(*newh)(uintptr_t, int, void *)) +{ + int l; + + /* + * Notes about rounding bucket size to a power of two. + * Given the original bucket size, we compute the nearest lower and + * higher power of two, minus 1 (respectively b_min and b_max) because + * this value will be used to do an AND with the index returned + * by hash function. + * To choice between these two values, the original bucket size is + * compared with b_min. If the original size is greater than 4/3 b_min, + * we round the bucket size to b_max, else to b_min. + * This ratio try to round to the nearest power of two, advantaging + * the greater size if the different between two power is relatively + * big. + * Rounding the bucket size to a power of two avoid the use of + * module when calculating the correct bucket. + * The ht->buckets variable store the bucket size - 1 to simply + * do an AND between the index returned by hash function and ht->bucket + * instead of a module. + */ + int b_min; /* min buckets */ + int b_max; /* max buckets */ + int b_ori; /* original buckets */ + + if (h == NULL || match == NULL) { + printf("--- missing hash or match function"); + return NULL; + } + if (buckets < 1 || buckets > 65536) + return NULL; + + b_ori = buckets; + /* calculate next power of 2, - 1*/ + buckets |= buckets >> 1; + buckets |= buckets >> 2; + buckets |= buckets >> 4; + buckets |= buckets >> 8; + buckets |= buckets >> 16; + + b_max = buckets; /* Next power */ + b_min = buckets >> 1; /* Previous power */ + + /* Calculate the 'nearest' bucket size */ + if (b_min * 4000 / 3000 < b_ori) + buckets = b_max; + else + buckets = b_min; + + if (ht) { /* see if we can reuse */ + if (buckets <= ht->buckets) { + ht->buckets = buckets; + } else { + /* free pointers if not allocated inline */ + if (ht->ht != (void *)(ht + 1)) + free(ht->ht, M_DN_HEAP); + free(ht, M_DN_HEAP); + ht = NULL; + } + } + if (ht == NULL) { + /* Allocate buckets + 1 entries because buckets is use to + * do the AND with the index returned by hash function + */ + l = sizeof(*ht) + (buckets + 1) * sizeof(void **); + ht = malloc(l, M_DN_HEAP, M_NOWAIT | M_ZERO); + } + if (ht) { + ht->ht = (void **)(ht + 1); + ht->buckets = buckets; + ht->ofs = ofs; + ht->hash = h; + ht->match = match; + ht->newh = newh; + } + return ht; +} + +/* dummy callback for dn_ht_free to unlink all */ +static int +do_del(void *obj, void *arg) +{ + return DNHT_SCAN_DEL; +} + +void +dn_ht_free(struct dn_ht *ht, int flags) +{ + if (ht == NULL) + return; + if (flags & DNHT_REMOVE) { + (void)dn_ht_scan(ht, do_del, NULL); + } else { + if (ht->ht && ht->ht != (void *)(ht + 1)) + free(ht->ht, M_DN_HEAP); + free(ht, M_DN_HEAP); + } +} + +int +dn_ht_entries(struct dn_ht *ht) +{ + return ht ? ht->entries : 0; +} + +/* lookup and optionally create or delete element */ +void * +dn_ht_find(struct dn_ht *ht, uintptr_t key, int flags, void *arg) +{ + int i; + void **pp, *p; + + if (ht == NULL) /* easy on an empty hash */ + return NULL; + i = (ht->buckets == 1) ? 0 : + (ht->hash(key, flags, arg) & ht->buckets); + + for (pp = &ht->ht[i]; (p = *pp); pp = (void **)((char *)p + ht->ofs)) { + if (flags & DNHT_MATCH_PTR) { + if (key == (uintptr_t)p) + break; + } else if (ht->match(p, key, flags, arg)) /* found match */ + break; + } + if (p) { + if (flags & DNHT_REMOVE) { + /* link in the next element */ + *pp = *(void **)((char *)p + ht->ofs); + *(void **)((char *)p + ht->ofs) = NULL; + ht->entries--; + } + } else if (flags & DNHT_INSERT) { + // printf("%s before calling new, bucket %d ofs %d\n", + // __FUNCTION__, i, ht->ofs); + p = ht->newh ? ht->newh(key, flags, arg) : (void *)key; + // printf("%s newh returns %p\n", __FUNCTION__, p); + if (p) { + ht->entries++; + *(void **)((char *)p + ht->ofs) = ht->ht[i]; + ht->ht[i] = p; + } + } + return p; +} + +/* + * do a scan with the option to delete the object. Extract next before + * running the callback because the element may be destroyed there. + */ +int +dn_ht_scan(struct dn_ht *ht, int (*fn)(void *, void *), void *arg) +{ + int i, ret, found = 0; + void **curp, *cur, *next; + + if (ht == NULL || fn == NULL) + return 0; + for (i = 0; i <= ht->buckets; i++) { + curp = &ht->ht[i]; + while ( (cur = *curp) != NULL) { + next = *(void **)((char *)cur + ht->ofs); + ret = fn(cur, arg); + if (ret & DNHT_SCAN_DEL) { + found++; + ht->entries--; + *curp = next; + } else { + curp = (void **)((char *)cur + ht->ofs); + } + if (ret & DNHT_SCAN_END) + return found; + } + } + return found; +} + +/* + * Similar to dn_ht_scan(), except that the scan is performed only + * in the bucket 'bucket'. The function returns a correct bucket number if + * the original is invalid. + * If the callback returns DNHT_SCAN_END, the function move the ht->ht[i] + * pointer to the last entry processed. Moreover, the bucket number passed + * by caller is decremented, because usually the caller increment it. + */ +int +dn_ht_scan_bucket(struct dn_ht *ht, int *bucket, int (*fn)(void *, void *), + void *arg) +{ + int i, ret, found = 0; + void **curp, *cur, *next; + + if (ht == NULL || fn == NULL) + return 0; + if (*bucket > ht->buckets) + *bucket = 0; + i = *bucket; + + curp = &ht->ht[i]; + while ( (cur = *curp) != NULL) { + next = *(void **)((char *)cur + ht->ofs); + ret = fn(cur, arg); + if (ret & DNHT_SCAN_DEL) { + found++; + ht->entries--; + *curp = next; + } else { + curp = (void **)((char *)cur + ht->ofs); + } + if (ret & DNHT_SCAN_END) + return found; + } + return found; +} diff --git a/sys/netpfil/ipfw/dn_heap.h b/sys/netpfil/ipfw/dn_heap.h new file mode 100644 index 0000000000000..c95473ade3926 --- /dev/null +++ b/sys/netpfil/ipfw/dn_heap.h @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 1998-2010 Luigi Rizzo, Universita` di Pisa + * 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. + */ + +/* + * Binary heap and hash tables, header file + * + * $FreeBSD$ + */ + +#ifndef _IP_DN_HEAP_H +#define _IP_DN_HEAP_H + +#define DN_KEY_LT(a,b) ((int64_t)((a)-(b)) < 0) +#define DN_KEY_LEQ(a,b) ((int64_t)((a)-(b)) <= 0) + +/* + * This module implements a binary heap supporting random extraction. + * + * A heap entry contains an uint64_t key and a pointer to object. + * DN_KEY_LT(a,b) returns true if key 'a' is smaller than 'b' + * + * The heap is a struct dn_heap plus a dynamically allocated + * array of dn_heap_entry entries. 'size' represents the size of + * the array, 'elements' count entries in use. The topmost + * element has the smallest key. + * The heap supports ordered insert, and extract from the top. + * To extract an object from the middle of the heap, we the object + * must reserve an 'int32_t' to store the position of the object + * in the heap itself, and the location of this field must be + * passed as an argument to heap_init() -- use -1 if the feature + * is not used. + */ +struct dn_heap_entry { + uint64_t key; /* sorting key, smallest comes first */ + void *object; /* object pointer */ +}; + +struct dn_heap { + int size; /* the size of the array */ + int elements; /* elements in use */ + int ofs; /* offset in the object of heap index */ + struct dn_heap_entry *p; /* array of "size" entries */ +}; + +enum { + HEAP_SCAN_DEL = 1, + HEAP_SCAN_END = 2, +}; + +/* + * heap_init() reinitializes the heap setting the size and the offset + * of the index for random extraction (use -1 if not used). + * The 'elements' counter is set to 0. + * + * SET_HEAP_OFS() indicates where, in the object, is stored the index + * for random extractions from the heap. + * + * heap_free() frees the memory associated to a heap. + * + * heap_insert() adds a key-pointer pair to the heap + * + * HEAP_TOP() returns a pointer to the top element of the heap, + * but makes no checks on its existance (XXX should we change ?) + * + * heap_extract() removes the entry at the top, returing the pointer. + * (the key should have been read before). + * + * heap_scan() invokes a callback on each entry of the heap. + * The callback can return a combination of HEAP_SCAN_DEL and + * HEAP_SCAN_END. HEAP_SCAN_DEL means the current element must + * be removed, and HEAP_SCAN_END means to terminate the scan. + * heap_scan() returns the number of elements removed. + * Because the order is not guaranteed, we should use heap_scan() + * only as a last resort mechanism. + */ +#define HEAP_TOP(h) ((h)->p) +#define SET_HEAP_OFS(h, n) do { (h)->ofs = n; } while (0) +int heap_init(struct dn_heap *h, int size, int ofs); +int heap_insert(struct dn_heap *h, uint64_t key1, void *p); +void heap_extract(struct dn_heap *h, void *obj); +void heap_free(struct dn_heap *h); +int heap_scan(struct dn_heap *, int (*)(void *, uintptr_t), uintptr_t); + +/*------------------------------------------------------ + * This module implements a generic hash table with support for + * running callbacks on the entire table. To avoid allocating + * memory during hash table operations, objects must reserve + * space for a link field. XXX if the heap is moderately full, + * an SLIST suffices, and we can tolerate the cost of a hash + * computation on each removal. + * + * dn_ht_init() initializes the table, setting the number of + * buckets, the offset of the link field, the main callbacks. + * Callbacks are: + * + * hash(key, flags, arg) called to return a bucket index. + * match(obj, key, flags, arg) called to determine if key + * matches the current 'obj' in the heap + * newh(key, flags, arg) optional, used to allocate a new + * object during insertions. + * + * dn_ht_free() frees the heap or unlink elements. + * DNHT_REMOVE unlink elements, 0 frees the heap. + * You need two calls to do both. + * + * dn_ht_find() is the main lookup function, which can also be + * used to insert or delete elements in the hash table. + * The final 'arg' is passed to all callbacks. + * + * dn_ht_scan() is used to invoke a callback on all entries of + * the heap, or possibly on just one bucket. The callback + * is invoked with a pointer to the object, and must return + * one of DNHT_SCAN_DEL or DNHT_SCAN_END to request the + * removal of the object from the heap and the end of the + * scan, respectively. + * + * dn_ht_scan_bucket() is similar to dn_ht_scan(), except that it scans + * only the specific bucket of the table. The bucket is a in-out + * parameter and return a valid bucket number if the original + * is invalid. + * + * A combination of flags can be used to modify the operation + * of the dn_ht_find(), and of the callbacks: + * + * DNHT_KEY_IS_OBJ means the key is the object pointer. + * It is usally of interest for the hash and match functions. + * + * DNHT_MATCH_PTR during a lookup, match pointers instead + * of calling match(). Normally used when removing specific + * entries. Does not imply KEY_IS_OBJ as the latter _is_ used + * by the match function. + * + * DNHT_INSERT insert the element if not found. + * Calls new() to allocates a new object unless + * DNHT_KEY_IS_OBJ is set. + * + * DNHT_UNIQUE only insert if object not found. + * XXX should it imply DNHT_INSERT ? + * + * DNHT_REMOVE remove objects if we find them. + */ +struct dn_ht; /* should be opaque */ + +struct dn_ht *dn_ht_init(struct dn_ht *, int buckets, int ofs, + uint32_t (*hash)(uintptr_t, int, void *), + int (*match)(void *, uintptr_t, int, void *), + void *(*newh)(uintptr_t, int, void *)); +void dn_ht_free(struct dn_ht *, int flags); + +void *dn_ht_find(struct dn_ht *, uintptr_t, int, void *); +int dn_ht_scan(struct dn_ht *, int (*)(void *, void *), void *); +int dn_ht_scan_bucket(struct dn_ht *, int * , int (*)(void *, void *), void *); +int dn_ht_entries(struct dn_ht *); + +enum { /* flags values. + * first two are returned by the scan callback to indicate + * to delete the matching element or to end the scan + */ + DNHT_SCAN_DEL = 0x0001, + DNHT_SCAN_END = 0x0002, + DNHT_KEY_IS_OBJ = 0x0004, /* key is the obj pointer */ + DNHT_MATCH_PTR = 0x0008, /* match by pointer, not match() */ + DNHT_INSERT = 0x0010, /* insert if not found */ + DNHT_UNIQUE = 0x0020, /* report error if already there */ + DNHT_REMOVE = 0x0040, /* remove on find or dn_ht_free */ +}; + +#endif /* _IP_DN_HEAP_H */ diff --git a/sys/netpfil/ipfw/dn_sched.h b/sys/netpfil/ipfw/dn_sched.h new file mode 100644 index 0000000000000..ab823fe7c806e --- /dev/null +++ b/sys/netpfil/ipfw/dn_sched.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2010 Riccardo Panicucci, Luigi Rizzo, Universita` di Pisa + * 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. + */ + +/* + * The API to write a packet scheduling algorithm for dummynet. + * + * $FreeBSD$ + */ + +#ifndef _DN_SCHED_H +#define _DN_SCHED_H + +#define DN_MULTIQUEUE 0x01 +/* + * Descriptor for a scheduling algorithm. + * Contains all function pointers for a given scheduler + * This is typically created when a module is loaded, and stored + * in a global list of schedulers. + */ +struct dn_alg { + uint32_t type; /* the scheduler type */ + const char *name; /* scheduler name */ + uint32_t flags; /* DN_MULTIQUEUE if supports multiple queues */ + + /* + * The following define the size of 3 optional data structures + * that may need to be allocated at runtime, and are appended + * to each of the base data structures: scheduler, sched.inst, + * and queue. We don't have a per-flowset structure. + */ + /* + parameters attached to the template, e.g. + * default queue sizes, weights, quantum size, and so on; + */ + size_t schk_datalen; + + /* + per-instance parameters, such as timestamps, + * containers for queues, etc; + */ + size_t si_datalen; + + size_t q_datalen; /* per-queue parameters (e.g. S,F) */ + + /* + * Methods implemented by the scheduler: + * enqueue enqueue packet 'm' on scheduler 's', queue 'q'. + * q is NULL for !MULTIQUEUE. + * Return 0 on success, 1 on drop (packet consumed anyways). + * Note that q should be interpreted only as a hint + * on the flow that the mbuf belongs to: while a + * scheduler will normally enqueue m into q, it is ok + * to leave q alone and put the mbuf elsewhere. + * This function is called in two cases: + * - when a new packet arrives to the scheduler; + * - when a scheduler is reconfigured. In this case the + * call is issued by the new_queue callback, with a + * non empty queue (q) and m pointing to the first + * mbuf in the queue. For this reason, the function + * should internally check for (m != q->mq.head) + * before calling dn_enqueue(). + * + * dequeue Called when scheduler instance 's' can + * dequeue a packet. Return NULL if none are available. + * XXX what about non work-conserving ? + * + * config called on 'sched X config ...', normally writes + * in the area of size sch_arg + * + * destroy called on 'sched delete', frees everything + * in sch_arg (other parts are handled by more specific + * functions) + * + * new_sched called when a new instance is created, e.g. + * to create the local queue for !MULTIQUEUE, set V or + * copy parameters for WFQ, and so on. + * + * free_sched called when deleting an instance, cleans + * extra data in the per-instance area. + * + * new_fsk called when a flowset is linked to a scheduler, + * e.g. to validate parameters such as weights etc. + * free_fsk when a flowset is unlinked from a scheduler. + * (probably unnecessary) + * + * new_queue called to set the per-queue parameters, + * e.g. S and F, adjust sum of weights in the parent, etc. + * + * The new_queue callback is normally called from when + * creating a new queue. In some cases (such as a + * scheduler change or reconfiguration) it can be called + * with a non empty queue. In this case, the queue + * In case of non empty queue, the new_queue callback could + * need to call the enqueue function. In this case, + * the callback should eventually call enqueue() passing + * as m the first element in the queue. + * + * free_queue actions related to a queue removal, e.g. undo + * all the above. If the queue has data in it, also remove + * from the scheduler. This can e.g. happen during a reconfigure. + */ + int (*enqueue)(struct dn_sch_inst *, struct dn_queue *, + struct mbuf *); + struct mbuf * (*dequeue)(struct dn_sch_inst *); + + int (*config)(struct dn_schk *); + int (*destroy)(struct dn_schk*); + int (*new_sched)(struct dn_sch_inst *); + int (*free_sched)(struct dn_sch_inst *); + int (*new_fsk)(struct dn_fsk *f); + int (*free_fsk)(struct dn_fsk *f); + int (*new_queue)(struct dn_queue *q); + int (*free_queue)(struct dn_queue *q); + + /* run-time fields */ + int ref_count; /* XXX number of instances in the system */ + SLIST_ENTRY(dn_alg) next; /* Next scheduler in the list */ +}; + +/* MSVC does not support initializers so we need this ugly macro */ +#ifdef _WIN32 +#define _SI(fld) +#else +#define _SI(fld) fld +#endif + +/* + * Additionally, dummynet exports some functions and macros + * to be used by schedulers: + */ + +void dn_free_pkts(struct mbuf *mnext); +int dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop); +/* bound a variable between min and max */ +int ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg); + +/* + * Extract the head of a queue, update stats. Must be the very last + * thing done on a dequeue as the queue itself may go away. + */ +static __inline struct mbuf* +dn_dequeue(struct dn_queue *q) +{ + struct mbuf *m = q->mq.head; + if (m == NULL) + return NULL; + q->mq.head = m->m_nextpkt; + + /* Update stats for the queue */ + q->ni.length--; + q->ni.len_bytes -= m->m_pkthdr.len; + if (q->_si) { + q->_si->ni.length--; + q->_si->ni.len_bytes -= m->m_pkthdr.len; + } + if (q->ni.length == 0) /* queue is now idle */ + q->q_time = dn_cfg.curr_time; + return m; +} + +int dn_sched_modevent(module_t mod, int cmd, void *arg); + +#define DECLARE_DNSCHED_MODULE(name, dnsched) \ + static moduledata_t name##_mod = { \ + #name, dn_sched_modevent, dnsched \ + }; \ + DECLARE_MODULE(name, name##_mod, \ + SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); \ + MODULE_DEPEND(name, dummynet, 3, 3, 3); +#endif /* _DN_SCHED_H */ diff --git a/sys/netpfil/ipfw/dn_sched_fifo.c b/sys/netpfil/ipfw/dn_sched_fifo.c new file mode 100644 index 0000000000000..e2aa60826eb8a --- /dev/null +++ b/sys/netpfil/ipfw/dn_sched_fifo.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa + * 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. + */ + +/* + * $FreeBSD$ + */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ */ +#include +#include /* ipfw_rule_ref */ +#include /* flow_id */ +#include +#include +#include +#include +#else +#include +#endif + +/* + * This file implements a FIFO scheduler for a single queue. + * The queue is allocated as part of the scheduler instance, + * and there is a single flowset is in the template which stores + * queue size and policy. + * Enqueue and dequeue use the default library functions. + */ +static int +fifo_enqueue(struct dn_sch_inst *si, struct dn_queue *q, struct mbuf *m) +{ + /* XXX if called with q != NULL and m=NULL, this is a + * re-enqueue from an existing scheduler, which we should + * handle. + */ + return dn_enqueue((struct dn_queue *)(si+1), m, 0); +} + +static struct mbuf * +fifo_dequeue(struct dn_sch_inst *si) +{ + return dn_dequeue((struct dn_queue *)(si + 1)); +} + +static int +fifo_new_sched(struct dn_sch_inst *si) +{ + /* This scheduler instance contains the queue */ + struct dn_queue *q = (struct dn_queue *)(si + 1); + + set_oid(&q->ni.oid, DN_QUEUE, sizeof(*q)); + q->_si = si; + q->fs = si->sched->fs; + return 0; +} + +static int +fifo_free_sched(struct dn_sch_inst *si) +{ + struct dn_queue *q = (struct dn_queue *)(si + 1); + dn_free_pkts(q->mq.head); + bzero(q, sizeof(*q)); + return 0; +} + +/* + * FIFO scheduler descriptor + * contains the type of the scheduler, the name, the size of extra + * data structures, and function pointers. + */ +static struct dn_alg fifo_desc = { + _SI( .type = ) DN_SCHED_FIFO, + _SI( .name = ) "FIFO", + _SI( .flags = ) 0, + + _SI( .schk_datalen = ) 0, + _SI( .si_datalen = ) sizeof(struct dn_queue), + _SI( .q_datalen = ) 0, + + _SI( .enqueue = ) fifo_enqueue, + _SI( .dequeue = ) fifo_dequeue, + _SI( .config = ) NULL, + _SI( .destroy = ) NULL, + _SI( .new_sched = ) fifo_new_sched, + _SI( .free_sched = ) fifo_free_sched, + _SI( .new_fsk = ) NULL, + _SI( .free_fsk = ) NULL, + _SI( .new_queue = ) NULL, + _SI( .free_queue = ) NULL, +}; + +DECLARE_DNSCHED_MODULE(dn_fifo, &fifo_desc); diff --git a/sys/netpfil/ipfw/dn_sched_prio.c b/sys/netpfil/ipfw/dn_sched_prio.c new file mode 100644 index 0000000000000..b77951528c1a7 --- /dev/null +++ b/sys/netpfil/ipfw/dn_sched_prio.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa + * 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. + */ + +/* + * $FreeBSD$ + */ +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ */ +#include +#include /* ipfw_rule_ref */ +#include /* flow_id */ +#include +#include +#include +#include +#else +#include +#endif + +#define DN_SCHED_PRIO 5 //XXX + +#if !defined(_KERNEL) || !defined(__linux__) +#define test_bit(ix, pData) ((*pData) & (1<<(ix))) +#define __set_bit(ix, pData) (*pData) |= (1<<(ix)) +#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix)) +#endif + +#ifdef __MIPSEL__ +#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix)) +#endif + +/* Size of the array of queues pointers. */ +#define BITMAP_T unsigned long +#define MAXPRIO (sizeof(BITMAP_T) * 8) + +/* + * The scheduler instance contains an array of pointers to queues, + * one for each priority, and a bitmap listing backlogged queues. + */ +struct prio_si { + BITMAP_T bitmap; /* array bitmap */ + struct dn_queue *q_array[MAXPRIO]; /* Array of queues pointers */ +}; + +/* + * If a queue with the same priority is already backlogged, use + * that one instead of the queue passed as argument. + */ +static int +prio_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m) +{ + struct prio_si *si = (struct prio_si *)(_si + 1); + int prio = q->fs->fs.par[0]; + + if (test_bit(prio, &si->bitmap) == 0) { + /* No queue with this priority, insert */ + __set_bit(prio, &si->bitmap); + si->q_array[prio] = q; + } else { /* use the existing queue */ + q = si->q_array[prio]; + } + if (dn_enqueue(q, m, 0)) + return 1; + return 0; +} + +/* + * Packets are dequeued only from the highest priority queue. + * The function ffs() return the lowest bit in the bitmap that rapresent + * the array index (-1) which contains the pointer to the highest priority + * queue. + * After the dequeue, if this queue become empty, it is index is removed + * from the bitmap. + * Scheduler is idle if the bitmap is empty + * + * NOTE: highest priority is 0, lowest is sched->max_prio_q + */ +static struct mbuf * +prio_dequeue(struct dn_sch_inst *_si) +{ + struct prio_si *si = (struct prio_si *)(_si + 1); + struct mbuf *m; + struct dn_queue *q; + int prio; + + if (si->bitmap == 0) /* scheduler idle */ + return NULL; + + prio = ffs(si->bitmap) - 1; + + /* Take the highest priority queue in the scheduler */ + q = si->q_array[prio]; + // assert(q) + + m = dn_dequeue(q); + if (q->mq.head == NULL) { + /* Queue is now empty, remove from scheduler + * and mark it + */ + si->q_array[prio] = NULL; + __clear_bit(prio, &si->bitmap); + } + return m; +} + +static int +prio_new_sched(struct dn_sch_inst *_si) +{ + struct prio_si *si = (struct prio_si *)(_si + 1); + + bzero(si->q_array, sizeof(si->q_array)); + si->bitmap = 0; + + return 0; +} + +static int +prio_new_fsk(struct dn_fsk *fs) +{ + /* Check if the prioritiy is between 0 and MAXPRIO-1 */ + ipdn_bound_var(&fs->fs.par[0], 0, 0, MAXPRIO - 1, "PRIO priority"); + return 0; +} + +static int +prio_new_queue(struct dn_queue *q) +{ + struct prio_si *si = (struct prio_si *)(q->_si + 1); + int prio = q->fs->fs.par[0]; + struct dn_queue *oldq; + + q->ni.oid.subtype = DN_SCHED_PRIO; + + if (q->mq.head == NULL) + return 0; + + /* Queue already full, must insert in the scheduler or append + * mbufs to existing queue. This partly duplicates prio_enqueue + */ + if (test_bit(prio, &si->bitmap) == 0) { + /* No queue with this priority, insert */ + __set_bit(prio, &si->bitmap); + si->q_array[prio] = q; + } else if ( (oldq = si->q_array[prio]) != q) { + /* must append to the existing queue. + * can simply append q->mq.head to q2->... + * and add the counters to those of q2 + */ + oldq->mq.tail->m_nextpkt = q->mq.head; + oldq->mq.tail = q->mq.tail; + oldq->ni.length += q->ni.length; + q->ni.length = 0; + oldq->ni.len_bytes += q->ni.len_bytes; + q->ni.len_bytes = 0; + q->mq.tail = q->mq.head = NULL; + } + return 0; +} + +static int +prio_free_queue(struct dn_queue *q) +{ + int prio = q->fs->fs.par[0]; + struct prio_si *si = (struct prio_si *)(q->_si + 1); + + if (si->q_array[prio] == q) { + si->q_array[prio] = NULL; + __clear_bit(prio, &si->bitmap); + } + return 0; +} + + +static struct dn_alg prio_desc = { + _SI( .type = ) DN_SCHED_PRIO, + _SI( .name = ) "PRIO", + _SI( .flags = ) DN_MULTIQUEUE, + + /* we need extra space in the si and the queue */ + _SI( .schk_datalen = ) 0, + _SI( .si_datalen = ) sizeof(struct prio_si), + _SI( .q_datalen = ) 0, + + _SI( .enqueue = ) prio_enqueue, + _SI( .dequeue = ) prio_dequeue, + + _SI( .config = ) NULL, + _SI( .destroy = ) NULL, + _SI( .new_sched = ) prio_new_sched, + _SI( .free_sched = ) NULL, + + _SI( .new_fsk = ) prio_new_fsk, + _SI( .free_fsk = ) NULL, + + _SI( .new_queue = ) prio_new_queue, + _SI( .free_queue = ) prio_free_queue, +}; + + +DECLARE_DNSCHED_MODULE(dn_prio, &prio_desc); diff --git a/sys/netpfil/ipfw/dn_sched_qfq.c b/sys/netpfil/ipfw/dn_sched_qfq.c new file mode 100644 index 0000000000000..5bbff8a91344b --- /dev/null +++ b/sys/netpfil/ipfw/dn_sched_qfq.c @@ -0,0 +1,864 @@ +/* + * Copyright (c) 2010 Fabio Checconi, Luigi Rizzo, Paolo Valente + * 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. + */ + +/* + * $FreeBSD$ + */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ */ +#include +#include /* ipfw_rule_ref */ +#include /* flow_id */ +#include +#include +#include +#include +#else +#include +#endif + +#ifdef QFQ_DEBUG +struct qfq_sched; +static void dump_sched(struct qfq_sched *q, const char *msg); +#define NO(x) x +#else +#define NO(x) +#endif +#define DN_SCHED_QFQ 4 // XXX Where? +typedef unsigned long bitmap; + +/* + * bitmaps ops are critical. Some linux versions have __fls + * and the bitmap ops. Some machines have ffs + */ +#if defined(_WIN32) || (defined(__MIPSEL__) && defined(LINUX_24)) +int fls(unsigned int n) +{ + int i = 0; + for (i = 0; n > 0; n >>= 1, i++) + ; + return i; +} +#endif + +#if !defined(_KERNEL) || defined( __FreeBSD__ ) || defined(_WIN32) || (defined(__MIPSEL__) && defined(LINUX_24)) +static inline unsigned long __fls(unsigned long word) +{ + return fls(word) - 1; +} +#endif + +#if !defined(_KERNEL) || !defined(__linux__) +#ifdef QFQ_DEBUG +int test_bit(int ix, bitmap *p) +{ + if (ix < 0 || ix > 31) + D("bad index %d", ix); + return *p & (1< 31) + D("bad index %d", ix); + *p |= (1< 31) + D("bad index %d", ix); + *p &= ~(1<index = 0 + *.__grp->slot_shift + + where MIN_SLOT_SHIFT is derived by difference from the others. + +The max group index corresponds to Lmax/w_min, where +Lmax=1<group mapping. Class weights are + * in the range [1, QFQ_MAX_WEIGHT], we to map each class i to the + * group with the smallest index that can support the L_i / r_i + * configured for the class. + * + * grp->index is the index of the group; and grp->slot_shift + * is the shift for the corresponding (scaled) sigma_i. + * + * When computing the group index, we do (len<i_wsum) +#define IWSUM ((1< 0; +} + +/* Round a precise timestamp to its slotted value. */ +static inline uint64_t qfq_round_down(uint64_t ts, unsigned int shift) +{ + return ts & ~((1ULL << shift) - 1); +} + +/* return the pointer to the group with lowest index in the bitmap */ +static inline struct qfq_group *qfq_ffs(struct qfq_sched *q, + unsigned long bitmap) +{ + int index = ffs(bitmap) - 1; // zero-based + return &q->groups[index]; +} + +/* + * Calculate a flow index, given its weight and maximum packet length. + * index = log_2(maxlen/weight) but we need to apply the scaling. + * This is used only once at flow creation. + */ +static int qfq_calc_index(uint32_t inv_w, unsigned int maxlen) +{ + uint64_t slot_size = (uint64_t)maxlen *inv_w; + unsigned long size_map; + int index = 0; + + size_map = (unsigned long)(slot_size >> QFQ_MIN_SLOT_SHIFT); + if (!size_map) + goto out; + + index = __fls(size_map) + 1; // basically a log_2() + index -= !(slot_size - (1ULL << (index + QFQ_MIN_SLOT_SHIFT - 1))); + + if (index < 0) + index = 0; + +out: + ND("W = %d, L = %d, I = %d\n", ONE_FP/inv_w, maxlen, index); + return index; +} +/*---- end support functions ----*/ + +/*-------- API calls --------------------------------*/ +/* + * Validate and copy parameters from flowset. + */ +static int +qfq_new_queue(struct dn_queue *_q) +{ + struct qfq_sched *q = (struct qfq_sched *)(_q->_si + 1); + struct qfq_class *cl = (struct qfq_class *)_q; + int i; + uint32_t w; /* approximated weight */ + + /* import parameters from the flowset. They should be correct + * already. + */ + w = _q->fs->fs.par[0]; + cl->lmax = _q->fs->fs.par[1]; + if (!w || w > QFQ_MAX_WEIGHT) { + w = 1; + D("rounding weight to 1"); + } + cl->inv_w = ONE_FP/w; + w = ONE_FP/cl->inv_w; + if (q->wsum + w > QFQ_MAX_WSUM) + return EINVAL; + + i = qfq_calc_index(cl->inv_w, cl->lmax); + cl->grp = &q->groups[i]; + q->wsum += w; + // XXX cl->S = q->V; ? + // XXX compute q->i_wsum + return 0; +} + +/* remove an empty queue */ +static int +qfq_free_queue(struct dn_queue *_q) +{ + struct qfq_sched *q = (struct qfq_sched *)(_q->_si + 1); + struct qfq_class *cl = (struct qfq_class *)_q; + if (cl->inv_w) { + q->wsum -= ONE_FP/cl->inv_w; + cl->inv_w = 0; /* reset weight to avoid run twice */ + } + return 0; +} + +/* Calculate a mask to mimic what would be ffs_from(). */ +static inline unsigned long +mask_from(unsigned long bitmap, int from) +{ + return bitmap & ~((1UL << from) - 1); +} + +/* + * The state computation relies on ER=0, IR=1, EB=2, IB=3 + * First compute eligibility comparing grp->S, q->V, + * then check if someone is blocking us and possibly add EB + */ +static inline unsigned int +qfq_calc_state(struct qfq_sched *q, struct qfq_group *grp) +{ + /* if S > V we are not eligible */ + unsigned int state = qfq_gt(grp->S, q->V); + unsigned long mask = mask_from(q->bitmaps[ER], grp->index); + struct qfq_group *next; + + if (mask) { + next = qfq_ffs(q, mask); + if (qfq_gt(grp->F, next->F)) + state |= EB; + } + + return state; +} + +/* + * In principle + * q->bitmaps[dst] |= q->bitmaps[src] & mask; + * q->bitmaps[src] &= ~mask; + * but we should make sure that src != dst + */ +static inline void +qfq_move_groups(struct qfq_sched *q, unsigned long mask, int src, int dst) +{ + q->bitmaps[dst] |= q->bitmaps[src] & mask; + q->bitmaps[src] &= ~mask; +} + +static inline void +qfq_unblock_groups(struct qfq_sched *q, int index, uint64_t old_finish) +{ + unsigned long mask = mask_from(q->bitmaps[ER], index + 1); + struct qfq_group *next; + + if (mask) { + next = qfq_ffs(q, mask); + if (!qfq_gt(next->F, old_finish)) + return; + } + + mask = (1UL << index) - 1; + qfq_move_groups(q, mask, EB, ER); + qfq_move_groups(q, mask, IB, IR); +} + +/* + * perhaps + * + old_V ^= q->V; + old_V >>= QFQ_MIN_SLOT_SHIFT; + if (old_V) { + ... + } + * + */ +static inline void +qfq_make_eligible(struct qfq_sched *q, uint64_t old_V) +{ + unsigned long mask, vslot, old_vslot; + + vslot = q->V >> QFQ_MIN_SLOT_SHIFT; + old_vslot = old_V >> QFQ_MIN_SLOT_SHIFT; + + if (vslot != old_vslot) { + mask = (2UL << (__fls(vslot ^ old_vslot))) - 1; + qfq_move_groups(q, mask, IR, ER); + qfq_move_groups(q, mask, IB, EB); + } +} + +/* + * XXX we should make sure that slot becomes less than 32. + * This is guaranteed by the input values. + * roundedS is always cl->S rounded on grp->slot_shift bits. + */ +static inline void +qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl, uint64_t roundedS) +{ + uint64_t slot = (roundedS - grp->S) >> grp->slot_shift; + unsigned int i = (grp->front + slot) % QFQ_MAX_SLOTS; + + cl->next = grp->slots[i]; + grp->slots[i] = cl; + __set_bit(slot, &grp->full_slots); +} + +/* + * remove the entry from the slot + */ +static inline void +qfq_front_slot_remove(struct qfq_group *grp) +{ + struct qfq_class **h = &grp->slots[grp->front]; + + *h = (*h)->next; + if (!*h) + __clear_bit(0, &grp->full_slots); +} + +/* + * Returns the first full queue in a group. As a side effect, + * adjust the bucket list so the first non-empty bucket is at + * position 0 in full_slots. + */ +static inline struct qfq_class * +qfq_slot_scan(struct qfq_group *grp) +{ + int i; + + ND("grp %d full %x", grp->index, grp->full_slots); + if (!grp->full_slots) + return NULL; + + i = ffs(grp->full_slots) - 1; // zero-based + if (i > 0) { + grp->front = (grp->front + i) % QFQ_MAX_SLOTS; + grp->full_slots >>= i; + } + + return grp->slots[grp->front]; +} + +/* + * adjust the bucket list. When the start time of a group decreases, + * we move the index down (modulo QFQ_MAX_SLOTS) so we don't need to + * move the objects. The mask of occupied slots must be shifted + * because we use ffs() to find the first non-empty slot. + * This covers decreases in the group's start time, but what about + * increases of the start time ? + * Here too we should make sure that i is less than 32 + */ +static inline void +qfq_slot_rotate(struct qfq_sched *q, struct qfq_group *grp, uint64_t roundedS) +{ + unsigned int i = (grp->S - roundedS) >> grp->slot_shift; + + grp->full_slots <<= i; + grp->front = (grp->front - i) % QFQ_MAX_SLOTS; +} + + +static inline void +qfq_update_eligible(struct qfq_sched *q, uint64_t old_V) +{ + bitmap ineligible; + + ineligible = q->bitmaps[IR] | q->bitmaps[IB]; + if (ineligible) { + if (!q->bitmaps[ER]) { + struct qfq_group *grp; + grp = qfq_ffs(q, ineligible); + if (qfq_gt(grp->S, q->V)) + q->V = grp->S; + } + qfq_make_eligible(q, old_V); + } +} + +/* + * Updates the class, returns true if also the group needs to be updated. + */ +static inline int +qfq_update_class(struct qfq_sched *q, struct qfq_group *grp, + struct qfq_class *cl) +{ + + cl->S = cl->F; + if (cl->_q.mq.head == NULL) { + qfq_front_slot_remove(grp); + } else { + unsigned int len; + uint64_t roundedS; + + len = cl->_q.mq.head->m_pkthdr.len; + cl->F = cl->S + (uint64_t)len * cl->inv_w; + roundedS = qfq_round_down(cl->S, grp->slot_shift); + if (roundedS == grp->S) + return 0; + + qfq_front_slot_remove(grp); + qfq_slot_insert(grp, cl, roundedS); + } + return 1; +} + +static struct mbuf * +qfq_dequeue(struct dn_sch_inst *si) +{ + struct qfq_sched *q = (struct qfq_sched *)(si + 1); + struct qfq_group *grp; + struct qfq_class *cl; + struct mbuf *m; + uint64_t old_V; + + NO(q->loops++;) + if (!q->bitmaps[ER]) { + NO(if (q->queued) + dump_sched(q, "start dequeue");) + return NULL; + } + + grp = qfq_ffs(q, q->bitmaps[ER]); + + cl = grp->slots[grp->front]; + /* extract from the first bucket in the bucket list */ + m = dn_dequeue(&cl->_q); + + if (!m) { + D("BUG/* non-workconserving leaf */"); + return NULL; + } + NO(q->queued--;) + old_V = q->V; + q->V += (uint64_t)m->m_pkthdr.len * IWSUM; + ND("m is %p F 0x%llx V now 0x%llx", m, cl->F, q->V); + + if (qfq_update_class(q, grp, cl)) { + uint64_t old_F = grp->F; + cl = qfq_slot_scan(grp); + if (!cl) { /* group gone, remove from ER */ + __clear_bit(grp->index, &q->bitmaps[ER]); + // grp->S = grp->F + 1; // XXX debugging only + } else { + uint64_t roundedS = qfq_round_down(cl->S, grp->slot_shift); + unsigned int s; + + if (grp->S == roundedS) + goto skip_unblock; + grp->S = roundedS; + grp->F = roundedS + (2ULL << grp->slot_shift); + /* remove from ER and put in the new set */ + __clear_bit(grp->index, &q->bitmaps[ER]); + s = qfq_calc_state(q, grp); + __set_bit(grp->index, &q->bitmaps[s]); + } + /* we need to unblock even if the group has gone away */ + qfq_unblock_groups(q, grp->index, old_F); + } + +skip_unblock: + qfq_update_eligible(q, old_V); + NO(if (!q->bitmaps[ER] && q->queued) + dump_sched(q, "end dequeue");) + + return m; +} + +/* + * Assign a reasonable start time for a new flow k in group i. + * Admissible values for \hat(F) are multiples of \sigma_i + * no greater than V+\sigma_i . Larger values mean that + * we had a wraparound so we consider the timestamp to be stale. + * + * If F is not stale and F >= V then we set S = F. + * Otherwise we should assign S = V, but this may violate + * the ordering in ER. So, if we have groups in ER, set S to + * the F_j of the first group j which would be blocking us. + * We are guaranteed not to move S backward because + * otherwise our group i would still be blocked. + */ +static inline void +qfq_update_start(struct qfq_sched *q, struct qfq_class *cl) +{ + unsigned long mask; + uint64_t limit, roundedF; + int slot_shift = cl->grp->slot_shift; + + roundedF = qfq_round_down(cl->F, slot_shift); + limit = qfq_round_down(q->V, slot_shift) + (1UL << slot_shift); + + if (!qfq_gt(cl->F, q->V) || qfq_gt(roundedF, limit)) { + /* timestamp was stale */ + mask = mask_from(q->bitmaps[ER], cl->grp->index); + if (mask) { + struct qfq_group *next = qfq_ffs(q, mask); + if (qfq_gt(roundedF, next->F)) { + cl->S = next->F; + return; + } + } + cl->S = q->V; + } else { /* timestamp is not stale */ + cl->S = cl->F; + } +} + +static int +qfq_enqueue(struct dn_sch_inst *si, struct dn_queue *_q, struct mbuf *m) +{ + struct qfq_sched *q = (struct qfq_sched *)(si + 1); + struct qfq_group *grp; + struct qfq_class *cl = (struct qfq_class *)_q; + uint64_t roundedS; + int s; + + NO(q->loops++;) + DX(4, "len %d flow %p inv_w 0x%x grp %d", m->m_pkthdr.len, + _q, cl->inv_w, cl->grp->index); + /* XXX verify that the packet obeys the parameters */ + if (m != _q->mq.head) { + if (dn_enqueue(_q, m, 0)) /* packet was dropped */ + return 1; + NO(q->queued++;) + if (m != _q->mq.head) + return 0; + } + /* If reach this point, queue q was idle */ + grp = cl->grp; + qfq_update_start(q, cl); /* adjust start time */ + /* compute new finish time and rounded start. */ + cl->F = cl->S + (uint64_t)(m->m_pkthdr.len) * cl->inv_w; + roundedS = qfq_round_down(cl->S, grp->slot_shift); + + /* + * insert cl in the correct bucket. + * If cl->S >= grp->S we don't need to adjust the + * bucket list and simply go to the insertion phase. + * Otherwise grp->S is decreasing, we must make room + * in the bucket list, and also recompute the group state. + * Finally, if there were no flows in this group and nobody + * was in ER make sure to adjust V. + */ + if (grp->full_slots) { + if (!qfq_gt(grp->S, cl->S)) + goto skip_update; + /* create a slot for this cl->S */ + qfq_slot_rotate(q, grp, roundedS); + /* group was surely ineligible, remove */ + __clear_bit(grp->index, &q->bitmaps[IR]); + __clear_bit(grp->index, &q->bitmaps[IB]); + } else if (!q->bitmaps[ER] && qfq_gt(roundedS, q->V)) + q->V = roundedS; + + grp->S = roundedS; + grp->F = roundedS + (2ULL << grp->slot_shift); // i.e. 2\sigma_i + s = qfq_calc_state(q, grp); + __set_bit(grp->index, &q->bitmaps[s]); + ND("new state %d 0x%x", s, q->bitmaps[s]); + ND("S %llx F %llx V %llx", cl->S, cl->F, q->V); +skip_update: + qfq_slot_insert(grp, cl, roundedS); + + return 0; +} + + +#if 0 +static inline void +qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp, + struct qfq_class *cl, struct qfq_class **pprev) +{ + unsigned int i, offset; + uint64_t roundedS; + + roundedS = qfq_round_down(cl->S, grp->slot_shift); + offset = (roundedS - grp->S) >> grp->slot_shift; + i = (grp->front + offset) % QFQ_MAX_SLOTS; + +#ifdef notyet + if (!pprev) { + pprev = &grp->slots[i]; + while (*pprev && *pprev != cl) + pprev = &(*pprev)->next; + } +#endif + + *pprev = cl->next; + if (!grp->slots[i]) + __clear_bit(offset, &grp->full_slots); +} + +/* + * called to forcibly destroy a queue. + * If the queue is not in the front bucket, or if it has + * other queues in the front bucket, we can simply remove + * the queue with no other side effects. + * Otherwise we must propagate the event up. + * XXX description to be completed. + */ +static void +qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl, + struct qfq_class **pprev) +{ + struct qfq_group *grp = &q->groups[cl->index]; + unsigned long mask; + uint64_t roundedS; + int s; + + cl->F = cl->S; // not needed if the class goes away. + qfq_slot_remove(q, grp, cl, pprev); + + if (!grp->full_slots) { + /* nothing left in the group, remove from all sets. + * Do ER last because if we were blocking other groups + * we must unblock them. + */ + __clear_bit(grp->index, &q->bitmaps[IR]); + __clear_bit(grp->index, &q->bitmaps[EB]); + __clear_bit(grp->index, &q->bitmaps[IB]); + + if (test_bit(grp->index, &q->bitmaps[ER]) && + !(q->bitmaps[ER] & ~((1UL << grp->index) - 1))) { + mask = q->bitmaps[ER] & ((1UL << grp->index) - 1); + if (mask) + mask = ~((1UL << __fls(mask)) - 1); + else + mask = ~0UL; + qfq_move_groups(q, mask, EB, ER); + qfq_move_groups(q, mask, IB, IR); + } + __clear_bit(grp->index, &q->bitmaps[ER]); + } else if (!grp->slots[grp->front]) { + cl = qfq_slot_scan(grp); + roundedS = qfq_round_down(cl->S, grp->slot_shift); + if (grp->S != roundedS) { + __clear_bit(grp->index, &q->bitmaps[ER]); + __clear_bit(grp->index, &q->bitmaps[IR]); + __clear_bit(grp->index, &q->bitmaps[EB]); + __clear_bit(grp->index, &q->bitmaps[IB]); + grp->S = roundedS; + grp->F = roundedS + (2ULL << grp->slot_shift); + s = qfq_calc_state(q, grp); + __set_bit(grp->index, &q->bitmaps[s]); + } + } + qfq_update_eligible(q, q->V); +} +#endif + +static int +qfq_new_fsk(struct dn_fsk *f) +{ + ipdn_bound_var(&f->fs.par[0], 1, 1, QFQ_MAX_WEIGHT, "qfq weight"); + ipdn_bound_var(&f->fs.par[1], 1500, 1, 2000, "qfq maxlen"); + ND("weight %d len %d\n", f->fs.par[0], f->fs.par[1]); + return 0; +} + +/* + * initialize a new scheduler instance + */ +static int +qfq_new_sched(struct dn_sch_inst *si) +{ + struct qfq_sched *q = (struct qfq_sched *)(si + 1); + struct qfq_group *grp; + int i; + + for (i = 0; i <= QFQ_MAX_INDEX; i++) { + grp = &q->groups[i]; + grp->index = i; + grp->slot_shift = QFQ_MTU_SHIFT + FRAC_BITS - + (QFQ_MAX_INDEX - i); + } + return 0; +} + +/* + * QFQ scheduler descriptor + */ +static struct dn_alg qfq_desc = { + _SI( .type = ) DN_SCHED_QFQ, + _SI( .name = ) "QFQ", + _SI( .flags = ) DN_MULTIQUEUE, + + _SI( .schk_datalen = ) 0, + _SI( .si_datalen = ) sizeof(struct qfq_sched), + _SI( .q_datalen = ) sizeof(struct qfq_class) - sizeof(struct dn_queue), + + _SI( .enqueue = ) qfq_enqueue, + _SI( .dequeue = ) qfq_dequeue, + + _SI( .config = ) NULL, + _SI( .destroy = ) NULL, + _SI( .new_sched = ) qfq_new_sched, + _SI( .free_sched = ) NULL, + _SI( .new_fsk = ) qfq_new_fsk, + _SI( .free_fsk = ) NULL, + _SI( .new_queue = ) qfq_new_queue, + _SI( .free_queue = ) qfq_free_queue, +}; + +DECLARE_DNSCHED_MODULE(dn_qfq, &qfq_desc); + +#ifdef QFQ_DEBUG +static void +dump_groups(struct qfq_sched *q, uint32_t mask) +{ + int i, j; + + for (i = 0; i < QFQ_MAX_INDEX + 1; i++) { + struct qfq_group *g = &q->groups[i]; + + if (0 == (mask & (1<slots[j]) + D(" bucket %d %p", j, g->slots[j]); + } + D("full_slots 0x%x", g->full_slots); + D(" %2d S 0x%20llx F 0x%llx %c", i, + g->S, g->F, + mask & (1<loops, q->queued, q->V); + D(" ER 0x%08x", q->bitmaps[ER]); + D(" EB 0x%08x", q->bitmaps[EB]); + D(" IR 0x%08x", q->bitmaps[IR]); + D(" IB 0x%08x", q->bitmaps[IB]); + dump_groups(q, 0xffffffff); +}; +#endif /* QFQ_DEBUG */ diff --git a/sys/netpfil/ipfw/dn_sched_rr.c b/sys/netpfil/ipfw/dn_sched_rr.c new file mode 100644 index 0000000000000..dd608d7fbd481 --- /dev/null +++ b/sys/netpfil/ipfw/dn_sched_rr.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa + * 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. + */ + +/* + * $FreeBSD$ + */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ */ +#include +#include /* ipfw_rule_ref */ +#include /* flow_id */ +#include +#include +#include +#include +#else +#include +#endif + +#define DN_SCHED_RR 3 // XXX Where? + +struct rr_queue { + struct dn_queue q; /* Standard queue */ + int status; /* 1: queue is in the list */ + int credit; /* Number of bytes to transmit */ + int quantum; /* quantum * C */ + struct rr_queue *qnext; /* */ +}; + +/* struct rr_schk contains global config parameters + * and is right after dn_schk + */ +struct rr_schk { + int min_q; /* Min quantum */ + int max_q; /* Max quantum */ + int q_bytes; /* Bytes per quantum */ +}; + +/* per-instance round robin list, right after dn_sch_inst */ +struct rr_si { + struct rr_queue *head, *tail; /* Pointer to current queue */ +}; + +/* Append a queue to the rr list */ +static inline void +rr_append(struct rr_queue *q, struct rr_si *si) +{ + q->status = 1; /* mark as in-rr_list */ + q->credit = q->quantum; /* initialize credit */ + + /* append to the tail */ + if (si->head == NULL) + si->head = q; + else + si->tail->qnext = q; + si->tail = q; /* advance the tail pointer */ + q->qnext = si->head; /* make it circular */ +} + +/* Remove the head queue from circular list. */ +static inline void +rr_remove_head(struct rr_si *si) +{ + if (si->head == NULL) + return; /* empty queue */ + si->head->status = 0; + + if (si->head == si->tail) { + si->head = si->tail = NULL; + return; + } + + si->head = si->head->qnext; + si->tail->qnext = si->head; +} + +/* Remove a queue from circular list. + * XXX see if ti can be merge with remove_queue() + */ +static inline void +remove_queue_q(struct rr_queue *q, struct rr_si *si) +{ + struct rr_queue *prev; + + if (q->status != 1) + return; + if (q == si->head) { + rr_remove_head(si); + return; + } + + for (prev = si->head; prev; prev = prev->qnext) { + if (prev->qnext != q) + continue; + prev->qnext = q->qnext; + if (q == si->tail) + si->tail = prev; + q->status = 0; + break; + } +} + + +static inline void +next_pointer(struct rr_si *si) +{ + if (si->head == NULL) + return; /* empty queue */ + + si->head = si->head->qnext; + si->tail = si->tail->qnext; +} + +static int +rr_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m) +{ + struct rr_si *si; + struct rr_queue *rrq; + + if (m != q->mq.head) { + if (dn_enqueue(q, m, 0)) /* packet was dropped */ + return 1; + if (m != q->mq.head) + return 0; + } + + /* If reach this point, queue q was idle */ + si = (struct rr_si *)(_si + 1); + rrq = (struct rr_queue *)q; + + if (rrq->status == 1) /* Queue is already in the queue list */ + return 0; + + /* Insert the queue in the queue list */ + rr_append(rrq, si); + + return 0; +} + +static struct mbuf * +rr_dequeue(struct dn_sch_inst *_si) +{ + /* Access scheduler instance private data */ + struct rr_si *si = (struct rr_si *)(_si + 1); + struct rr_queue *rrq; + uint64_t len; + + while ( (rrq = si->head) ) { + struct mbuf *m = rrq->q.mq.head; + if ( m == NULL) { + /* empty queue, remove from list */ + rr_remove_head(si); + continue; + } + len = m->m_pkthdr.len; + + if (len > rrq->credit) { + /* Packet too big */ + rrq->credit += rrq->quantum; + /* Try next queue */ + next_pointer(si); + } else { + rrq->credit -= len; + return dn_dequeue(&rrq->q); + } + } + + /* no packet to dequeue*/ + return NULL; +} + +static int +rr_config(struct dn_schk *_schk) +{ + struct rr_schk *schk = (struct rr_schk *)(_schk + 1); + ND("called"); + + /* use reasonable quantums (64..2k bytes, default 1500) */ + schk->min_q = 64; + schk->max_q = 2048; + schk->q_bytes = 1500; /* quantum */ + + return 0; +} + +static int +rr_new_sched(struct dn_sch_inst *_si) +{ + struct rr_si *si = (struct rr_si *)(_si + 1); + + ND("called"); + si->head = si->tail = NULL; + + return 0; +} + +static int +rr_free_sched(struct dn_sch_inst *_si) +{ + ND("called"); + /* Nothing to do? */ + return 0; +} + +static int +rr_new_fsk(struct dn_fsk *fs) +{ + struct rr_schk *schk = (struct rr_schk *)(fs->sched + 1); + /* par[0] is the weight, par[1] is the quantum step */ + ipdn_bound_var(&fs->fs.par[0], 1, + 1, 65536, "RR weight"); + ipdn_bound_var(&fs->fs.par[1], schk->q_bytes, + schk->min_q, schk->max_q, "RR quantum"); + return 0; +} + +static int +rr_new_queue(struct dn_queue *_q) +{ + struct rr_queue *q = (struct rr_queue *)_q; + + _q->ni.oid.subtype = DN_SCHED_RR; + + q->quantum = _q->fs->fs.par[0] * _q->fs->fs.par[1]; + ND("called, q->quantum %d", q->quantum); + q->credit = q->quantum; + q->status = 0; + + if (_q->mq.head != NULL) { + /* Queue NOT empty, insert in the queue list */ + rr_append(q, (struct rr_si *)(_q->_si + 1)); + } + return 0; +} + +static int +rr_free_queue(struct dn_queue *_q) +{ + struct rr_queue *q = (struct rr_queue *)_q; + + ND("called"); + if (q->status == 1) { + struct rr_si *si = (struct rr_si *)(_q->_si + 1); + remove_queue_q(q, si); + } + return 0; +} + +/* + * RR scheduler descriptor + * contains the type of the scheduler, the name, the size of the + * structures and function pointers. + */ +static struct dn_alg rr_desc = { + _SI( .type = ) DN_SCHED_RR, + _SI( .name = ) "RR", + _SI( .flags = ) DN_MULTIQUEUE, + + _SI( .schk_datalen = ) 0, + _SI( .si_datalen = ) sizeof(struct rr_si), + _SI( .q_datalen = ) sizeof(struct rr_queue) - sizeof(struct dn_queue), + + _SI( .enqueue = ) rr_enqueue, + _SI( .dequeue = ) rr_dequeue, + + _SI( .config = ) rr_config, + _SI( .destroy = ) NULL, + _SI( .new_sched = ) rr_new_sched, + _SI( .free_sched = ) rr_free_sched, + _SI( .new_fsk = ) rr_new_fsk, + _SI( .free_fsk = ) NULL, + _SI( .new_queue = ) rr_new_queue, + _SI( .free_queue = ) rr_free_queue, +}; + + +DECLARE_DNSCHED_MODULE(dn_rr, &rr_desc); diff --git a/sys/netpfil/ipfw/dn_sched_wf2q.c b/sys/netpfil/ipfw/dn_sched_wf2q.c new file mode 100644 index 0000000000000..a91c1ce0475d5 --- /dev/null +++ b/sys/netpfil/ipfw/dn_sched_wf2q.c @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa + * Copyright (c) 2000-2002 Luigi Rizzo, Universita` di Pisa + * 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. + */ + +/* + * $FreeBSD$ + */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ */ +#include +#include /* ipfw_rule_ref */ +#include /* flow_id */ +#include +#include +#include +#include +#else +#include +#endif + +#ifndef MAX64 +#define MAX64(x,y) (( (int64_t) ( (y)-(x) )) > 0 ) ? (y) : (x) +#endif + +/* + * timestamps are computed on 64 bit using fixed point arithmetic. + * LMAX_BITS, WMAX_BITS are the max number of bits for the packet len + * and sum of weights, respectively. FRAC_BITS is the number of + * fractional bits. We want FRAC_BITS >> WMAX_BITS to avoid too large + * errors when computing the inverse, FRAC_BITS < 32 so we can do 1/w + * using an unsigned 32-bit division, and to avoid wraparounds we need + * LMAX_BITS + WMAX_BITS + FRAC_BITS << 64 + * As an example + * FRAC_BITS = 26, LMAX_BITS=14, WMAX_BITS = 19 + */ +#ifndef FRAC_BITS +#define FRAC_BITS 28 /* shift for fixed point arithmetic */ +#define ONE_FP (1UL << FRAC_BITS) +#endif + +/* + * Private information for the scheduler instance: + * sch_heap (key is Finish time) returns the next queue to serve + * ne_heap (key is Start time) stores not-eligible queues + * idle_heap (key=start/finish time) stores idle flows. It must + * support extract-from-middle. + * A flow is only in 1 of the three heaps. + * XXX todo: use a more efficient data structure, e.g. a tree sorted + * by F with min_subtree(S) in each node + */ +struct wf2qp_si { + struct dn_heap sch_heap; /* top extract - key Finish time */ + struct dn_heap ne_heap; /* top extract - key Start time */ + struct dn_heap idle_heap; /* random extract - key Start=Finish time */ + uint64_t V; /* virtual time */ + uint32_t inv_wsum; /* inverse of sum of weights */ + uint32_t wsum; /* sum of weights */ +}; + +struct wf2qp_queue { + struct dn_queue _q; + uint64_t S, F; /* start time, finish time */ + uint32_t inv_w; /* ONE_FP / weight */ + int32_t heap_pos; /* position (index) of struct in heap */ +}; + +/* + * This file implements a WF2Q+ scheduler as it has been in dummynet + * since 2000. + * The scheduler supports per-flow queues and has O(log N) complexity. + * + * WF2Q+ needs to drain entries from the idle heap so that we + * can keep the sum of weights up to date. We can do it whenever + * we get a chance, or periodically, or following some other + * strategy. The function idle_check() drains at most N elements + * from the idle heap. + */ +static void +idle_check(struct wf2qp_si *si, int n, int force) +{ + struct dn_heap *h = &si->idle_heap; + while (n-- > 0 && h->elements > 0 && + (force || DN_KEY_LT(HEAP_TOP(h)->key, si->V))) { + struct dn_queue *q = HEAP_TOP(h)->object; + struct wf2qp_queue *alg_fq = (struct wf2qp_queue *)q; + + heap_extract(h, NULL); + /* XXX to let the flowset delete the queue we should + * mark it as 'unused' by the scheduler. + */ + alg_fq->S = alg_fq->F + 1; /* Mark timestamp as invalid. */ + si->wsum -= q->fs->fs.par[0]; /* adjust sum of weights */ + if (si->wsum > 0) + si->inv_wsum = ONE_FP/si->wsum; + } +} + +static int +wf2qp_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m) +{ + struct dn_fsk *fs = q->fs; + struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); + struct wf2qp_queue *alg_fq; + uint64_t len = m->m_pkthdr.len; + + if (m != q->mq.head) { + if (dn_enqueue(q, m, 0)) /* packet was dropped */ + return 1; + if (m != q->mq.head) /* queue was already busy */ + return 0; + } + + /* If reach this point, queue q was idle */ + alg_fq = (struct wf2qp_queue *)q; + + if (DN_KEY_LT(alg_fq->F, alg_fq->S)) { + /* Fbrand new queue. */ + alg_fq->S = si->V; /* init start time */ + si->wsum += fs->fs.par[0]; /* add weight of new queue. */ + si->inv_wsum = ONE_FP/si->wsum; + } else { /* if it was idle then it was in the idle heap */ + heap_extract(&si->idle_heap, q); + alg_fq->S = MAX64(alg_fq->F, si->V); /* compute new S */ + } + alg_fq->F = alg_fq->S + len * alg_fq->inv_w; + + /* if nothing is backlogged, make sure this flow is eligible */ + if (si->ne_heap.elements == 0 && si->sch_heap.elements == 0) + si->V = MAX64(alg_fq->S, si->V); + + /* + * Look at eligibility. A flow is not eligibile if S>V (when + * this happens, it means that there is some other flow already + * scheduled for the same pipe, so the sch_heap cannot be + * empty). If the flow is not eligible we just store it in the + * ne_heap. Otherwise, we store in the sch_heap. + * Note that for all flows in sch_heap (SCH), S_i <= V, + * and for all flows in ne_heap (NEH), S_i > V. + * So when we need to compute max(V, min(S_i)) forall i in + * SCH+NEH, we only need to look into NEH. + */ + if (DN_KEY_LT(si->V, alg_fq->S)) { + /* S>V means flow Not eligible. */ + if (si->sch_heap.elements == 0) + D("++ ouch! not eligible but empty scheduler!"); + heap_insert(&si->ne_heap, alg_fq->S, q); + } else { + heap_insert(&si->sch_heap, alg_fq->F, q); + } + return 0; +} + +/* XXX invariant: sch > 0 || V >= min(S in neh) */ +static struct mbuf * +wf2qp_dequeue(struct dn_sch_inst *_si) +{ + /* Access scheduler instance private data */ + struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); + struct mbuf *m; + struct dn_queue *q; + struct dn_heap *sch = &si->sch_heap; + struct dn_heap *neh = &si->ne_heap; + struct wf2qp_queue *alg_fq; + + if (sch->elements == 0 && neh->elements == 0) { + /* we have nothing to do. We could kill the idle heap + * altogether and reset V + */ + idle_check(si, 0x7fffffff, 1); + si->V = 0; + si->wsum = 0; /* should be set already */ + return NULL; /* quick return if nothing to do */ + } + idle_check(si, 1, 0); /* drain something from the idle heap */ + + /* make sure at least one element is eligible, bumping V + * and moving entries that have become eligible. + * We need to repeat the first part twice, before and + * after extracting the candidate, or enqueue() will + * find the data structure in a wrong state. + */ + m = NULL; + for(;;) { + /* + * Compute V = max(V, min(S_i)). Remember that all elements + * in sch have by definition S_i <= V so if sch is not empty, + * V is surely the max and we must not update it. Conversely, + * if sch is empty we only need to look at neh. + * We don't need to move the queues, as it will be done at the + * next enqueue + */ + if (sch->elements == 0 && neh->elements > 0) { + si->V = MAX64(si->V, HEAP_TOP(neh)->key); + } + while (neh->elements > 0 && + DN_KEY_LEQ(HEAP_TOP(neh)->key, si->V)) { + q = HEAP_TOP(neh)->object; + alg_fq = (struct wf2qp_queue *)q; + heap_extract(neh, NULL); + heap_insert(sch, alg_fq->F, q); + } + if (m) /* pkt found in previous iteration */ + break; + /* ok we have at least one eligible pkt */ + q = HEAP_TOP(sch)->object; + alg_fq = (struct wf2qp_queue *)q; + m = dn_dequeue(q); + heap_extract(sch, NULL); /* Remove queue from heap. */ + si->V += (uint64_t)(m->m_pkthdr.len) * si->inv_wsum; + alg_fq->S = alg_fq->F; /* Update start time. */ + if (q->mq.head == 0) { /* not backlogged any more. */ + heap_insert(&si->idle_heap, alg_fq->F, q); + } else { /* Still backlogged. */ + /* Update F, store in neh or sch */ + uint64_t len = q->mq.head->m_pkthdr.len; + alg_fq->F += len * alg_fq->inv_w; + if (DN_KEY_LEQ(alg_fq->S, si->V)) { + heap_insert(sch, alg_fq->F, q); + } else { + heap_insert(neh, alg_fq->S, q); + } + } + } + return m; +} + +static int +wf2qp_new_sched(struct dn_sch_inst *_si) +{ + struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); + int ofs = offsetof(struct wf2qp_queue, heap_pos); + + /* all heaps support extract from middle */ + if (heap_init(&si->idle_heap, 16, ofs) || + heap_init(&si->sch_heap, 16, ofs) || + heap_init(&si->ne_heap, 16, ofs)) { + heap_free(&si->ne_heap); + heap_free(&si->sch_heap); + heap_free(&si->idle_heap); + return ENOMEM; + } + return 0; +} + +static int +wf2qp_free_sched(struct dn_sch_inst *_si) +{ + struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1); + + heap_free(&si->sch_heap); + heap_free(&si->ne_heap); + heap_free(&si->idle_heap); + + return 0; +} + +static int +wf2qp_new_fsk(struct dn_fsk *fs) +{ + ipdn_bound_var(&fs->fs.par[0], 1, + 1, 100, "WF2Q+ weight"); + return 0; +} + +static int +wf2qp_new_queue(struct dn_queue *_q) +{ + struct wf2qp_queue *q = (struct wf2qp_queue *)_q; + + _q->ni.oid.subtype = DN_SCHED_WF2QP; + q->F = 0; /* not strictly necessary */ + q->S = q->F + 1; /* mark timestamp as invalid. */ + q->inv_w = ONE_FP / _q->fs->fs.par[0]; + if (_q->mq.head != NULL) { + wf2qp_enqueue(_q->_si, _q, _q->mq.head); + } + return 0; +} + +/* + * Called when the infrastructure removes a queue (e.g. flowset + * is reconfigured). Nothing to do if we did not 'own' the queue, + * otherwise remove it from the right heap and adjust the sum + * of weights. + */ +static int +wf2qp_free_queue(struct dn_queue *q) +{ + struct wf2qp_queue *alg_fq = (struct wf2qp_queue *)q; + struct wf2qp_si *si = (struct wf2qp_si *)(q->_si + 1); + + if (alg_fq->S >= alg_fq->F + 1) + return 0; /* nothing to do, not in any heap */ + si->wsum -= q->fs->fs.par[0]; + if (si->wsum > 0) + si->inv_wsum = ONE_FP/si->wsum; + + /* extract from the heap. XXX TODO we may need to adjust V + * to make sure the invariants hold. + */ + if (q->mq.head == NULL) { + heap_extract(&si->idle_heap, q); + } else if (DN_KEY_LT(si->V, alg_fq->S)) { + heap_extract(&si->ne_heap, q); + } else { + heap_extract(&si->sch_heap, q); + } + return 0; +} + +/* + * WF2Q+ scheduler descriptor + * contains the type of the scheduler, the name, the size of the + * structures and function pointers. + */ +static struct dn_alg wf2qp_desc = { + _SI( .type = ) DN_SCHED_WF2QP, + _SI( .name = ) "WF2Q+", + _SI( .flags = ) DN_MULTIQUEUE, + + /* we need extra space in the si and the queue */ + _SI( .schk_datalen = ) 0, + _SI( .si_datalen = ) sizeof(struct wf2qp_si), + _SI( .q_datalen = ) sizeof(struct wf2qp_queue) - + sizeof(struct dn_queue), + + _SI( .enqueue = ) wf2qp_enqueue, + _SI( .dequeue = ) wf2qp_dequeue, + + _SI( .config = ) NULL, + _SI( .destroy = ) NULL, + _SI( .new_sched = ) wf2qp_new_sched, + _SI( .free_sched = ) wf2qp_free_sched, + + _SI( .new_fsk = ) wf2qp_new_fsk, + _SI( .free_fsk = ) NULL, + + _SI( .new_queue = ) wf2qp_new_queue, + _SI( .free_queue = ) wf2qp_free_queue, +}; + + +DECLARE_DNSCHED_MODULE(dn_wf2qp, &wf2qp_desc); diff --git a/sys/netpfil/ipfw/dummynet.txt b/sys/netpfil/ipfw/dummynet.txt new file mode 100644 index 0000000000000..e8c97255aeff9 --- /dev/null +++ b/sys/netpfil/ipfw/dummynet.txt @@ -0,0 +1,860 @@ +# +# $FreeBSD$ +# + +Notes on the internal structure of dummynet (2010 version) +by Riccardo Panicucci and Luigi Rizzo +Work supported by the EC project ONELAB2 + + +********* +* INDEX * +********* +Implementation of new dummynet + Internal structure + Files +Packet arrival + The reconfiguration routine +dummynet_task() +Configuration + Add a pipe + Add a scheduler + Add a flowset +Listing object +Delete of object + Delete a pipe + Delete a flowset + Delete a scheduler +Compatibility with FreeBSD7.2 and FreeBSD 8 ipfw binary + ip_dummynet_glue.c + ip_fw_glue.c +How to configure dummynet +How to implement a new scheduler + + + +OPEN ISSUES +------------------------------ +20100131 deleting RR causes infinite loop + presumably in the rr_free_queue() call -- seems to hang + forever when deleting a live flow +------------------------------ + +Dummynet is a traffic shaper and network emulator. Packets are +selected by an external filter such as ipfw, and passed to the emulator +with a tag such as "pipe 10" or "queue 5" which tells what to +do with the packet. As an example + + ipfw add queue 5 icmp from 10.0.0.2 to all + +All packets with the same tag belong to a "flowset", or a set +of flows which can be further partitioned according to a mask. +Flowsets are then passed to a scheduler for processing. The +association of flowsets and schedulers is configurable e.g. + + ipfw queue 5 config sched 10 weight 3 flow_mask xxxx + ipfw queue 8 config sched 10 weight 1 ... + ipfw queue 3 config sched 20 weight 1 ... + +"sched 10" represents one or more scheduler instances, +selected through a mask on the 5-tuple itself. + + ipfw sched 20 config type FIFO sched_mask yyy ... + +There are in fact two masks applied to each packet: ++ the "sched_mask" sends packets arriving to a scheduler_id to + one of many instances. ++ the "flow_mask" together with the flowset_id is used to + collect packets into independent flows on each scheduler. + +As an example, we can have + ipfw queue 5 config sched 10 flow_mask src-ip 0x000000ff + ipfw sched 10 config type WF2Q+ sched_mask src-ip 0xffffff00 + +means that sched 10 will have one instance per /24 source subnet, +and within that, each individual source will be a flow. + +Internal structure +----------------- +Dummynet-related data is split into several data structures, +part of them constituting the userland-kernel API, and others +specific to the kernel. +NOTE: for up-to-date details please look at the relevant source + headers (ip_dummynet.h, ip_dn_private.h, dn_sched.h) + +USERLAND-KERNEL API (ip_dummynet.h) + + struct dn_link: + contains data about the physical link such as + bandwith, delay, burst size; + + struct dn_fs: + describes a flowset, i.e. a template for queues. + Main parameters are the scheduler we attach to, a flow_mask, + buckets, queue size, plr, weight, and other scheduler-specific + parameters. + + struct dn_flow + contains information on a flow, including masks and + statistics + + struct dn_sch: + defines a scheduler (and a link attached to it). + Parameters include scheduler type, sched_mask, number of + buckets, and possibly other scheduler-specific parameters, + + struct dn_profile: + fields to simulate a delay profile + + +KERNEL REPRESENTATION (ip_dn_private.h) + + struct mq + a queue of mbufs with head and tail. + + struct dn_queue + individual queue of packets, created by a flowset using + flow_mask and attached to a scheduler instance selected + through sched_mask. + A dn_queue has a pointer to the dn_fsk (which in turn counts + how many queues point to it), a pointer to the + dn_sch_inst it attaches to, and is in a hash table in the + flowset. scheduler instances also should store queues in + their own containers used for scheduling (lists, trees, etc.) + CREATE: done on packet arrivals when a flow matches a flowset. + DELETE: done only when deleting the parent dn_sch_inst + or draining memory. + + struct dn_fsk + includes a dn_fs; a pointer to the dn_schk; a link field + for the list of dn_fsk attached to the same scheduler, + or for the unlinked list; + a refcount for the number of queues pointing to it; + The dn_fsk is in a hash table, fshash. + CREATE: done on configuration commands. + DELETE: on configuration commands. + + struct dn_sch_inst + a scheduler instance, created from a dn_schk applying sched_mask. + Contains a delay line, a reference to the parent, and scheduler- + specific info. Both dn_sch_inst and its delay line can be in the + evheap if they have events to be processed. + CREATE: created from a dn_schk applying sched_mask + DELETE: configuration command delete a scheduler which in turn + sweeps the hash table of instances deleting them + + struct dn_schk + includes dn_sch, dn_link, a pointer to dn_profile, + a hash table of dn_sch_inst, a list of dn_fsk + attached to it. + CREATE: configuration command. If there are flowsets that + refer to this number, they are attached and moved + to the hash table + DELETE: manual, see dn_sch_inst + + + fshash schedhash + +---------------+ sched +--------------+ + | sched-------------------->| NEW_SCHK| + -<----*sch_chain |<-----------------*fsk_list | + |NEW_FSK |<----. | [dn_link] | + +---------------+ | +--------------+ + |qht (hash) | | | siht(hash) | + | [dn_queue] | | | [dn_si] | + | [dn_queue] | | | [dn_si] | + | ... | | | ... | + | +--------+ | | | +---------+ | + | |dn_queue| | | | |dn_si | | + | | fs *----------' | | | | + | | si *---------------------->| | | + | +---------+ | | +---------+ | + +---------------+ +--------------+ + +The following global data structures contain all +schedulers and flowsets. + +- schedhash[x]: contains all scheduler templates in the system. + Looked up only on manual configurations, where flowsets + are attached to matching schedulers. + We have one entry per 'sched X config' command + (plus one for each 'pipe X config'). + +- fshash[x]: contains all flowsets. + We do a lookup on this for each packet. + We have one entry for each 'queue X config' + (plus one for each 'pipe X config'). + +Additionally, a list that contains all unlinked flowset: +- fsu: contains flowset that are not linked with any scheduler. + Flowset are put in this list when they refer to a non + existing scheduler. + We don't need an efficient data structure as we never search + here on a packet arrivals. + +Scheduler instances and the delay lines associated with each scheduler +instance need to be woken up at certain times. Because we have many +such objects, we keep them in a priority heap (system_heap). + +Almost all objects in this implementation are preceded by a structure +(struct dn_id) which makes it easier to identify them. + + +Files +----- +The dummynet code is split in several files. +All kernel code is in sys/netinet/ipfw except ip_dummynet.h +All userland code is in sbin/ipfw. +Files are +- sys/netinet/ip_dummynet.h defines the kernel-userland API +- ip_dn_private.h contains the kernel-specific APIs + and data structures +- dn_sched.h defines the scheduler API +- ip_dummynet.c cointains module glue and sockopt handlers, with all + functions to configure and list objects. +- ip_dn_io.c contains the functions directly related to packet processing, + and run in the critical path. It also contains some functions + exported to the schedulers. +- dn_heap.[ch] implement a binary heap and a generic hash table +- dn_sched_* implement the various scheduler modules + +- dummynet.c is the file used to implement the user side of dummynet. + It contains the function to parsing command line, and functions to + show the output of dummynet objects. +Moreover, there are two new file (ip_dummynet_glue.c and ip_fw_glue.c) that +are used to allow compatibility with the "ipfw" binary from FreeBSD 7.2 and +FreeBSD 8. + +LOCKING +======= +At the moment the entire processing occurs under a single lock +which is expected to be acquired in exclusive mode +DN_BH_WLOCK() / DN_BH_WUNLOCK(). + +In perspective we aim at the following: +- the 'busy' flag, 'pending' list and all structures modified by packet + arrivals and departures are protected by the BH_WLOCK. + This is normally acquired in exclusive mode by the packet processing + functions for short sections of code (exception -- the timer). + If 'busy' is not set, we can do regular packet processing. + If 'busy' is set, no pieces can be accessed. + We must enqueue the packet on 'pending' and return immediately. + +- the 'busy' flag is set/cleared by long sections of code as follows: + UH_WLOCK(); KASSERT(busy == 0); + BH_WLOCK(); busy=1; BH_WUNLOCK(); + ... do processing ... + BH_WLOCK(); busy=0; drain_queue(pending); BH_WUNLOCK(); + UH_WUNLOCK(); + this normally happens when the upper half has something heavy + to do. The prologue and epilogue are not in the critical path. + +- the main containers (fshash, schedhash, ...) are protected by + UH_WLOCK. + +Packet processing +================= +A packet enters dummynet through dummynet_io(). We first lookup +the flowset number in fshash using dn_ht_find(), then find the scheduler +instance using ipdn_si_find(), then possibly identify the correct +queue with ipdn_q_find(). +If successful, we call the scheduler's enqueue function(), and +if needed start I/O on the link calling serve_sched(). +If the packet can be returned immediately, this is done by +leaving *m0 set. Otherwise, the packet is absorbed by dummynet +and we simply return, possibly with some appropriate error code. + +Reconfiguration +--------------- +Reconfiguration is the complex part of the system because we need to +keep track of the various objects and containers. +At the moment we do not use reference counts for objects so all +processing must be done under a lock. + +The main entry points for configuration is the ip_dn_ctl() handler +for the IP_DUMMYNET3 sockopt (others are provided only for backward +compatibility). Modifications to the configuration call do_config(). +The argument is a sequence of blocks each starting with a struct dn_id +which specifies its content. +The first dn_id must contain as obj.id the DN_API_VERSION +The obj.type is DN_CMD_CONFIG (followed by actual objects), +DN_CMD_DELETE (with the correct subtype and list of objects), or +DN_CMD_FLUSH. + +DN_CMD_CONFIG is followed by objects to add/reconfigure. In general, +if an object already exists it is reconfigured, otherwise it is +created in a way that keeps the structure consistent. +We have the following objects in the system, normally numbered with +an identifier N between 1 and 65535. For certain objects we have +"shadow" copies numbered I+NMAX and I+ 2*NMAX which are used to +implement certain backward compatibility features. + +In general we have the following linking + + TRADITIONAL DUMMYNET QUEUES "queue N config ... pipe M ..." + corresponds to a dn_fs object numbered N + + TRADITIONAL DUMMYNET PIPES "pipe N config ..." + dn_fs N+2*NMAX --> dn_sch N+NMAX type FIFO --> dn_link N+NMAX + + GENERIC SCHEDULER "sched N config ... " + [dn_fs N+NMAX] --> dn_sch N --> dn_link N + The flowset N+NMAX is created only if the scheduler is not + of type MULTIQUEUE. + + DELAY PROFILE "pipe N config profile ..." + it is always attached to an existing dn_link N + +Because traditional dummynet pipes actually configure both a +'standalone' instance and one that can be used by queues, +we do the following: + + "pipe N config ..." configures: + dn_sched N type WF2Q+ + dn_sched N+NMAX type FIFO + dn_fs N+2NMAX attached to dn_sched N+NMAX + dn_pipe N + dn_pipe N+NMAX + + "queue N config" configures + dn_fs N + + "sched N config" configures + dn_sched N type as desired + dn_fs N+NMAX attached to dn_sched N + + +dummynet_task() +=============== +The dummynet_task() function is the main dummynet processing function and is +called every tick. This function first calculate the new current time, then +it checks if it is the time to wake up object from the system_heap comparing +the current time and the key of the heap. Two types of object (really the +heap contains pointer to objects) are in the +system_heap: + +- scheduler instance: if a scheduler instance is waked up, the dequeue() + function is called until it has credit. If the dequeue() returns packets, + the scheduler instance is inserted in the heap with a new key depending of + the data that will be send out. If the scheduler instance remains with + some credit, it means that is hasn't other packet to send and so the + instance is no longer inserted in the heap. + + If the scheduler instance extracted from the heap has the DELETE flag set, + the dequeue() is not called and the instance is destroyed now. + +- delay line: when extracting a delay line, the function transmit_event() is + called to send out packet from delay line. + + If the scheduler instance associated with this delay line doesn't exists, + the delay line will be delete now. + +Configuration +============= +To create a pipe, queue or scheduler, the user should type commands like: +"ipfw pipe x config" +"ipfw queue y config pipe x" +"ipfw pipe x config sched " + +The userland side of dummynet will prepare a buffer contains data to pass to +kernel side. +The buffer contains all struct needed to configure an object. In more detail, +to configure a pipe all three structs (dn_link, dn_sch, dn_fs) are needed, +plus the delay profile struct if the pipe has a delay profile. + +If configuring a scheduler only the struct dn_sch is wrote in the buffer, +while if configuring a flowset only the dn_fs struct is wrote. + +The first struct in the buffer contains the type of command request, that is +if it is configuring a pipe, a queue, or a scheduler. Then there are structs +need to configure the object, and finally there is the struct that mark +the end of the buffer. + +To support the insertion of pipe and queue using the old syntax, when adding +a pipe it's necessary to create a FIFO flowset and a FIFO scheduler, which +have a number x + DN_PIPEOFFSET. + +Add a pipe +---------- +A pipe is only a template for a link. +If the pipe already exists, parameters are updated. If a delay profile exists +it is deleted and a new one is created. +If the pipe doesn't exist a new one is created. After the creation, the +flowset unlinked list is scanned to see if there are some flowset that would +be linked with this pipe. If so, these flowset will be of wf2q+ type (for +compatibility) and a new wf2q+ scheduler is created now. + +Add a scheduler +--------------- +If the scheduler already exists, and the type and the mask are the same, the +scheduler is simply reconfigured calling the config_scheduler() scheduler +function with the RECONFIGURE flag active. +If the type or the mask differ, it is necessary to delete the old scheduler +and create a new one. +If the scheduler doesn't exists, a new one is created. If the scheduler has +a mask, the hash table is created to store pointers to scheduler instances. +When a new scheduler is created, it is necessary to scan the unlinked +flowset list to search eventually flowset that would be linked with this +scheduler number. If some are found, flowsets became of the type of this +scheduler and they are configured properly. + +Add a flowset +------------- +Flowset pointers are store in the system in two list. The unlinked flowset list +contains all flowset that aren't linked with a scheduler, the flowset list +contains flowset linked to a scheduler, and so they have a type. +When adding a new flowset, first it is checked if the flowset exists (that is, +it is in the flowset list) and if it doesn't exists a new flowset is created +and added to unlinked flowset list if the scheduler which the flowset would be +linked doesn't exists, or added in the flowset list and configured properly if +the scheduler exists. If the flowset (before to be created) was in the +unlinked flowset list, it is removed and deleted, and then recreated. +If the flowset exists, to allow reconfiguration of this flowset, the +scheduler number and types must match with the one in memory. If this isn't +so, the flowset is deleted and a new one will be created. Really, the flowset +it isn't deleted now, but it is removed from flowset list and it will be +deleted later because there could be some queues that are using it. + +Listing of object +================= +The user can request a list of object present in dummynet through the command +"ipfw [-v] pipe|queue [x] list|show" +The kernel side of dummynet send a buffer to user side that contains all +pipe, all scheduler, all flowset, plus all scheduler instances and all queues. +The dummynet user land will format the output and show only the relevant +information. +The buffer sent start with all pipe from the system. The entire struct dn_link +is passed, except the delay_profile struct that is useless in user space. +After pipes, all flowset are wrote in the buffer. The struct contains +scheduler flowset specific data is linked with the flowset writing the +'obj' id of the extension into the 'alg_fs' pointer. +Then schedulers are wrote. If a scheduler has one or more scheduler instance, +these are linked to the parent scheduler writing the id of the parent in the +'ptr_sched' pointer. If a scheduler instance has queues, there are wrote in +the buffer and linked thorugh the 'obj' and 'sched_inst' pointer. +Finally, flowsets in the unlinked flowset list are write in the buffer, and +then a struct gen in saved in the buffer to mark the last struct in the buffer. + + +Delete of object +================ +An object is usually removed by user through a command like +"ipfw pipe|queue x delete". XXX sched? +ipfw pass to the kernel a struct gen that contains the type and the number +of the object to remove + +Delete of pipe x +---------------- +A pipe can be deleted by the user throught the command 'ipfw pipe x delete'. +To delete a pipe, the pipe is removed from the pipe list, and then deleted. +Also the scheduler associated with this pipe should be deleted. +For compatibility with old dummynet syntax, the associated FIFO scheduler and +FIFO flowset must be deleted. + +Delete of flowset x +------------------- +To remove a flowset, we must be sure that is no loger referenced by any object. +If the flowset to remove is in the unlinked flowset list, there is not any +issue, the flowset can be safely removed calling a free() (the flowset +extension is not yet created if the flowset is in this list). +If the flowset is in the flowset list, first we remove from it so new packet +are discarded when arrive. Next, the flowset is marked as delete. +Now we must check if some queue is using this flowset. +To do this, a counter (active_f) is provided. This counter indicate how many +queues exist using this flowset. +The active_f counter is automatically incremented when a queue is created +and decremented when a queue is deleted. +If the counter is 0, the flowset can be safely deleted, and the delete_alg_fs() +scheduler function is called before deallocate memory. +If the counter is not 0, the flowset remain in memory until the counter become +zero. When a queue is delete (by dn_delete_queue() function) it is checked if +the linked flowset is deleting and if so the counter is decrementing. If the +counter reaches 0, the flowset is deleted. +The deletion of a queue can be done only by the scheduler, or when the scheduler +is destroyed. + +Delete of scheduler x +--------------------- +To delete a scheduler we must be sure that any scheduler instance of this type +are in the system_heap. To do so, a counter (inst_counter) is provided. +This counter is managed by the system: it is incremented every time it is +inserted in the system_heap, and decremented every time it is extracted from it. +To delete the scheduler, first we remove it from the scheduler list, so new +packet are discarded when they arrive, and mark the scheduler as deleting. + +If the counter is 0, we can remove the scheduler safely calling the +really_deletescheduler() function. This function will scan all scheduler +instances and call the delete_scheduler_instance() function that will delete +the instance. When all instance are deleted, the scheduler template is +deleted calling the delete_scheduler_template(). If the delay line associate +with the scheduler is empty, it is deleted now, else it will be deleted when +it will became empy. +If the counter was not 0, we wait for it. Every time the dummynet_task() +function extract a scheduler from the system_heap, the counter is decremented. +If the scheduler has the delete flag enabled the dequeue() is not called and +delete_scheduler_instance() is called to delete the instance. +Obviously this scheduler instance is no loger inserted in the system_heap. +If the counter reaches 0, the delete_scheduler_template() function is called +all memory is released. +NOTE: Flowsets that belong to this scheduler are not deleted, so if a new + scheduler with the same number is inserted will use these flowsets. + To do so, the best approach would be insert these flowset in the + unlinked flowset list, but doing this now will be very expensive. + So flowsets will remain in memory and linked with a scheduler that no + longer exists until a packet belonging to this flowset arrives. When + this packet arrives, the reconfigure() function is called because the + generation number mismatch with one contains in the flowset and so + the flowset will be moved into the flowset unlinked list, or will be + linked with the new scheduler if a new one was created. + + +COMPATIBILITY WITH FREEBSD 7.2 AND FREEBSD 8 'IPFW' BINARY +========================================================== +Dummynet is not compatible with old ipfw binary because internal structs are +changed. Moreover, the old ipfw binary is not compatible with new kernels +because the struct that represents a firewall rule has changed. So, if a user +install a new kernel on a FreeBSD 7.2, the ipfw (and possibly many other +commands) will not work. +New dummynet uses a new socket option: IP_DUMMYNET3, used for both set and get. +The old option can be used to allow compatibility with the 'ipfw' binary of +older version (tested with 7.2 and 8.0) of FreeBSD. +Two file are provided for this purpose: +- ip_dummynet_glue.c translates old dummynet requests to the new ones, +- ip_fw_glue.c converts the rule format between 7.2 and 8 versions. +Let see in detail these two files. + +IP_DUMMYNET_GLUE.C +------------------ +The internal structs of new dummynet are very different from the original. +Because of there are some difference from between dummynet in FreeBSD 7.2 and +dummynet in FreeBSD 8 (the FreeBSD 8 version includes support to pipe delay +profile and burst option), I have to include both header files. I copied +the revision 191715 (for version 7.2) and the revision 196045 (for version 8) +and I appended a number to each struct to mark them. + +The main function of this file is ip_dummynet_compat() that is called by +ip_dn_ctl() when it receive a request of old socket option. + +A global variabile ('is7') store the version of 'ipfw' that FreeBSD is using. +This variable is set every time a request of configuration is done, because +with this request we receive a buffer of which size depending of ipfw version. +Because of in general the first action is a configuration, this variable is +usually set accordly. If the first action is a request of listing of pipes +or queues, the system cannot know the version of ipfw, and we suppose that +version 7.2 is used. If version is wrong, the output can be senseless, but +the application should not crash. + +There are four request for old dummynet: +- IP_DUMMYNET_FLUSH: the flush options have no parameter, so simply the + dummynet_flush() function is called; +- IP_DUMMYNET_DEL: the delete option need to be translate. + It is only necessary to extract the number and the type of the object + (pipe or queue) to delete from the buffer received and build a new struct + gen contains the right parameters, then call the delete_object() function; +- IP_DUMMYNET_CONFIGURE: the configure command receive a buffer depending of + the ipfw version. After the properly extraction of all data, that depends + by the ipfw version used, new structures are filled and then the dummynet + config_link() function is properly called. Note that the 7.2 version does + not support some parameter as burst or delay profile. +- IP_DUMMYNET_GET: The get command should send to the ipfw the correct buffer + depending of its version. There are two function that build the + corrected buffer, ip_dummynet_get7() and ip_dummynet_get8(). These + functions reproduce the buffer exactly as 'ipfw' expect. The only difference + is that the weight parameter for a queue is no loger sent by dummynet and so + it is set to 0. + Moreover, because of the internal structure has changed, the bucket size + of a queue could not be correct, because now all flowset share the hash + table. + If the version of ipfw is wrong, the output could be senseless or truncated, + but the application should not crash. + +IP_FW_GLUE.C +------------ +The ipfw binary also is used to add rules to FreeBSD firewall. Because of the +struct ip_fw is changed from FreeBsd 7.2 to FreeBSD 8, it is necessary +to write some glue code to allow use ipfw from FreeBSD 7.2 with the kernel +provided with FreeBSD 8. +This file contains two functions to convert a rule from FreeBSD 7.2 format to +FreeBSD 8 format, and viceversa. +The conversion should be done when a rule passes from userspace to kernel space +and viceversa. +I have to modify the ip_fw2.c file to manage these two case, and added a +variable (is7) to store the ipfw version used, using an approach like the +previous file: +- when a new rule is added (option IP_FW_ADD) the is7 variable is set if the + size of the rule received corrispond to FreeBSD 7.2 ipfw version. If so, the + rule is converted to version 8 calling the function convert_rule_to_8(). + Moreover, after the insertion of the rule, the rule is now reconverted to + version 7 because the ipfw binary will print it. +- when the user request a list of rules (option IP_FW_GET) the is7 variable + should be set correctly because we suppose that a configure command was done, + else we suppose that the FreeBSD version is 8. The function ipfw_getrules() + in ip_fw2.c file return all rules, eventually converted to version 7 (if + the is7 is set) to the ipfw binary. +The conversion of a rule is quite simple. The only difference between the +two structures (struct ip_fw) is that in the new there is a new field +(uint32_t id). So, I copy the entire rule in a buffer and the copy the rule in +the right position in the new (or old) struct. The size of commands are not +changed, and the copy is done into a cicle. + +How to configure dummynet +========================= +It is possible to configure dummynet through two main commands: +'ipfw pipe' and 'ipfw queue'. +To allow compatibility with old version, it is possible configure dummynet +using the old command syntax. Doing so, obviously, it is only possible to +configure a FIFO scheduler or a wf2q+ scheduler. +A new command, 'ipfw pipe x config sched ' is supported to add a new +scheduler to the system. + +- ipfw pipe x config ... + create a new pipe with the link parameters + create a new scheduler fifo (x + offset) + create a new flowset fifo (x + offset) + the mask is eventually stored in the FIFO scheduler + +- ipfw queue y config pipe x ... + create a new flowset y linked to sched x. + The type of flowset depends by the specified scheduler. + If the scheduler does not exist, this flowset is inserted in a special + list and will be not active. + If pipe x exists and sched does not exist, a new wf2q+ scheduler is + created and the flowset will be linked to this new scheduler (this is + done for compatibility with old syntax). + +- ipfw pipe x config sched ... + create a new scheduler x of type . + Search into the flowset unlinked list if there are some flowset that + should be linked with this new scheduler. + +- ipfw pipe x delete + delete the pipe x + delete the scheduler fifo (x + offset) + delete the scheduler x + delete the flowset fifo (x + offset) + +- ipfw queue x delete + delete the flowset x + +- ipfw sched x delete ///XXX + delete the scheduler x + +Follow now some examples to how configure dummynet: +- Ex1: + ipfw pipe 10 config bw 1M delay 15 // create a pipe with band and delay + A FIFO flowset and scheduler is + also created + ipfw queue 5 config pipe 10 weight 56 // create a flowset. This flowset + will be of wf2q+ because a pipe 10 + exists. Moreover, the wf2q+ + scheduler is created now. +- Ex2: + ipfw queue 5 config pipe 10 weight 56 // Create a flowset. Scheduler 10 + does not exist, so this flowset + is inserted in the unlinked + flowset list. + ipfw pipe 10 config bw... // Create a pipe, a FIFO flowset and scheduler. + Because of a flowset with 'pipe 10' exists, + a wf2q+ scheduler is created now and that + flowset is linked with this sceduler. + +- Ex3: + ipfw pipe 10 config bw... // Create a pipe, a FIFO flowset and scheduler. + ipfw pipe 10 config sched rr // Create a scheduler of type RR, linked to + pipe 10 + ipfw queue 5 config pipe 10 weight 56 // Create a flowset 5. This flowset + will belong to scheduler 10 and + it is of type RR + +- Ex4: + ipfw pipe 10 config sched rr // Create a scheduler of type RR, linked to + pipe 10 (not exist yet) + ipfw pipe 10 config bw... // Create a pipe, a FIFO flowset and scheduler. + ipfw queue 5 config pipe 10 weight 56 // Create a flowset 5.This flowset + will belong to scheduler 10 and + it is of type RR + ipfw pipe 10 config sched wf2q+ // Modify the type of scheduler 10. It + becomes a wf2q+ scheduler. + When a new packet of flowset 5 arrives, + the flowset 5 becomes to wf2q+ type. + +How to implement a new scheduler +================================ +In dummynet, a scheduler algorithm is represented by two main structs, some +functions and other minor structs. +- A struct dn_sch_xyz (where xyz is the 'type' of scheduler algorithm + implemented) contains data relative to scheduler, as global parameter that + are common to all instances of the scheduler +- A struct dn_sch_inst_xyz contains data relative to a single scheduler + instance, as local status variable depending for example by flows that + are linked with the scheduler, and so on. +To add a scheduler to dummynet, the user should type a command like: +'ipfw pipe x config sched [mask ... ...]' +This command creates a new struct dn_sch_xyz of type , and +store the optional parameter in that struct. + +The parameter mask determines how many scheduler instance of this +scheduler may exist. For example, it is possible to divide traffic +depending on the source port (or destination, or ip address...), +so that every scheduler instance act as an independent scheduler. +If the mask is not set, all traffic goes to the same instance. + +When a packet arrives to a scheduler, the system search the corrected +scheduler instance, and if it does not exist it is created now (the +struct dn_sch_inst_xyz is allocated by the system, and the scheduler +fills the field correctly). It is a task of the scheduler to create +the struct that contains all queues for a scheduler instance. +Dummynet provides some function to create an hash table to store +queues, but the schedule algorithm can choice the own struct. + +To link a flow to a scheduler, the user should type a command like: +'ipfw queue z config pipe x [mask... ...]' + +This command creates a new 'dn_fs' struct that will be inserted +in the system. If the scheduler x exists, this flowset will be +linked to that scheduler and the flowset type become the same as +the scheduler type. At this point, the function create_alg_fs_xyz() +is called to allow store eventually parameter for the flowset that +depend by scheduler (for example the 'weight' parameter for a wf2q+ +scheduler, or some priority...). A parameter mask can be used for +a flowset. If the mask parameter is set, the scheduler instance can +separate packet according to its flow id (src and dst ip, ports...) +and assign it to a separate queue. This is done by the scheduler, +so it can ignore the mask if it wants. + +See now the two main structs: +struct dn_sch_xyz { + struct gen g; /* important the name g */ + /* global params */ +}; +struct dn_sch_inst_xyz { + struct gen g; /* important the name g */ + /* params of the instance */ +}; +It is important to embed the struct gen as first parameter. The struct gen +contains some values that the scheduler instance must fill (the 'type' of +scheduler, the 'len' of the struct...) +The function create_scheduler_xyz() should be implemented to initialize global +parameters in the first struct, and if memory allocation is done it is +mandatory to implement the delete_scheduler_template() function to free that +memory. +The function create_scheduler_instance_xyz() must be implemented even if the +scheduler instance does not use extra parameters. In this function the struct +gen fields must be filled with corrected infos. The +delete_scheduler_instance_xyz() function must bu implemented if the instance +has allocated some memory in the previous function. + +To store data belonging to a flowset the follow struct is used: +struct alg_fs_xyz { + struct gen g; + /* fill correctly the gen struct + g.subtype = DN_XYZ; + g.len = sizeof(struct alg_fs_xyz) + ... + */ + /* params for the flow */ +}; +The create_alg_fs_xyz() function is mandatory, because it must fill the struct +gen, but the delete_alg_fs_xyz() is mandatory only if the previous function +has allocated some memory. + +A struct dn_queue contains packets belonging to a queue and some statistical +data. The scheduler could have to store data in this struct, so it must define +a dn_queue_xyz struct: +struct dn_queue_xyz { + struct dn_queue q; + /* parameter for a queue */ +} + +All structures are allocated by the system. To do so, the scheduler must +set the size of its structs in the scheduler descriptor: +scheduler_size: sizeof(dn_sch_xyz) +scheduler_i_size: sizeof(dn_sch_inst_xyz) +flowset_size: sizeof(alg_fs_xyz) +queue_size: sizeof(dn_queue_xyz); +The scheduler_size could be 0, but other struct must have at least a struct gen. + + +After the definition of structs, it is necessary to implement the +scheduler functions. + +- int (*config_scheduler)(char *command, void *sch, int reconfigure); + Configure a scheduler, or reconfigure if 'reconfigure' == 1. + This function performs additional allocation and initialization of global + parameter for this scheduler. + If memory is allocated here, the delete_scheduler_template() function + should be implemented to remove this memory. +- int (*delete_scheduler_template)(void* sch); + Delete a scheduler template. This function is mandatory if the scheduler + uses extra data respect the struct dn_sch. +- int (*create_scheduler_instance)(void *s); + Create a new scheduler instance. The system allocate the necessary memory + and the schedulet can access it using the 's' pointer. + The scheduler instance stores all queues, and to do this can use the + hash table provided by the system. +- int (*delete_scheduler_instance)(void *s); + Delete a scheduler instance. It is important to free memory allocated + by create_scheduler_instance() function. The memory allocated by system + is freed by the system itself. The struct contains all queue also has + to be deleted. +- int (*enqueue)(void *s, struct gen *f, struct mbuf *m, + struct ipfw_flow_id *id); + Called when a packet arrives. The packet 'm' belongs to the scheduler + instance 's', has a flowset 'f' and the flowid 'id' has already been + masked. The enqueue() must call dn_queue_packet(q, m) function to really + enqueue packet in the queue q. The queue 'q' is chosen by the scheduler + and if it does not exist should be created calling the dn_create_queue() + function. If the schedule want to drop the packet, it must call the + dn_drop_packet() function and then return 1. +- struct mbuf * (*dequeue)(void *s); + Called when the timer expires (or when a packet arrives and the scheduler + instance is idle). + This function is called when at least a packet can be send out. The + scheduler choices the packet and returns it; if no packet are in the + schedulerinstance, the function must return NULL. + Before return a packet, it is important to call the function + dn_return_packet() to update some statistic of the queue and update the + queue counters. +- int (*drain_queue)(void *s, int flag); + The system request to scheduler to delete all queues that is not using + to free memory. The flag parameter indicate if a queue must be deleted + even if it is active. + +- int (*create_alg_fs)(char *command, struct gen *g, int reconfigure); + It is called when a flowset is linked with a scheduler. This is done + when the scheduler is defined, so we can know the type of flowset. + The function initialize the flowset paramenter parsing the command + line. The parameter will be stored in the g struct that have the right + size allocated by the system. If the reconfigure flag is set, it means + that the flowset is reconfiguring +- int (*delete_alg_fs)(struct gen *f); + It is called when a flowset is deleting. Must remove the memory allocate + by the create_alg_fs() function. + +- int (*create_queue_alg)(struct dn_queue *q, struct gen *f); + Called when a queue is created. The function should link the queue + to the struct used by the scheduler instance to store all queues. +- int (*delete_queue_alg)(struct dn_queue *q); + Called when a queue is deleting. The function should remove extra data + and update the struct contains all queues in the scheduler instance. + +The struct scheduler represent the scheduler descriptor that is passed to +dummynet when a scheduler module is loaded. +This struct contains the type of scheduler, the length of all structs and +all function pointers. +If a function is not implemented should be initialize to NULL. Some functions +are mandatory, other are mandatory if some memory should be freed. +Mandatory functions: +- create_scheduler_instance() +- enqueue() +- dequeue() +- create_alg_fs() +- drain_queue() +Optional functions: +- config_scheduler() +- create_queue_alg() +Mandatory functions if the corresponding create...() has allocated memory: +- delete_scheduler_template() +- delete_scheduler_instance() +- delete_alg_fs() +- delete_queue_alg() + diff --git a/sys/netpfil/ipfw/ip_dn_glue.c b/sys/netpfil/ipfw/ip_dn_glue.c new file mode 100644 index 0000000000000..2e4f460babea6 --- /dev/null +++ b/sys/netpfil/ipfw/ip_dn_glue.c @@ -0,0 +1,846 @@ +/*- + * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa + * 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. + */ + +/* + * $FreeBSD$ + * + * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8 + */ + +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ +#include +#include /* ip_output(), IP_FORWARDING */ +#include +#include + +#include +#include +#include +#include + +/* FREEBSD7.2 ip_dummynet.h r191715*/ + +struct dn_heap_entry7 { + int64_t key; /* sorting key. Topmost element is smallest one */ + void *object; /* object pointer */ +}; + +struct dn_heap7 { + int size; + int elements; + int offset; /* XXX if > 0 this is the offset of direct ptr to obj */ + struct dn_heap_entry7 *p; /* really an array of "size" entries */ +}; + +/* Common to 7.2 and 8 */ +struct dn_flow_set { + SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ + + u_short fs_nr ; /* flow_set number */ + u_short flags_fs; +#define DNOLD_HAVE_FLOW_MASK 0x0001 +#define DNOLD_IS_RED 0x0002 +#define DNOLD_IS_GENTLE_RED 0x0004 +#define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ +#define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ +#define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ +#define DNOLD_IS_PIPE 0x4000 +#define DNOLD_IS_QUEUE 0x8000 + + struct dn_pipe7 *pipe ; /* pointer to parent pipe */ + u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ + + int weight ; /* WFQ queue weight */ + int qsize ; /* queue size in slots or bytes */ + int plr ; /* pkt loss rate (2^31-1 means 100%) */ + + struct ipfw_flow_id flow_mask ; + + /* hash table of queues onto this flow_set */ + int rq_size ; /* number of slots */ + int rq_elements ; /* active elements */ + struct dn_flow_queue7 **rq; /* array of rq_size entries */ + + u_int32_t last_expired ; /* do not expire too frequently */ + int backlogged ; /* #active queues for this flowset */ + + /* RED parameters */ +#define SCALE_RED 16 +#define SCALE(x) ( (x) << SCALE_RED ) +#define SCALE_VAL(x) ( (x) >> SCALE_RED ) +#define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED ) + int w_q ; /* queue weight (scaled) */ + int max_th ; /* maximum threshold for queue (scaled) */ + int min_th ; /* minimum threshold for queue (scaled) */ + int max_p ; /* maximum value for p_b (scaled) */ + u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ + u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ + u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ + u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ + u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ + u_int lookup_depth ; /* depth of lookup table */ + int lookup_step ; /* granularity inside the lookup table */ + int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ + int avg_pkt_size ; /* medium packet size */ + int max_pkt_size ; /* max packet size */ +}; +SLIST_HEAD(dn_flow_set_head, dn_flow_set); + +#define DN_IS_PIPE 0x4000 +#define DN_IS_QUEUE 0x8000 +struct dn_flow_queue7 { + struct dn_flow_queue7 *next ; + struct ipfw_flow_id id ; + + struct mbuf *head, *tail ; /* queue of packets */ + u_int len ; + u_int len_bytes ; + + u_long numbytes; + + u_int64_t tot_pkts ; /* statistics counters */ + u_int64_t tot_bytes ; + u_int32_t drops ; + + int hash_slot ; /* debugging/diagnostic */ + + /* RED parameters */ + int avg ; /* average queue length est. (scaled) */ + int count ; /* arrivals since last RED drop */ + int random ; /* random value (scaled) */ + u_int32_t q_time; /* start of queue idle time */ + + /* WF2Q+ support */ + struct dn_flow_set *fs ; /* parent flow set */ + int heap_pos ; /* position (index) of struct in heap */ + int64_t sched_time ; /* current time when queue enters ready_heap */ + + int64_t S,F ; /* start time, finish time */ +}; + +struct dn_pipe7 { /* a pipe */ + SLIST_ENTRY(dn_pipe7) next; /* linked list in a hash slot */ + + int pipe_nr ; /* number */ + int bandwidth; /* really, bytes/tick. */ + int delay ; /* really, ticks */ + + struct mbuf *head, *tail ; /* packets in delay line */ + + /* WF2Q+ */ + struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ + struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ + struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ + + int64_t V ; /* virtual time */ + int sum; /* sum of weights of all active sessions */ + + int numbytes; + + int64_t sched_time ; /* time pipe was scheduled in ready_heap */ + + /* + * When the tx clock come from an interface (if_name[0] != '\0'), its name + * is stored below, whereas the ifp is filled when the rule is configured. + */ + char if_name[IFNAMSIZ]; + struct ifnet *ifp ; + int ready ; /* set if ifp != NULL and we got a signal from it */ + + struct dn_flow_set fs ; /* used with fixed-rate flows */ +}; +SLIST_HEAD(dn_pipe_head7, dn_pipe7); + + +/* FREEBSD8 ip_dummynet.h r196045 */ +struct dn_flow_queue8 { + struct dn_flow_queue8 *next ; + struct ipfw_flow_id id ; + + struct mbuf *head, *tail ; /* queue of packets */ + u_int len ; + u_int len_bytes ; + + uint64_t numbytes ; /* credit for transmission (dynamic queues) */ + int64_t extra_bits; /* extra bits simulating unavailable channel */ + + u_int64_t tot_pkts ; /* statistics counters */ + u_int64_t tot_bytes ; + u_int32_t drops ; + + int hash_slot ; /* debugging/diagnostic */ + + /* RED parameters */ + int avg ; /* average queue length est. (scaled) */ + int count ; /* arrivals since last RED drop */ + int random ; /* random value (scaled) */ + int64_t idle_time; /* start of queue idle time */ + + /* WF2Q+ support */ + struct dn_flow_set *fs ; /* parent flow set */ + int heap_pos ; /* position (index) of struct in heap */ + int64_t sched_time ; /* current time when queue enters ready_heap */ + + int64_t S,F ; /* start time, finish time */ +}; + +struct dn_pipe8 { /* a pipe */ + SLIST_ENTRY(dn_pipe8) next; /* linked list in a hash slot */ + + int pipe_nr ; /* number */ + int bandwidth; /* really, bytes/tick. */ + int delay ; /* really, ticks */ + + struct mbuf *head, *tail ; /* packets in delay line */ + + /* WF2Q+ */ + struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ + struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ + struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ + + int64_t V ; /* virtual time */ + int sum; /* sum of weights of all active sessions */ + + /* Same as in dn_flow_queue, numbytes can become large */ + int64_t numbytes; /* bits I can transmit (more or less). */ + uint64_t burst; /* burst size, scaled: bits * hz */ + + int64_t sched_time ; /* time pipe was scheduled in ready_heap */ + int64_t idle_time; /* start of pipe idle time */ + + char if_name[IFNAMSIZ]; + struct ifnet *ifp ; + int ready ; /* set if ifp != NULL and we got a signal from it */ + + struct dn_flow_set fs ; /* used with fixed-rate flows */ + + /* fields to simulate a delay profile */ +#define ED_MAX_NAME_LEN 32 + char name[ED_MAX_NAME_LEN]; + int loss_level; + int samples_no; + int *samples; +}; + +#define ED_MAX_SAMPLES_NO 1024 +struct dn_pipe_max8 { + struct dn_pipe8 pipe; + int samples[ED_MAX_SAMPLES_NO]; +}; +SLIST_HEAD(dn_pipe_head8, dn_pipe8); + +/* + * Changes from 7.2 to 8: + * dn_pipe: + * numbytes from int to int64_t + * add burst (int64_t) + * add idle_time (int64_t) + * add profile + * add struct dn_pipe_max + * add flag DN_HAS_PROFILE + * + * dn_flow_queue + * numbytes from u_long to int64_t + * add extra_bits (int64_t) + * q_time from u_int32_t to int64_t and name idle_time + * + * dn_flow_set unchanged + * + */ + +/* NOTE:XXX copied from dummynet.c */ +#define O_NEXT(p, len) ((void *)((char *)p + len)) +static void +oid_fill(struct dn_id *oid, int len, int type, uintptr_t id) +{ + oid->len = len; + oid->type = type; + oid->subtype = 0; + oid->id = id; +} +/* make room in the buffer and move the pointer forward */ +static void * +o_next(struct dn_id **o, int len, int type) +{ + struct dn_id *ret = *o; + oid_fill(ret, len, type, 0); + *o = O_NEXT(*o, len); + return ret; +} + + +static size_t pipesize7 = sizeof(struct dn_pipe7); +static size_t pipesize8 = sizeof(struct dn_pipe8); +static size_t pipesizemax8 = sizeof(struct dn_pipe_max8); + +/* Indicate 'ipfw' version + * 1: from FreeBSD 7.2 + * 0: from FreeBSD 8 + * -1: unknow (for now is unused) + * + * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives + * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknow, + * it is suppose to be the FreeBSD 8 version. + */ +static int is7 = 0; + +static int +convertflags2new(int src) +{ + int dst = 0; + + if (src & DNOLD_HAVE_FLOW_MASK) + dst |= DN_HAVE_MASK; + if (src & DNOLD_QSIZE_IS_BYTES) + dst |= DN_QSIZE_BYTES; + if (src & DNOLD_NOERROR) + dst |= DN_NOERROR; + if (src & DNOLD_IS_RED) + dst |= DN_IS_RED; + if (src & DNOLD_IS_GENTLE_RED) + dst |= DN_IS_GENTLE_RED; + if (src & DNOLD_HAS_PROFILE) + dst |= DN_HAS_PROFILE; + + return dst; +} + +static int +convertflags2old(int src) +{ + int dst = 0; + + if (src & DN_HAVE_MASK) + dst |= DNOLD_HAVE_FLOW_MASK; + if (src & DN_IS_RED) + dst |= DNOLD_IS_RED; + if (src & DN_IS_GENTLE_RED) + dst |= DNOLD_IS_GENTLE_RED; + if (src & DN_NOERROR) + dst |= DNOLD_NOERROR; + if (src & DN_HAS_PROFILE) + dst |= DNOLD_HAS_PROFILE; + if (src & DN_QSIZE_BYTES) + dst |= DNOLD_QSIZE_IS_BYTES; + + return dst; +} + +static int +dn_compat_del(void *v) +{ + struct dn_pipe7 *p = (struct dn_pipe7 *) v; + struct dn_pipe8 *p8 = (struct dn_pipe8 *) v; + struct { + struct dn_id oid; + uintptr_t a[1]; /* add more if we want a list */ + } cmd; + + /* XXX DN_API_VERSION ??? */ + oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); + + if (is7) { + if (p->pipe_nr == 0 && p->fs.fs_nr == 0) + return EINVAL; + if (p->pipe_nr != 0 && p->fs.fs_nr != 0) + return EINVAL; + } else { + if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0) + return EINVAL; + if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0) + return EINVAL; + } + + if (p->pipe_nr != 0) { /* pipe x delete */ + cmd.a[0] = p->pipe_nr; + cmd.oid.subtype = DN_LINK; + } else { /* queue x delete */ + cmd.oid.subtype = DN_FS; + cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr; + } + + return do_config(&cmd, cmd.oid.len); +} + +static int +dn_compat_config_queue(struct dn_fs *fs, void* v) +{ + struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; + struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; + struct dn_flow_set *f; + + if (is7) + f = &p7->fs; + else + f = &p8->fs; + + fs->fs_nr = f->fs_nr; + fs->sched_nr = f->parent_nr; + fs->flow_mask = f->flow_mask; + fs->buckets = f->rq_size; + fs->qsize = f->qsize; + fs->plr = f->plr; + fs->par[0] = f->weight; + fs->flags = convertflags2new(f->flags_fs); + if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { + fs->w_q = f->w_q; + fs->max_th = f->max_th; + fs->min_th = f->min_th; + fs->max_p = f->max_p; + } + + return 0; +} + +static int +dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p, + struct dn_fs *fs, void* v) +{ + struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; + struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; + int i = p7->pipe_nr; + + sch->sched_nr = i; + sch->oid.subtype = 0; + p->link_nr = i; + fs->fs_nr = i + 2*DN_MAX_ID; + fs->sched_nr = i + DN_MAX_ID; + + /* Common to 7 and 8 */ + p->bandwidth = p7->bandwidth; + p->delay = p7->delay; + if (!is7) { + /* FreeBSD 8 has burst */ + p->burst = p8->burst; + } + + /* fill the fifo flowset */ + dn_compat_config_queue(fs, v); + fs->fs_nr = i + 2*DN_MAX_ID; + fs->sched_nr = i + DN_MAX_ID; + + /* Move scheduler related parameter from fs to sch */ + sch->buckets = fs->buckets; /*XXX*/ + fs->buckets = 0; + if (fs->flags & DN_HAVE_MASK) { + sch->flags |= DN_HAVE_MASK; + fs->flags &= ~DN_HAVE_MASK; + sch->sched_mask = fs->flow_mask; + bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id)); + } + + return 0; +} + +static int +dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p, + void *v) +{ + struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; + + p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]); + + pf->link_nr = p->link_nr; + pf->loss_level = p8->loss_level; +// pf->bandwidth = p->bandwidth; //XXX bandwidth redundant? + pf->samples_no = p8->samples_no; + strncpy(pf->name, p8->name,sizeof(pf->name)); + bcopy(p8->samples, pf->samples, sizeof(pf->samples)); + + return 0; +} + +/* + * If p->pipe_nr != 0 the command is 'pipe x config', so need to create + * the three main struct, else only a flowset is created + */ +static int +dn_compat_configure(void *v) +{ + struct dn_id *buf = NULL, *base; + struct dn_sch *sch = NULL; + struct dn_link *p = NULL; + struct dn_fs *fs = NULL; + struct dn_profile *pf = NULL; + int lmax; + int error; + + struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; + struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; + + int i; /* number of object to configure */ + + lmax = sizeof(struct dn_id); /* command header */ + lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + + sizeof(struct dn_fs) + sizeof(struct dn_profile); + + base = buf = malloc(lmax, M_DUMMYNET, M_WAIT|M_ZERO); + o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); + base->id = DN_API_VERSION; + + /* pipe_nr is the same in p7 and p8 */ + i = p7->pipe_nr; + if (i != 0) { /* pipe config */ + sch = o_next(&buf, sizeof(*sch), DN_SCH); + p = o_next(&buf, sizeof(*p), DN_LINK); + fs = o_next(&buf, sizeof(*fs), DN_FS); + + error = dn_compat_config_pipe(sch, p, fs, v); + if (error) { + free(buf, M_DUMMYNET); + return error; + } + if (!is7 && p8->samples_no > 0) { + /* Add profiles*/ + pf = o_next(&buf, sizeof(*pf), DN_PROFILE); + error = dn_compat_config_profile(pf, p, v); + if (error) { + free(buf, M_DUMMYNET); + return error; + } + } + } else { /* queue config */ + fs = o_next(&buf, sizeof(*fs), DN_FS); + error = dn_compat_config_queue(fs, v); + if (error) { + free(buf, M_DUMMYNET); + return error; + } + } + error = do_config(base, (char *)buf - (char *)base); + + if (buf) + free(buf, M_DUMMYNET); + return error; +} + +int +dn_compat_calc_size(void) +{ + int need = 0; + /* XXX use FreeBSD 8 struct size */ + /* NOTE: + * - half scheduler: schk_count/2 + * - all flowset: fsk_count + * - all flowset queues: queue_count + * - all pipe queue: si_count + */ + need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2; + need += dn_cfg.fsk_count * sizeof(struct dn_flow_set); + need += dn_cfg.si_count * sizeof(struct dn_flow_queue8); + need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8); + + return need; +} + +int +dn_c_copy_q (void *_ni, void *arg) +{ + struct copy_args *a = arg; + struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start; + struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start; + struct dn_flow *ni = (struct dn_flow *)_ni; + int size = 0; + + /* XXX hash slot not set */ + /* No difference between 7.2/8 */ + fq7->len = ni->length; + fq7->len_bytes = ni->len_bytes; + fq7->id = ni->fid; + + if (is7) { + size = sizeof(struct dn_flow_queue7); + fq7->tot_pkts = ni->tot_pkts; + fq7->tot_bytes = ni->tot_bytes; + fq7->drops = ni->drops; + } else { + size = sizeof(struct dn_flow_queue8); + fq8->tot_pkts = ni->tot_pkts; + fq8->tot_bytes = ni->tot_bytes; + fq8->drops = ni->drops; + } + + *a->start += size; + return 0; +} + +int +dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq) +{ + struct dn_link *l = &s->link; + struct dn_fsk *f = s->fs; + + struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start; + struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start; + struct dn_flow_set *fs; + int size = 0; + + if (is7) { + fs = &pipe7->fs; + size = sizeof(struct dn_pipe7); + } else { + fs = &pipe8->fs; + size = sizeof(struct dn_pipe8); + } + + /* These 4 field are the same in pipe7 and pipe8 */ + pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE; + pipe7->bandwidth = l->bandwidth; + pipe7->delay = l->delay * 1000 / hz; + pipe7->pipe_nr = l->link_nr - DN_MAX_ID; + + if (!is7) { + if (s->profile) { + struct dn_profile *pf = s->profile; + strncpy(pipe8->name, pf->name, sizeof(pf->name)); + pipe8->loss_level = pf->loss_level; + pipe8->samples_no = pf->samples_no; + } + pipe8->burst = div64(l->burst , 8 * hz); + } + + fs->flow_mask = s->sch.sched_mask; + fs->rq_size = s->sch.buckets ? s->sch.buckets : 1; + + fs->parent_nr = l->link_nr - DN_MAX_ID; + fs->qsize = f->fs.qsize; + fs->plr = f->fs.plr; + fs->w_q = f->fs.w_q; + fs->max_th = f->max_th; + fs->min_th = f->min_th; + fs->max_p = f->fs.max_p; + fs->rq_elements = nq; + + fs->flags_fs = convertflags2old(f->fs.flags); + + *a->start += size; + return 0; +} + + +int +dn_compat_copy_pipe(struct copy_args *a, void *_o) +{ + int have = a->end - *a->start; + int need = 0; + int pipe_size = sizeof(struct dn_pipe8); + int queue_size = sizeof(struct dn_flow_queue8); + int n_queue = 0; /* number of queues */ + + struct dn_schk *s = (struct dn_schk *)_o; + /* calculate needed space: + * - struct dn_pipe + * - if there are instances, dn_queue * n_instances + */ + n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) : + (s->siht ? 1 : 0)); + need = pipe_size + queue_size * n_queue; + if (have < need) { + D("have %d < need %d", have, need); + return 1; + } + /* copy pipe */ + dn_c_copy_pipe(s, a, n_queue); + + /* copy queues */ + if (s->sch.flags & DN_HAVE_MASK) + dn_ht_scan(s->siht, dn_c_copy_q, a); + else if (s->siht) + dn_c_copy_q(s->siht, a); + return 0; +} + +int +dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) +{ + struct dn_flow_set *fs = (struct dn_flow_set *)*a->start; + + fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; + fs->fs_nr = f->fs.fs_nr; + fs->qsize = f->fs.qsize; + fs->plr = f->fs.plr; + fs->w_q = f->fs.w_q; + fs->max_th = f->max_th; + fs->min_th = f->min_th; + fs->max_p = f->fs.max_p; + fs->flow_mask = f->fs.flow_mask; + fs->rq_elements = nq; + fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1); + fs->parent_nr = f->fs.sched_nr; + fs->weight = f->fs.par[0]; + + fs->flags_fs = convertflags2old(f->fs.flags); + *a->start += sizeof(struct dn_flow_set); + return 0; +} + +int +dn_compat_copy_queue(struct copy_args *a, void *_o) +{ + int have = a->end - *a->start; + int need = 0; + int fs_size = sizeof(struct dn_flow_set); + int queue_size = sizeof(struct dn_flow_queue8); + + struct dn_fsk *fs = (struct dn_fsk *)_o; + int n_queue = 0; /* number of queues */ + + n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) : + (fs->qht ? 1 : 0)); + + need = fs_size + queue_size * n_queue; + if (have < need) { + D("have < need"); + return 1; + } + + /* copy flowset */ + dn_c_copy_fs(fs, a, n_queue); + + /* copy queues */ + if (fs->fs.flags & DN_HAVE_MASK) + dn_ht_scan(fs->qht, dn_c_copy_q, a); + else if (fs->qht) + dn_c_copy_q(fs->qht, a); + + return 0; +} + +int +copy_data_helper_compat(void *_o, void *_arg) +{ + struct copy_args *a = _arg; + + if (a->type == DN_COMPAT_PIPE) { + struct dn_schk *s = _o; + if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) { + return 0; /* not old type */ + } + /* copy pipe parameters, and if instance exists, copy + * other parameters and eventually queues. + */ + if(dn_compat_copy_pipe(a, _o)) + return DNHT_SCAN_END; + } else if (a->type == DN_COMPAT_QUEUE) { + struct dn_fsk *fs = _o; + if (fs->fs.fs_nr >= DN_MAX_ID) + return 0; + if (dn_compat_copy_queue(a, _o)) + return DNHT_SCAN_END; + } + return 0; +} + +/* Main function to manage old requests */ +int +ip_dummynet_compat(struct sockopt *sopt) +{ + int error=0; + void *v = NULL; + struct dn_id oid; + + /* Lenght of data, used to found ipfw version... */ + int len = sopt->sopt_valsize; + + /* len can be 0 if command was dummynet_flush */ + if (len == pipesize7) { + D("setting compatibility with FreeBSD 7.2"); + is7 = 1; + } + else if (len == pipesize8 || len == pipesizemax8) { + D("setting compatibility with FreeBSD 8"); + is7 = 0; + } + + switch (sopt->sopt_name) { + default: + printf("dummynet: -- unknown option %d", sopt->sopt_name); + error = EINVAL; + break; + + case IP_DUMMYNET_FLUSH: + oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); + do_config(&oid, oid.len); + break; + + case IP_DUMMYNET_DEL: + v = malloc(len, M_TEMP, M_WAITOK); + error = sooptcopyin(sopt, v, len, len); + if (error) + break; + error = dn_compat_del(v); + free(v, M_TEMP); + break; + + case IP_DUMMYNET_CONFIGURE: + v = malloc(len, M_TEMP, M_WAITOK); + error = sooptcopyin(sopt, v, len, len); + if (error) + break; + error = dn_compat_configure(v); + free(v, M_TEMP); + break; + + case IP_DUMMYNET_GET: { + void *buf; + int ret; + int original_size = sopt->sopt_valsize; + int size; + + ret = dummynet_get(sopt, &buf); + if (ret) + return 0;//XXX ? + size = sopt->sopt_valsize; + sopt->sopt_valsize = original_size; + D("size=%d, buf=%p", size, buf); + ret = sooptcopyout(sopt, buf, size); + if (ret) + printf(" %s ERROR sooptcopyout\n", __FUNCTION__); + if (buf) + free(buf, M_DUMMYNET); + } + } + + return error; +} + + diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c new file mode 100644 index 0000000000000..2047b7479ad76 --- /dev/null +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -0,0 +1,857 @@ +/*- + * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa + * 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. + */ + +/* + * Dummynet portions related to packet handling. + */ +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ +#include +#include + +#include +#include /* ip_len, ip_off */ +#include /* ip_output(), IP_FORWARDING */ +#include +#include +#include /* various ether_* routines */ +#include /* for ip6_input, ip6_output prototypes */ +#include + +#include +#include +#include +#include + +/* + * We keep a private variable for the simulation time, but we could + * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) + * instead of dn_cfg.curr_time + */ + +struct dn_parms dn_cfg; +//VNET_DEFINE(struct dn_parms, _base_dn_cfg); + +static long tick_last; /* Last tick duration (usec). */ +static long tick_delta; /* Last vs standard tick diff (usec). */ +static long tick_delta_sum; /* Accumulated tick difference (usec).*/ +static long tick_adjustment; /* Tick adjustments done. */ +static long tick_lost; /* Lost(coalesced) ticks number. */ +/* Adjusted vs non-adjusted curr_time difference (ticks). */ +static long tick_diff; + +static unsigned long io_pkt; +static unsigned long io_pkt_fast; +static unsigned long io_pkt_drop; + +/* + * We use a heap to store entities for which we have pending timer events. + * The heap is checked at every tick and all entities with expired events + * are extracted. + */ + +MALLOC_DEFINE(M_DUMMYNET, "dummynet", "dummynet heap"); + +extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); + +#ifdef SYSCTL_NODE + +/* + * Because of the way the SYSBEGIN/SYSEND macros work on other + * platforms, there should not be functions between them. + * So keep the handlers outside the block. + */ +static int +sysctl_hash_size(SYSCTL_HANDLER_ARGS) +{ + int error, value; + + value = dn_cfg.hash_size; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (value < 16 || value > 65536) + return (EINVAL); + dn_cfg.hash_size = value; + return (0); +} + +static int +sysctl_limits(SYSCTL_HANDLER_ARGS) +{ + int error; + long value; + + if (arg2 != 0) + value = dn_cfg.slot_limit; + else + value = dn_cfg.byte_limit; + error = sysctl_handle_long(oidp, &value, 0, req); + + if (error != 0 || req->newptr == NULL) + return (error); + if (arg2 != 0) { + if (value < 1) + return (EINVAL); + dn_cfg.slot_limit = value; + } else { + if (value < 1500) + return (EINVAL); + dn_cfg.byte_limit = value; + } + return (0); +} + +SYSBEGIN(f4) + +SYSCTL_DECL(_net_inet); +SYSCTL_DECL(_net_inet_ip); +static SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet"); + +/* wrapper to pass dn_cfg fields to SYSCTL_* */ +//#define DC(x) (&(VNET_NAME(_base_dn_cfg).x)) +#define DC(x) (&(dn_cfg.x)) +/* parameters */ + + +SYSCTL_PROC(_net_inet_ip_dummynet, OID_AUTO, hash_size, + CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_hash_size, + "I", "Default hash table size"); + + +SYSCTL_PROC(_net_inet_ip_dummynet, OID_AUTO, pipe_slot_limit, + CTLTYPE_LONG | CTLFLAG_RW, 0, 1, sysctl_limits, + "L", "Upper limit in slots for pipe queue."); +SYSCTL_PROC(_net_inet_ip_dummynet, OID_AUTO, pipe_byte_limit, + CTLTYPE_LONG | CTLFLAG_RW, 0, 0, sysctl_limits, + "L", "Upper limit in bytes for pipe queue."); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, io_fast, + CTLFLAG_RW, DC(io_fast), 0, "Enable fast dummynet io."); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug, + CTLFLAG_RW, DC(debug), 0, "Dummynet debug level"); + +/* RED parameters */ +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_lookup_depth, + CTLFLAG_RD, DC(red_lookup_depth), 0, "Depth of RED lookup table"); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_avg_pkt_size, + CTLFLAG_RD, DC(red_avg_pkt_size), 0, "RED Medium packet size"); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_max_pkt_size, + CTLFLAG_RD, DC(red_max_pkt_size), 0, "RED Max packet size"); + +/* time adjustment */ +SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta, + CTLFLAG_RD, &tick_delta, 0, "Last vs standard tick difference (usec)."); +SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta_sum, + CTLFLAG_RD, &tick_delta_sum, 0, "Accumulated tick difference (usec)."); +SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_adjustment, + CTLFLAG_RD, &tick_adjustment, 0, "Tick adjustments done."); +SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_diff, + CTLFLAG_RD, &tick_diff, 0, + "Adjusted vs non-adjusted curr_time difference (ticks)."); +SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_lost, + CTLFLAG_RD, &tick_lost, 0, + "Number of ticks coalesced by dummynet taskqueue."); + +/* Drain parameters */ +SYSCTL_UINT(_net_inet_ip_dummynet, OID_AUTO, expire, + CTLFLAG_RW, DC(expire), 0, "Expire empty queues/pipes"); +SYSCTL_UINT(_net_inet_ip_dummynet, OID_AUTO, expire_cycle, + CTLFLAG_RD, DC(expire_cycle), 0, "Expire cycle for queues/pipes"); + +/* statistics */ +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, schk_count, + CTLFLAG_RD, DC(schk_count), 0, "Number of schedulers"); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, si_count, + CTLFLAG_RD, DC(si_count), 0, "Number of scheduler instances"); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, fsk_count, + CTLFLAG_RD, DC(fsk_count), 0, "Number of flowsets"); +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, queue_count, + CTLFLAG_RD, DC(queue_count), 0, "Number of queues"); +SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt, + CTLFLAG_RD, &io_pkt, 0, + "Number of packets passed to dummynet."); +SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_fast, + CTLFLAG_RD, &io_pkt_fast, 0, + "Number of packets bypassed dummynet scheduler."); +SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_drop, + CTLFLAG_RD, &io_pkt_drop, 0, + "Number of packets dropped by dummynet."); +#undef DC +SYSEND + +#endif + +static void dummynet_send(struct mbuf *); + +/* + * Packets processed by dummynet have an mbuf tag associated with + * them that carries their dummynet state. + * Outside dummynet, only the 'rule' field is relevant, and it must + * be at the beginning of the structure. + */ +struct dn_pkt_tag { + struct ipfw_rule_ref rule; /* matching rule */ + + /* second part, dummynet specific */ + int dn_dir; /* action when packet comes out.*/ + /* see ip_fw_private.h */ + uint64_t output_time; /* when the pkt is due for delivery*/ + struct ifnet *ifp; /* interface, for ip_output */ + struct _ip6dn_args ip6opt; /* XXX ipv6 options */ +}; + +/* + * Return the mbuf tag holding the dummynet state (it should + * be the first one on the list). + */ +static struct dn_pkt_tag * +dn_tag_get(struct mbuf *m) +{ + struct m_tag *mtag = m_tag_first(m); + KASSERT(mtag != NULL && + mtag->m_tag_cookie == MTAG_ABI_COMPAT && + mtag->m_tag_id == PACKET_TAG_DUMMYNET, + ("packet on dummynet queue w/o dummynet tag!")); + return (struct dn_pkt_tag *)(mtag+1); +} + +static inline void +mq_append(struct mq *q, struct mbuf *m) +{ + if (q->head == NULL) + q->head = m; + else + q->tail->m_nextpkt = m; + q->tail = m; + m->m_nextpkt = NULL; +} + +/* + * Dispose a list of packet. Use a functions so if we need to do + * more work, this is a central point to do it. + */ +void dn_free_pkts(struct mbuf *mnext) +{ + struct mbuf *m; + + while ((m = mnext) != NULL) { + mnext = m->m_nextpkt; + FREE_PKT(m); + } +} + +static int +red_drops (struct dn_queue *q, int len) +{ + /* + * RED algorithm + * + * RED calculates the average queue size (avg) using a low-pass filter + * with an exponential weighted (w_q) moving average: + * avg <- (1-w_q) * avg + w_q * q_size + * where q_size is the queue length (measured in bytes or * packets). + * + * If q_size == 0, we compute the idle time for the link, and set + * avg = (1 - w_q)^(idle/s) + * where s is the time needed for transmitting a medium-sized packet. + * + * Now, if avg < min_th the packet is enqueued. + * If avg > max_th the packet is dropped. Otherwise, the packet is + * dropped with probability P function of avg. + */ + + struct dn_fsk *fs = q->fs; + int64_t p_b = 0; + + /* Queue in bytes or packets? */ + uint32_t q_size = (fs->fs.flags & DN_QSIZE_BYTES) ? + q->ni.len_bytes : q->ni.length; + + /* Average queue size estimation. */ + if (q_size != 0) { + /* Queue is not empty, avg <- avg + (q_size - avg) * w_q */ + int diff = SCALE(q_size) - q->avg; + int64_t v = SCALE_MUL((int64_t)diff, (int64_t)fs->w_q); + + q->avg += (int)v; + } else { + /* + * Queue is empty, find for how long the queue has been + * empty and use a lookup table for computing + * (1 - * w_q)^(idle_time/s) where s is the time to send a + * (small) packet. + * XXX check wraps... + */ + if (q->avg) { + u_int t = div64((dn_cfg.curr_time - q->q_time), fs->lookup_step); + + q->avg = (t < fs->lookup_depth) ? + SCALE_MUL(q->avg, fs->w_q_lookup[t]) : 0; + } + } + + /* Should i drop? */ + if (q->avg < fs->min_th) { + q->count = -1; + return (0); /* accept packet */ + } + if (q->avg >= fs->max_th) { /* average queue >= max threshold */ + if (fs->fs.flags & DN_IS_GENTLE_RED) { + /* + * According to Gentle-RED, if avg is greater than + * max_th the packet is dropped with a probability + * p_b = c_3 * avg - c_4 + * where c_3 = (1 - max_p) / max_th + * c_4 = 1 - 2 * max_p + */ + p_b = SCALE_MUL((int64_t)fs->c_3, (int64_t)q->avg) - + fs->c_4; + } else { + q->count = -1; + return (1); + } + } else if (q->avg > fs->min_th) { + /* + * We compute p_b using the linear dropping function + * p_b = c_1 * avg - c_2 + * where c_1 = max_p / (max_th - min_th) + * c_2 = max_p * min_th / (max_th - min_th) + */ + p_b = SCALE_MUL((int64_t)fs->c_1, (int64_t)q->avg) - fs->c_2; + } + + if (fs->fs.flags & DN_QSIZE_BYTES) + p_b = div64((p_b * len) , fs->max_pkt_size); + if (++q->count == 0) + q->random = random() & 0xffff; + else { + /* + * q->count counts packets arrived since last drop, so a greater + * value of q->count means a greater packet drop probability. + */ + if (SCALE_MUL(p_b, SCALE((int64_t)q->count)) > q->random) { + q->count = 0; + /* After a drop we calculate a new random value. */ + q->random = random() & 0xffff; + return (1); /* drop */ + } + } + /* End of RED algorithm. */ + + return (0); /* accept */ + +} + +/* + * Enqueue a packet in q, subject to space and queue management policy + * (whose parameters are in q->fs). + * Update stats for the queue and the scheduler. + * Return 0 on success, 1 on drop. The packet is consumed anyways. + */ +int +dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) +{ + struct dn_fs *f; + struct dn_flow *ni; /* stats for scheduler instance */ + uint64_t len; + + if (q->fs == NULL || q->_si == NULL) { + printf("%s fs %p si %p, dropping\n", + __FUNCTION__, q->fs, q->_si); + FREE_PKT(m); + return 1; + } + f = &(q->fs->fs); + ni = &q->_si->ni; + len = m->m_pkthdr.len; + /* Update statistics, then check reasons to drop pkt. */ + q->ni.tot_bytes += len; + q->ni.tot_pkts++; + ni->tot_bytes += len; + ni->tot_pkts++; + if (drop) + goto drop; + if (f->plr && random() < f->plr) + goto drop; + if (f->flags & DN_IS_RED && red_drops(q, m->m_pkthdr.len)) + goto drop; + if (f->flags & DN_QSIZE_BYTES) { + if (q->ni.len_bytes > f->qsize) + goto drop; + } else if (q->ni.length >= f->qsize) { + goto drop; + } + mq_append(&q->mq, m); + q->ni.length++; + q->ni.len_bytes += len; + ni->length++; + ni->len_bytes += len; + return 0; + +drop: + io_pkt_drop++; + q->ni.drops++; + ni->drops++; + FREE_PKT(m); + return 1; +} + +/* + * Fetch packets from the delay line which are due now. If there are + * leftover packets, reinsert the delay line in the heap. + * Runs under scheduler lock. + */ +static void +transmit_event(struct mq *q, struct delay_line *dline, uint64_t now) +{ + struct mbuf *m; + struct dn_pkt_tag *pkt = NULL; + + dline->oid.subtype = 0; /* not in heap */ + while ((m = dline->mq.head) != NULL) { + pkt = dn_tag_get(m); + if (!DN_KEY_LEQ(pkt->output_time, now)) + break; + dline->mq.head = m->m_nextpkt; + mq_append(q, m); + } + if (m != NULL) { + dline->oid.subtype = 1; /* in heap */ + heap_insert(&dn_cfg.evheap, pkt->output_time, dline); + } +} + +/* + * Convert the additional MAC overheads/delays into an equivalent + * number of bits for the given data rate. The samples are + * in milliseconds so we need to divide by 1000. + */ +static uint64_t +extra_bits(struct mbuf *m, struct dn_schk *s) +{ + int index; + uint64_t bits; + struct dn_profile *pf = s->profile; + + if (!pf || pf->samples_no == 0) + return 0; + index = random() % pf->samples_no; + bits = div64((uint64_t)pf->samples[index] * s->link.bandwidth, 1000); + if (index >= pf->loss_level) { + struct dn_pkt_tag *dt = dn_tag_get(m); + if (dt) + dt->dn_dir = DIR_DROP; + } + return bits; +} + +/* + * Send traffic from a scheduler instance due by 'now'. + * Return a pointer to the head of the queue. + */ +static struct mbuf * +serve_sched(struct mq *q, struct dn_sch_inst *si, uint64_t now) +{ + struct mq def_q; + struct dn_schk *s = si->sched; + struct mbuf *m = NULL; + int delay_line_idle = (si->dline.mq.head == NULL); + int done, bw; + + if (q == NULL) { + q = &def_q; + q->head = NULL; + } + + bw = s->link.bandwidth; + si->kflags &= ~DN_ACTIVE; + + if (bw > 0) + si->credit += (now - si->sched_time) * bw; + else + si->credit = 0; + si->sched_time = now; + done = 0; + while (si->credit >= 0 && (m = s->fp->dequeue(si)) != NULL) { + uint64_t len_scaled; + + done++; + len_scaled = (bw == 0) ? 0 : hz * + (m->m_pkthdr.len * 8 + extra_bits(m, s)); + si->credit -= len_scaled; + /* Move packet in the delay line */ + dn_tag_get(m)->output_time = dn_cfg.curr_time + s->link.delay ; + mq_append(&si->dline.mq, m); + } + + /* + * If credit >= 0 the instance is idle, mark time. + * Otherwise put back in the heap, and adjust the output + * time of the last inserted packet, m, which was too early. + */ + if (si->credit >= 0) { + si->idle_time = now; + } else { + uint64_t t; + KASSERT (bw > 0, ("bw=0 and credit<0 ?")); + t = div64(bw - 1 - si->credit, bw); + if (m) + dn_tag_get(m)->output_time += t; + si->kflags |= DN_ACTIVE; + heap_insert(&dn_cfg.evheap, now + t, si); + } + if (delay_line_idle && done) + transmit_event(q, &si->dline, now); + return q->head; +} + +/* + * The timer handler for dummynet. Time is computed in ticks, but + * but the code is tolerant to the actual rate at which this is called. + * Once complete, the function reschedules itself for the next tick. + */ +void +dummynet_task(void *context, int pending) +{ + struct timeval t; + struct mq q = { NULL, NULL }; /* queue to accumulate results */ + + CURVNET_SET((struct vnet *)context); + + DN_BH_WLOCK(); + + /* Update number of lost(coalesced) ticks. */ + tick_lost += pending - 1; + + getmicrouptime(&t); + /* Last tick duration (usec). */ + tick_last = (t.tv_sec - dn_cfg.prev_t.tv_sec) * 1000000 + + (t.tv_usec - dn_cfg.prev_t.tv_usec); + /* Last tick vs standard tick difference (usec). */ + tick_delta = (tick_last * hz - 1000000) / hz; + /* Accumulated tick difference (usec). */ + tick_delta_sum += tick_delta; + + dn_cfg.prev_t = t; + + /* + * Adjust curr_time if the accumulated tick difference is + * greater than the 'standard' tick. Since curr_time should + * be monotonically increasing, we do positive adjustments + * as required, and throttle curr_time in case of negative + * adjustment. + */ + dn_cfg.curr_time++; + if (tick_delta_sum - tick >= 0) { + int diff = tick_delta_sum / tick; + + dn_cfg.curr_time += diff; + tick_diff += diff; + tick_delta_sum %= tick; + tick_adjustment++; + } else if (tick_delta_sum + tick <= 0) { + dn_cfg.curr_time--; + tick_diff--; + tick_delta_sum += tick; + tick_adjustment++; + } + + /* serve pending events, accumulate in q */ + for (;;) { + struct dn_id *p; /* generic parameter to handler */ + + if (dn_cfg.evheap.elements == 0 || + DN_KEY_LT(dn_cfg.curr_time, HEAP_TOP(&dn_cfg.evheap)->key)) + break; + p = HEAP_TOP(&dn_cfg.evheap)->object; + heap_extract(&dn_cfg.evheap, NULL); + + if (p->type == DN_SCH_I) { + serve_sched(&q, (struct dn_sch_inst *)p, dn_cfg.curr_time); + } else { /* extracted a delay line */ + transmit_event(&q, (struct delay_line *)p, dn_cfg.curr_time); + } + } + if (dn_cfg.expire && ++dn_cfg.expire_cycle >= dn_cfg.expire) { + dn_cfg.expire_cycle = 0; + dn_drain_scheduler(); + dn_drain_queue(); + } + + DN_BH_WUNLOCK(); + dn_reschedule(); + if (q.head != NULL) + dummynet_send(q.head); + CURVNET_RESTORE(); +} + +/* + * forward a chain of packets to the proper destination. + * This runs outside the dummynet lock. + */ +static void +dummynet_send(struct mbuf *m) +{ + struct mbuf *n; + + for (; m != NULL; m = n) { + struct ifnet *ifp = NULL; /* gcc 3.4.6 complains */ + struct m_tag *tag; + int dst; + + n = m->m_nextpkt; + m->m_nextpkt = NULL; + tag = m_tag_first(m); + if (tag == NULL) { /* should not happen */ + dst = DIR_DROP; + } else { + struct dn_pkt_tag *pkt = dn_tag_get(m); + /* extract the dummynet info, rename the tag + * to carry reinject info. + */ + dst = pkt->dn_dir; + ifp = pkt->ifp; + tag->m_tag_cookie = MTAG_IPFW_RULE; + tag->m_tag_id = 0; + } + + switch (dst) { + case DIR_OUT: + SET_HOST_IPLEN(mtod(m, struct ip *)); + ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); + break ; + + case DIR_IN : + /* put header in network format for ip_input() */ + //SET_NET_IPLEN(mtod(m, struct ip *)); + netisr_dispatch(NETISR_IP, m); + break; + +#ifdef INET6 + case DIR_IN | PROTO_IPV6: + netisr_dispatch(NETISR_IPV6, m); + break; + + case DIR_OUT | PROTO_IPV6: + ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL); + break; +#endif + + case DIR_FWD | PROTO_IFB: /* DN_TO_IFB_FWD: */ + if (bridge_dn_p != NULL) + ((*bridge_dn_p)(m, ifp)); + else + printf("dummynet: if_bridge not loaded\n"); + + break; + + case DIR_IN | PROTO_LAYER2: /* DN_TO_ETH_DEMUX: */ + /* + * The Ethernet code assumes the Ethernet header is + * contiguous in the first mbuf header. + * Insure this is true. + */ + if (m->m_len < ETHER_HDR_LEN && + (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) { + printf("dummynet/ether: pullup failed, " + "dropping packet\n"); + break; + } + ether_demux(m->m_pkthdr.rcvif, m); + break; + + case DIR_OUT | PROTO_LAYER2: /* N_TO_ETH_OUT: */ + ether_output_frame(ifp, m); + break; + + case DIR_DROP: + /* drop the packet after some time */ + FREE_PKT(m); + break; + + default: + printf("dummynet: bad switch %d!\n", dst); + FREE_PKT(m); + break; + } + } +} + +static inline int +tag_mbuf(struct mbuf *m, int dir, struct ip_fw_args *fwa) +{ + struct dn_pkt_tag *dt; + struct m_tag *mtag; + + mtag = m_tag_get(PACKET_TAG_DUMMYNET, + sizeof(*dt), M_NOWAIT | M_ZERO); + if (mtag == NULL) + return 1; /* Cannot allocate packet header. */ + m_tag_prepend(m, mtag); /* Attach to mbuf chain. */ + dt = (struct dn_pkt_tag *)(mtag + 1); + dt->rule = fwa->rule; + dt->rule.info &= IPFW_ONEPASS; /* only keep this info */ + dt->dn_dir = dir; + dt->ifp = fwa->oif; + /* dt->output tame is updated as we move through */ + dt->output_time = dn_cfg.curr_time; + return 0; +} + + +/* + * dummynet hook for packets. + * We use the argument to locate the flowset fs and the sched_set sch + * associated to it. The we apply flow_mask and sched_mask to + * determine the queue and scheduler instances. + * + * dir where shall we send the packet after dummynet. + * *m0 the mbuf with the packet + * ifp the 'ifp' parameter from the caller. + * NULL in ip_input, destination interface in ip_output, + */ +int +dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa) +{ + struct mbuf *m = *m0; + struct dn_fsk *fs = NULL; + struct dn_sch_inst *si; + struct dn_queue *q = NULL; /* default */ + + int fs_id = (fwa->rule.info & IPFW_INFO_MASK) + + ((fwa->rule.info & IPFW_IS_PIPE) ? 2*DN_MAX_ID : 0); + DN_BH_WLOCK(); + io_pkt++; + /* we could actually tag outside the lock, but who cares... */ + if (tag_mbuf(m, dir, fwa)) + goto dropit; + if (dn_cfg.busy) { + /* if the upper half is busy doing something expensive, + * lets queue the packet and move forward + */ + mq_append(&dn_cfg.pending, m); + m = *m0 = NULL; /* consumed */ + goto done; /* already active, nothing to do */ + } + /* XXX locate_flowset could be optimised with a direct ref. */ + fs = dn_ht_find(dn_cfg.fshash, fs_id, 0, NULL); + if (fs == NULL) + goto dropit; /* This queue/pipe does not exist! */ + if (fs->sched == NULL) /* should not happen */ + goto dropit; + /* find scheduler instance, possibly applying sched_mask */ + si = ipdn_si_find(fs->sched, &(fwa->f_id)); + if (si == NULL) + goto dropit; + /* + * If the scheduler supports multiple queues, find the right one + * (otherwise it will be ignored by enqueue). + */ + if (fs->sched->fp->flags & DN_MULTIQUEUE) { + q = ipdn_q_find(fs, si, &(fwa->f_id)); + if (q == NULL) + goto dropit; + } + if (fs->sched->fp->enqueue(si, q, m)) { + /* packet was dropped by enqueue() */ + m = *m0 = NULL; + goto dropit; + } + + if (si->kflags & DN_ACTIVE) { + m = *m0 = NULL; /* consumed */ + goto done; /* already active, nothing to do */ + } + + /* compute the initial allowance */ + if (si->idle_time < dn_cfg.curr_time) { + /* Do this only on the first packet on an idle pipe */ + struct dn_link *p = &fs->sched->link; + + si->sched_time = dn_cfg.curr_time; + si->credit = dn_cfg.io_fast ? p->bandwidth : 0; + if (p->burst) { + uint64_t burst = (dn_cfg.curr_time - si->idle_time) * p->bandwidth; + if (burst > p->burst) + burst = p->burst; + si->credit += burst; + } + } + /* pass through scheduler and delay line */ + m = serve_sched(NULL, si, dn_cfg.curr_time); + + /* optimization -- pass it back to ipfw for immediate send */ + /* XXX Don't call dummynet_send() if scheduler return the packet + * just enqueued. This avoid a lock order reversal. + * + */ + if (/*dn_cfg.io_fast &&*/ m == *m0 && (dir & PROTO_LAYER2) == 0 ) { + /* fast io, rename the tag * to carry reinject info. */ + struct m_tag *tag = m_tag_first(m); + + tag->m_tag_cookie = MTAG_IPFW_RULE; + tag->m_tag_id = 0; + io_pkt_fast++; + if (m->m_nextpkt != NULL) { + printf("dummynet: fast io: pkt chain detected!\n"); + m->m_nextpkt = NULL; + } + m = NULL; + } else { + *m0 = NULL; + } +done: + DN_BH_WUNLOCK(); + if (m) + dummynet_send(m); + return 0; + +dropit: + io_pkt_drop++; + DN_BH_WUNLOCK(); + if (m) + FREE_PKT(m); + *m0 = NULL; + return (fs && (fs->fs.flags & DN_NOERROR)) ? 0 : ENOBUFS; +} diff --git a/sys/netpfil/ipfw/ip_dn_private.h b/sys/netpfil/ipfw/ip_dn_private.h new file mode 100644 index 0000000000000..159ddc9ab8b29 --- /dev/null +++ b/sys/netpfil/ipfw/ip_dn_private.h @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa + * 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. + */ + +/* + * internal dummynet APIs. + * + * $FreeBSD$ + */ + +#ifndef _IP_DN_PRIVATE_H +#define _IP_DN_PRIVATE_H + +/* debugging support + * use ND() to remove debugging, D() to print a line, + * DX(level, ...) to print above a certain level + * If you redefine D() you are expected to redefine all. + */ +#ifndef D +#define ND(fmt, ...) do {} while (0) +#define D1(fmt, ...) do {} while (0) +#define D(fmt, ...) printf("%-10s " fmt "\n", \ + __FUNCTION__, ## __VA_ARGS__) +#define DX(lev, fmt, ...) do { \ + if (dn_cfg.debug > lev) D(fmt, ## __VA_ARGS__); } while (0) +#endif + +MALLOC_DECLARE(M_DUMMYNET); + +#ifndef __linux__ +#define div64(a, b) ((int64_t)(a) / (int64_t)(b)) +#endif + +#define DN_LOCK_INIT() do { \ + mtx_init(&dn_cfg.uh_mtx, "dn_uh", NULL, MTX_DEF); \ + mtx_init(&dn_cfg.bh_mtx, "dn_bh", NULL, MTX_DEF); \ + } while (0) +#define DN_LOCK_DESTROY() do { \ + mtx_destroy(&dn_cfg.uh_mtx); \ + mtx_destroy(&dn_cfg.bh_mtx); \ + } while (0) +#if 0 /* not used yet */ +#define DN_UH_RLOCK() mtx_lock(&dn_cfg.uh_mtx) +#define DN_UH_RUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) +#define DN_UH_WLOCK() mtx_lock(&dn_cfg.uh_mtx) +#define DN_UH_WUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) +#define DN_UH_LOCK_ASSERT() mtx_assert(&dn_cfg.uh_mtx, MA_OWNED) +#endif + +#define DN_BH_RLOCK() mtx_lock(&dn_cfg.uh_mtx) +#define DN_BH_RUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) +#define DN_BH_WLOCK() mtx_lock(&dn_cfg.uh_mtx) +#define DN_BH_WUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) +#define DN_BH_LOCK_ASSERT() mtx_assert(&dn_cfg.uh_mtx, MA_OWNED) + +SLIST_HEAD(dn_schk_head, dn_schk); +SLIST_HEAD(dn_sch_inst_head, dn_sch_inst); +SLIST_HEAD(dn_fsk_head, dn_fsk); +SLIST_HEAD(dn_queue_head, dn_queue); +SLIST_HEAD(dn_alg_head, dn_alg); + +struct mq { /* a basic queue of packets*/ + struct mbuf *head, *tail; +}; + +static inline void +set_oid(struct dn_id *o, int type, int len) +{ + o->type = type; + o->len = len; + o->subtype = 0; +}; + +/* + * configuration and global data for a dummynet instance + * + * When a configuration is modified from userland, 'id' is incremented + * so we can use the value to check for stale pointers. + */ +struct dn_parms { + uint32_t id; /* configuration version */ + + /* defaults (sysctl-accessible) */ + int red_lookup_depth; + int red_avg_pkt_size; + int red_max_pkt_size; + int hash_size; + int max_hash_size; + long byte_limit; /* max queue sizes */ + long slot_limit; + + int io_fast; + int debug; + + /* timekeeping */ + struct timeval prev_t; /* last time dummynet_tick ran */ + struct dn_heap evheap; /* scheduled events */ + + /* counters of objects -- used for reporting space */ + int schk_count; + int si_count; + int fsk_count; + int queue_count; + + /* ticks and other stuff */ + uint64_t curr_time; + /* flowsets and schedulers are in hash tables, with 'hash_size' + * buckets. fshash is looked up at every packet arrival + * so better be generous if we expect many entries. + */ + struct dn_ht *fshash; + struct dn_ht *schedhash; + /* list of flowsets without a scheduler -- use sch_chain */ + struct dn_fsk_head fsu; /* list of unlinked flowsets */ + struct dn_alg_head schedlist; /* list of algorithms */ + + /* Store the fs/sch to scan when draining. The value is the + * bucket number of the hash table. Expire can be disabled + * with net.inet.ip.dummynet.expire=0, or it happens every + * expire ticks. + **/ + int drain_fs; + int drain_sch; + uint32_t expire; + uint32_t expire_cycle; /* tick count */ + + int init_done; + + /* if the upper half is busy doing something long, + * can set the busy flag and we will enqueue packets in + * a queue for later processing. + */ + int busy; + struct mq pending; + +#ifdef _KERNEL + /* + * This file is normally used in the kernel, unless we do + * some userland tests, in which case we do not need a mtx. + * uh_mtx arbitrates between system calls and also + * protects fshash, schedhash and fsunlinked. + * These structures are readonly for the lower half. + * bh_mtx protects all other structures which may be + * modified upon packet arrivals + */ +#if defined( __linux__ ) || defined( _WIN32 ) + spinlock_t uh_mtx; + spinlock_t bh_mtx; +#else + struct mtx uh_mtx; + struct mtx bh_mtx; +#endif + +#endif /* _KERNEL */ +}; + +/* + * Delay line, contains all packets on output from a link. + * Every scheduler instance has one. + */ +struct delay_line { + struct dn_id oid; + struct dn_sch_inst *si; + struct mq mq; +}; + +/* + * The kernel side of a flowset. It is linked in a hash table + * of flowsets, and in a list of children of their parent scheduler. + * qht is either the queue or (if HAVE_MASK) a hash table queues. + * Note that the mask to use is the (flow_mask|sched_mask), which + * changes as we attach/detach schedulers. So we store it here. + * + * XXX If we want to add scheduler-specific parameters, we need to + * put them in external storage because the scheduler may not be + * available when the fsk is created. + */ +struct dn_fsk { /* kernel side of a flowset */ + struct dn_fs fs; + SLIST_ENTRY(dn_fsk) fsk_next; /* hash chain for fshash */ + + struct ipfw_flow_id fsk_mask; + + /* qht is a hash table of queues, or just a single queue + * a bit in fs.flags tells us which one + */ + struct dn_ht *qht; + struct dn_schk *sched; /* Sched we are linked to */ + SLIST_ENTRY(dn_fsk) sch_chain; /* list of fsk attached to sched */ + + /* bucket index used by drain routine to drain queues for this + * flowset + */ + int drain_bucket; + /* Parameter realted to RED / GRED */ + /* original values are in dn_fs*/ + int w_q ; /* queue weight (scaled) */ + int max_th ; /* maximum threshold for queue (scaled) */ + int min_th ; /* minimum threshold for queue (scaled) */ + int max_p ; /* maximum value for p_b (scaled) */ + + u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ + u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ + u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ + u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ + u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ + u_int lookup_depth ; /* depth of lookup table */ + int lookup_step ; /* granularity inside the lookup table */ + int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ + int avg_pkt_size ; /* medium packet size */ + int max_pkt_size ; /* max packet size */ +}; + +/* + * A queue is created as a child of a flowset unless it belongs to + * a !MULTIQUEUE scheduler. It is normally in a hash table in the + * flowset. fs always points to the parent flowset. + * si normally points to the sch_inst, unless the flowset has been + * detached from the scheduler -- in this case si == NULL and we + * should not enqueue. + */ +struct dn_queue { + struct dn_flow ni; /* oid, flow_id, stats */ + struct mq mq; /* packets queue */ + struct dn_sch_inst *_si; /* owner scheduler instance */ + SLIST_ENTRY(dn_queue) q_next; /* hash chain list for qht */ + struct dn_fsk *fs; /* parent flowset. */ + + /* RED parameters */ + int avg; /* average queue length est. (scaled) */ + int count; /* arrivals since last RED drop */ + int random; /* random value (scaled) */ + uint64_t q_time; /* start of queue idle time */ + +}; + +/* + * The kernel side of a scheduler. Contains the userland config, + * a link, pointer to extra config arguments from command line, + * kernel flags, and a pointer to the scheduler methods. + * It is stored in a hash table, and holds a list of all + * flowsets and scheduler instances. + * XXX sch must be at the beginning, see schk_hash(). + */ +struct dn_schk { + struct dn_sch sch; + struct dn_alg *fp; /* Pointer to scheduler functions */ + struct dn_link link; /* The link, embedded */ + struct dn_profile *profile; /* delay profile, if any */ + struct dn_id *cfg; /* extra config arguments */ + + SLIST_ENTRY(dn_schk) schk_next; /* hash chain for schedhash */ + + struct dn_fsk_head fsk_list; /* all fsk linked to me */ + struct dn_fsk *fs; /* Flowset for !MULTIQUEUE */ + + /* bucket index used by the drain routine to drain the scheduler + * instance for this flowset. + */ + int drain_bucket; + + /* Hash table of all instances (through sch.sched_mask) + * or single instance if no mask. Always valid. + */ + struct dn_ht *siht; +}; + + +/* + * Scheduler instance. + * Contains variables and all queues relative to a this instance. + * This struct is created a runtime. + */ +struct dn_sch_inst { + struct dn_flow ni; /* oid, flowid and stats */ + SLIST_ENTRY(dn_sch_inst) si_next; /* hash chain for siht */ + struct delay_line dline; + struct dn_schk *sched; /* the template */ + int kflags; /* DN_ACTIVE */ + + int64_t credit; /* bits I can transmit (more or less). */ + uint64_t sched_time; /* time link was scheduled in ready_heap */ + uint64_t idle_time; /* start of scheduler instance idle time */ + + /* q_count is the number of queues that this instance is using. + * The counter is incremented or decremented when + * a reference from the queue is created or deleted. + * It is used to make sure that a scheduler instance can be safely + * deleted by the drain routine. See notes below. + */ + int q_count; + +}; + +/* + * NOTE about object drain. + * The system will automatically (XXX check when) drain queues and + * scheduler instances when they are idle. + * A queue is idle when it has no packets; an instance is idle when + * it is not in the evheap heap, and the corresponding delay line is empty. + * A queue can be safely deleted when it is idle because of the scheduler + * function xxx_free_queue() will remove any references to it. + * An instance can be only deleted when no queues reference it. To be sure + * of that, a counter (q_count) stores the number of queues that are pointing + * to the instance. + * + * XXX + * Order of scan: + * - take all flowset in a bucket for the flowset hash table + * - take all queues in a bucket for the flowset + * - increment the queue bucket + * - scan next flowset bucket + * Nothing is done if a bucket contains no entries. + * + * The same schema is used for sceduler instances + */ + + +/* kernel-side flags. Linux has DN_DELETE in fcntl.h + */ +enum { + /* 1 and 2 are reserved for the SCAN flags */ + DN_DESTROY = 0x0004, /* destroy */ + DN_DELETE_FS = 0x0008, /* destroy flowset */ + DN_DETACH = 0x0010, + DN_ACTIVE = 0x0020, /* object is in evheap */ + DN_F_DLINE = 0x0040, /* object is a delay line */ + DN_DEL_SAFE = 0x0080, /* delete a queue only if no longer needed + * by scheduler */ + DN_QHT_IS_Q = 0x0100, /* in flowset, qht is a single queue */ +}; + +extern struct dn_parms dn_cfg; +//VNET_DECLARE(struct dn_parms, _base_dn_cfg); +//#define dn_cfg VNET(_base_dn_cfg) + +int dummynet_io(struct mbuf **, int , struct ip_fw_args *); +void dummynet_task(void *context, int pending); +void dn_reschedule(void); + +struct dn_queue *ipdn_q_find(struct dn_fsk *, struct dn_sch_inst *, + struct ipfw_flow_id *); +struct dn_sch_inst *ipdn_si_find(struct dn_schk *, struct ipfw_flow_id *); + +/* + * copy_range is a template for requests for ranges of pipes/queues/scheds. + * The number of ranges is variable and can be derived by o.len. + * As a default, we use a small number of entries so that the struct + * fits easily on the stack and is sufficient for most common requests. + */ +#define DEFAULT_RANGES 5 +struct copy_range { + struct dn_id o; + uint32_t r[ 2 * DEFAULT_RANGES ]; +}; + +struct copy_args { + char **start; + char *end; + int flags; + int type; + struct copy_range *extra; /* extra filtering */ +}; + +struct sockopt; +int ip_dummynet_compat(struct sockopt *sopt); +int dummynet_get(struct sockopt *sopt, void **compat); +int dn_c_copy_q (void *_ni, void *arg); +int dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq); +int dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq); +int dn_compat_copy_queue(struct copy_args *a, void *_o); +int dn_compat_copy_pipe(struct copy_args *a, void *_o); +int copy_data_helper_compat(void *_o, void *_arg); +int dn_compat_calc_size(void); +int do_config(void *p, int l); + +/* function to drain idle object */ +void dn_drain_scheduler(void); +void dn_drain_queue(void); + +#endif /* _IP_DN_PRIVATE_H */ diff --git a/sys/netpfil/ipfw/ip_dummynet.c b/sys/netpfil/ipfw/ip_dummynet.c new file mode 100644 index 0000000000000..509aeb7529669 --- /dev/null +++ b/sys/netpfil/ipfw/ip_dummynet.c @@ -0,0 +1,2315 @@ +/*- + * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa + * Portions Copyright (c) 2000 Akamba Corp. + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Configuration and internal object management for dummynet. + */ + +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ +#include +#include /* ip_output(), IP_FORWARDING */ +#include +#include + +#include +#include +#include +#include + +/* which objects to copy */ +#define DN_C_LINK 0x01 +#define DN_C_SCH 0x02 +#define DN_C_FLOW 0x04 +#define DN_C_FS 0x08 +#define DN_C_QUEUE 0x10 + +/* we use this argument in case of a schk_new */ +struct schk_new_arg { + struct dn_alg *fp; + struct dn_sch *sch; +}; + +/*---- callout hooks. ----*/ +static struct callout dn_timeout; +static struct task dn_task; +static struct taskqueue *dn_tq = NULL; + +static void +dummynet(void *arg) +{ + + (void)arg; /* UNUSED */ + taskqueue_enqueue(dn_tq, &dn_task); +} + +void +dn_reschedule(void) +{ + callout_reset(&dn_timeout, 1, dummynet, NULL); +} +/*----- end of callout hooks -----*/ + +/* Return a scheduler descriptor given the type or name. */ +static struct dn_alg * +find_sched_type(int type, char *name) +{ + struct dn_alg *d; + + SLIST_FOREACH(d, &dn_cfg.schedlist, next) { + if (d->type == type || (name && !strcasecmp(d->name, name))) + return d; + } + return NULL; /* not found */ +} + +int +ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg) +{ + int oldv = *v; + const char *op = NULL; + if (dflt < lo) + dflt = lo; + if (dflt > hi) + dflt = hi; + if (oldv < lo) { + *v = dflt; + op = "Bump"; + } else if (oldv > hi) { + *v = hi; + op = "Clamp"; + } else + return *v; + if (op && msg) + printf("%s %s to %d (was %d)\n", op, msg, *v, oldv); + return *v; +} + +/*---- flow_id mask, hash and compare functions ---*/ +/* + * The flow_id includes the 5-tuple, the queue/pipe number + * which we store in the extra area in host order, + * and for ipv6 also the flow_id6. + * XXX see if we want the tos byte (can store in 'flags') + */ +static struct ipfw_flow_id * +flow_id_mask(struct ipfw_flow_id *mask, struct ipfw_flow_id *id) +{ + int is_v6 = IS_IP6_FLOW_ID(id); + + id->dst_port &= mask->dst_port; + id->src_port &= mask->src_port; + id->proto &= mask->proto; + id->extra &= mask->extra; + if (is_v6) { + APPLY_MASK(&id->dst_ip6, &mask->dst_ip6); + APPLY_MASK(&id->src_ip6, &mask->src_ip6); + id->flow_id6 &= mask->flow_id6; + } else { + id->dst_ip &= mask->dst_ip; + id->src_ip &= mask->src_ip; + } + return id; +} + +/* computes an OR of two masks, result in dst and also returned */ +static struct ipfw_flow_id * +flow_id_or(struct ipfw_flow_id *src, struct ipfw_flow_id *dst) +{ + int is_v6 = IS_IP6_FLOW_ID(dst); + + dst->dst_port |= src->dst_port; + dst->src_port |= src->src_port; + dst->proto |= src->proto; + dst->extra |= src->extra; + if (is_v6) { +#define OR_MASK(_d, _s) \ + (_d)->__u6_addr.__u6_addr32[0] |= (_s)->__u6_addr.__u6_addr32[0]; \ + (_d)->__u6_addr.__u6_addr32[1] |= (_s)->__u6_addr.__u6_addr32[1]; \ + (_d)->__u6_addr.__u6_addr32[2] |= (_s)->__u6_addr.__u6_addr32[2]; \ + (_d)->__u6_addr.__u6_addr32[3] |= (_s)->__u6_addr.__u6_addr32[3]; + OR_MASK(&dst->dst_ip6, &src->dst_ip6); + OR_MASK(&dst->src_ip6, &src->src_ip6); +#undef OR_MASK + dst->flow_id6 |= src->flow_id6; + } else { + dst->dst_ip |= src->dst_ip; + dst->src_ip |= src->src_ip; + } + return dst; +} + +static int +nonzero_mask(struct ipfw_flow_id *m) +{ + if (m->dst_port || m->src_port || m->proto || m->extra) + return 1; + if (IS_IP6_FLOW_ID(m)) { + return + m->dst_ip6.__u6_addr.__u6_addr32[0] || + m->dst_ip6.__u6_addr.__u6_addr32[1] || + m->dst_ip6.__u6_addr.__u6_addr32[2] || + m->dst_ip6.__u6_addr.__u6_addr32[3] || + m->src_ip6.__u6_addr.__u6_addr32[0] || + m->src_ip6.__u6_addr.__u6_addr32[1] || + m->src_ip6.__u6_addr.__u6_addr32[2] || + m->src_ip6.__u6_addr.__u6_addr32[3] || + m->flow_id6; + } else { + return m->dst_ip || m->src_ip; + } +} + +/* XXX we may want a better hash function */ +static uint32_t +flow_id_hash(struct ipfw_flow_id *id) +{ + uint32_t i; + + if (IS_IP6_FLOW_ID(id)) { + uint32_t *d = (uint32_t *)&id->dst_ip6; + uint32_t *s = (uint32_t *)&id->src_ip6; + i = (d[0] ) ^ (d[1]) ^ + (d[2] ) ^ (d[3]) ^ + (d[0] >> 15) ^ (d[1] >> 15) ^ + (d[2] >> 15) ^ (d[3] >> 15) ^ + (s[0] << 1) ^ (s[1] << 1) ^ + (s[2] << 1) ^ (s[3] << 1) ^ + (s[0] << 16) ^ (s[1] << 16) ^ + (s[2] << 16) ^ (s[3] << 16) ^ + (id->dst_port << 1) ^ (id->src_port) ^ + (id->extra) ^ + (id->proto ) ^ (id->flow_id6); + } else { + i = (id->dst_ip) ^ (id->dst_ip >> 15) ^ + (id->src_ip << 1) ^ (id->src_ip >> 16) ^ + (id->extra) ^ + (id->dst_port << 1) ^ (id->src_port) ^ (id->proto); + } + return i; +} + +/* Like bcmp, returns 0 if ids match, 1 otherwise. */ +static int +flow_id_cmp(struct ipfw_flow_id *id1, struct ipfw_flow_id *id2) +{ + int is_v6 = IS_IP6_FLOW_ID(id1); + + if (!is_v6) { + if (IS_IP6_FLOW_ID(id2)) + return 1; /* different address families */ + + return (id1->dst_ip == id2->dst_ip && + id1->src_ip == id2->src_ip && + id1->dst_port == id2->dst_port && + id1->src_port == id2->src_port && + id1->proto == id2->proto && + id1->extra == id2->extra) ? 0 : 1; + } + /* the ipv6 case */ + return ( + !bcmp(&id1->dst_ip6,&id2->dst_ip6, sizeof(id1->dst_ip6)) && + !bcmp(&id1->src_ip6,&id2->src_ip6, sizeof(id1->src_ip6)) && + id1->dst_port == id2->dst_port && + id1->src_port == id2->src_port && + id1->proto == id2->proto && + id1->extra == id2->extra && + id1->flow_id6 == id2->flow_id6) ? 0 : 1; +} +/*--------- end of flow-id mask, hash and compare ---------*/ + +/*--- support functions for the qht hashtable ---- + * Entries are hashed by flow-id + */ +static uint32_t +q_hash(uintptr_t key, int flags, void *arg) +{ + /* compute the hash slot from the flow id */ + struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ? + &((struct dn_queue *)key)->ni.fid : + (struct ipfw_flow_id *)key; + + return flow_id_hash(id); +} + +static int +q_match(void *obj, uintptr_t key, int flags, void *arg) +{ + struct dn_queue *o = (struct dn_queue *)obj; + struct ipfw_flow_id *id2; + + if (flags & DNHT_KEY_IS_OBJ) { + /* compare pointers */ + id2 = &((struct dn_queue *)key)->ni.fid; + } else { + id2 = (struct ipfw_flow_id *)key; + } + return (0 == flow_id_cmp(&o->ni.fid, id2)); +} + +/* + * create a new queue instance for the given 'key'. + */ +static void * +q_new(uintptr_t key, int flags, void *arg) +{ + struct dn_queue *q, *template = arg; + struct dn_fsk *fs = template->fs; + int size = sizeof(*q) + fs->sched->fp->q_datalen; + + q = malloc(size, M_DUMMYNET, M_NOWAIT | M_ZERO); + if (q == NULL) { + D("no memory for new queue"); + return NULL; + } + + set_oid(&q->ni.oid, DN_QUEUE, size); + if (fs->fs.flags & DN_QHT_HASH) + q->ni.fid = *(struct ipfw_flow_id *)key; + q->fs = fs; + q->_si = template->_si; + q->_si->q_count++; + + if (fs->sched->fp->new_queue) + fs->sched->fp->new_queue(q); + dn_cfg.queue_count++; + return q; +} + +/* + * Notify schedulers that a queue is going away. + * If (flags & DN_DESTROY), also free the packets. + * The version for callbacks is called q_delete_cb(). + */ +static void +dn_delete_queue(struct dn_queue *q, int flags) +{ + struct dn_fsk *fs = q->fs; + + // D("fs %p si %p\n", fs, q->_si); + /* notify the parent scheduler that the queue is going away */ + if (fs && fs->sched->fp->free_queue) + fs->sched->fp->free_queue(q); + q->_si->q_count--; + q->_si = NULL; + if (flags & DN_DESTROY) { + if (q->mq.head) + dn_free_pkts(q->mq.head); + bzero(q, sizeof(*q)); // safety + free(q, M_DUMMYNET); + dn_cfg.queue_count--; + } +} + +static int +q_delete_cb(void *q, void *arg) +{ + int flags = (int)(uintptr_t)arg; + dn_delete_queue(q, flags); + return (flags & DN_DESTROY) ? DNHT_SCAN_DEL : 0; +} + +/* + * calls dn_delete_queue/q_delete_cb on all queues, + * which notifies the parent scheduler and possibly drains packets. + * flags & DN_DESTROY: drains queues and destroy qht; + */ +static void +qht_delete(struct dn_fsk *fs, int flags) +{ + ND("fs %d start flags %d qht %p", + fs->fs.fs_nr, flags, fs->qht); + if (!fs->qht) + return; + if (fs->fs.flags & DN_QHT_HASH) { + dn_ht_scan(fs->qht, q_delete_cb, (void *)(uintptr_t)flags); + if (flags & DN_DESTROY) { + dn_ht_free(fs->qht, 0); + fs->qht = NULL; + } + } else { + dn_delete_queue((struct dn_queue *)(fs->qht), flags); + if (flags & DN_DESTROY) + fs->qht = NULL; + } +} + +/* + * Find and possibly create the queue for a MULTIQUEUE scheduler. + * We never call it for !MULTIQUEUE (the queue is in the sch_inst). + */ +struct dn_queue * +ipdn_q_find(struct dn_fsk *fs, struct dn_sch_inst *si, + struct ipfw_flow_id *id) +{ + struct dn_queue template; + + template._si = si; + template.fs = fs; + + if (fs->fs.flags & DN_QHT_HASH) { + struct ipfw_flow_id masked_id; + if (fs->qht == NULL) { + fs->qht = dn_ht_init(NULL, fs->fs.buckets, + offsetof(struct dn_queue, q_next), + q_hash, q_match, q_new); + if (fs->qht == NULL) + return NULL; + } + masked_id = *id; + flow_id_mask(&fs->fsk_mask, &masked_id); + return dn_ht_find(fs->qht, (uintptr_t)&masked_id, + DNHT_INSERT, &template); + } else { + if (fs->qht == NULL) + fs->qht = q_new(0, 0, &template); + return (struct dn_queue *)fs->qht; + } +} +/*--- end of queue hash table ---*/ + +/*--- support functions for the sch_inst hashtable ---- + * + * These are hashed by flow-id + */ +static uint32_t +si_hash(uintptr_t key, int flags, void *arg) +{ + /* compute the hash slot from the flow id */ + struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ? + &((struct dn_sch_inst *)key)->ni.fid : + (struct ipfw_flow_id *)key; + + return flow_id_hash(id); +} + +static int +si_match(void *obj, uintptr_t key, int flags, void *arg) +{ + struct dn_sch_inst *o = obj; + struct ipfw_flow_id *id2; + + id2 = (flags & DNHT_KEY_IS_OBJ) ? + &((struct dn_sch_inst *)key)->ni.fid : + (struct ipfw_flow_id *)key; + return flow_id_cmp(&o->ni.fid, id2) == 0; +} + +/* + * create a new instance for the given 'key' + * Allocate memory for instance, delay line and scheduler private data. + */ +static void * +si_new(uintptr_t key, int flags, void *arg) +{ + struct dn_schk *s = arg; + struct dn_sch_inst *si; + int l = sizeof(*si) + s->fp->si_datalen; + + si = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO); + if (si == NULL) + goto error; + + /* Set length only for the part passed up to userland. */ + set_oid(&si->ni.oid, DN_SCH_I, sizeof(struct dn_flow)); + set_oid(&(si->dline.oid), DN_DELAY_LINE, + sizeof(struct delay_line)); + /* mark si and dline as outside the event queue */ + si->ni.oid.id = si->dline.oid.id = -1; + + si->sched = s; + si->dline.si = si; + + if (s->fp->new_sched && s->fp->new_sched(si)) { + D("new_sched error"); + goto error; + } + if (s->sch.flags & DN_HAVE_MASK) + si->ni.fid = *(struct ipfw_flow_id *)key; + + dn_cfg.si_count++; + return si; + +error: + if (si) { + bzero(si, sizeof(*si)); // safety + free(si, M_DUMMYNET); + } + return NULL; +} + +/* + * Callback from siht to delete all scheduler instances. Remove + * si and delay line from the system heap, destroy all queues. + * We assume that all flowset have been notified and do not + * point to us anymore. + */ +static int +si_destroy(void *_si, void *arg) +{ + struct dn_sch_inst *si = _si; + struct dn_schk *s = si->sched; + struct delay_line *dl = &si->dline; + + if (dl->oid.subtype) /* remove delay line from event heap */ + heap_extract(&dn_cfg.evheap, dl); + dn_free_pkts(dl->mq.head); /* drain delay line */ + if (si->kflags & DN_ACTIVE) /* remove si from event heap */ + heap_extract(&dn_cfg.evheap, si); + if (s->fp->free_sched) + s->fp->free_sched(si); + bzero(si, sizeof(*si)); /* safety */ + free(si, M_DUMMYNET); + dn_cfg.si_count--; + return DNHT_SCAN_DEL; +} + +/* + * Find the scheduler instance for this packet. If we need to apply + * a mask, do on a local copy of the flow_id to preserve the original. + * Assume siht is always initialized if we have a mask. + */ +struct dn_sch_inst * +ipdn_si_find(struct dn_schk *s, struct ipfw_flow_id *id) +{ + + if (s->sch.flags & DN_HAVE_MASK) { + struct ipfw_flow_id id_t = *id; + flow_id_mask(&s->sch.sched_mask, &id_t); + return dn_ht_find(s->siht, (uintptr_t)&id_t, + DNHT_INSERT, s); + } + if (!s->siht) + s->siht = si_new(0, 0, s); + return (struct dn_sch_inst *)s->siht; +} + +/* callback to flush credit for the scheduler instance */ +static int +si_reset_credit(void *_si, void *arg) +{ + struct dn_sch_inst *si = _si; + struct dn_link *p = &si->sched->link; + + si->credit = p->burst + (dn_cfg.io_fast ? p->bandwidth : 0); + return 0; +} + +static void +schk_reset_credit(struct dn_schk *s) +{ + if (s->sch.flags & DN_HAVE_MASK) + dn_ht_scan(s->siht, si_reset_credit, NULL); + else if (s->siht) + si_reset_credit(s->siht, NULL); +} +/*---- end of sch_inst hashtable ---------------------*/ + +/*------------------------------------------------------- + * flowset hash (fshash) support. Entries are hashed by fs_nr. + * New allocations are put in the fsunlinked list, from which + * they are removed when they point to a specific scheduler. + */ +static uint32_t +fsk_hash(uintptr_t key, int flags, void *arg) +{ + uint32_t i = !(flags & DNHT_KEY_IS_OBJ) ? key : + ((struct dn_fsk *)key)->fs.fs_nr; + + return ( (i>>8)^(i>>4)^i ); +} + +static int +fsk_match(void *obj, uintptr_t key, int flags, void *arg) +{ + struct dn_fsk *fs = obj; + int i = !(flags & DNHT_KEY_IS_OBJ) ? key : + ((struct dn_fsk *)key)->fs.fs_nr; + + return (fs->fs.fs_nr == i); +} + +static void * +fsk_new(uintptr_t key, int flags, void *arg) +{ + struct dn_fsk *fs; + + fs = malloc(sizeof(*fs), M_DUMMYNET, M_NOWAIT | M_ZERO); + if (fs) { + set_oid(&fs->fs.oid, DN_FS, sizeof(fs->fs)); + dn_cfg.fsk_count++; + fs->drain_bucket = 0; + SLIST_INSERT_HEAD(&dn_cfg.fsu, fs, sch_chain); + } + return fs; +} + +/* + * detach flowset from its current scheduler. Flags as follows: + * DN_DETACH removes from the fsk_list + * DN_DESTROY deletes individual queues + * DN_DELETE_FS destroys the flowset (otherwise goes in unlinked). + */ +static void +fsk_detach(struct dn_fsk *fs, int flags) +{ + if (flags & DN_DELETE_FS) + flags |= DN_DESTROY; + ND("fs %d from sched %d flags %s %s %s", + fs->fs.fs_nr, fs->fs.sched_nr, + (flags & DN_DELETE_FS) ? "DEL_FS":"", + (flags & DN_DESTROY) ? "DEL":"", + (flags & DN_DETACH) ? "DET":""); + if (flags & DN_DETACH) { /* detach from the list */ + struct dn_fsk_head *h; + h = fs->sched ? &fs->sched->fsk_list : &dn_cfg.fsu; + SLIST_REMOVE(h, fs, dn_fsk, sch_chain); + } + /* Free the RED parameters, they will be recomputed on + * subsequent attach if needed. + */ + if (fs->w_q_lookup) + free(fs->w_q_lookup, M_DUMMYNET); + fs->w_q_lookup = NULL; + qht_delete(fs, flags); + if (fs->sched && fs->sched->fp->free_fsk) + fs->sched->fp->free_fsk(fs); + fs->sched = NULL; + if (flags & DN_DELETE_FS) { + bzero(fs, sizeof(fs)); /* safety */ + free(fs, M_DUMMYNET); + dn_cfg.fsk_count--; + } else { + SLIST_INSERT_HEAD(&dn_cfg.fsu, fs, sch_chain); + } +} + +/* + * Detach or destroy all flowsets in a list. + * flags specifies what to do: + * DN_DESTROY: flush all queues + * DN_DELETE_FS: DN_DESTROY + destroy flowset + * DN_DELETE_FS implies DN_DESTROY + */ +static void +fsk_detach_list(struct dn_fsk_head *h, int flags) +{ + struct dn_fsk *fs; + int n = 0; /* only for stats */ + + ND("head %p flags %x", h, flags); + while ((fs = SLIST_FIRST(h))) { + SLIST_REMOVE_HEAD(h, sch_chain); + n++; + fsk_detach(fs, flags); + } + ND("done %d flowsets", n); +} + +/* + * called on 'queue X delete' -- removes the flowset from fshash, + * deletes all queues for the flowset, and removes the flowset. + */ +static int +delete_fs(int i, int locked) +{ + struct dn_fsk *fs; + int err = 0; + + if (!locked) + DN_BH_WLOCK(); + fs = dn_ht_find(dn_cfg.fshash, i, DNHT_REMOVE, NULL); + ND("fs %d found %p", i, fs); + if (fs) { + fsk_detach(fs, DN_DETACH | DN_DELETE_FS); + err = 0; + } else + err = EINVAL; + if (!locked) + DN_BH_WUNLOCK(); + return err; +} + +/*----- end of flowset hashtable support -------------*/ + +/*------------------------------------------------------------ + * Scheduler hash. When searching by index we pass sched_nr, + * otherwise we pass struct dn_sch * which is the first field in + * struct dn_schk so we can cast between the two. We use this trick + * because in the create phase (but it should be fixed). + */ +static uint32_t +schk_hash(uintptr_t key, int flags, void *_arg) +{ + uint32_t i = !(flags & DNHT_KEY_IS_OBJ) ? key : + ((struct dn_schk *)key)->sch.sched_nr; + return ( (i>>8)^(i>>4)^i ); +} + +static int +schk_match(void *obj, uintptr_t key, int flags, void *_arg) +{ + struct dn_schk *s = (struct dn_schk *)obj; + int i = !(flags & DNHT_KEY_IS_OBJ) ? key : + ((struct dn_schk *)key)->sch.sched_nr; + return (s->sch.sched_nr == i); +} + +/* + * Create the entry and intialize with the sched hash if needed. + * Leave s->fp unset so we can tell whether a dn_ht_find() returns + * a new object or a previously existing one. + */ +static void * +schk_new(uintptr_t key, int flags, void *arg) +{ + struct schk_new_arg *a = arg; + struct dn_schk *s; + int l = sizeof(*s) +a->fp->schk_datalen; + + s = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO); + if (s == NULL) + return NULL; + set_oid(&s->link.oid, DN_LINK, sizeof(s->link)); + s->sch = *a->sch; // copy initial values + s->link.link_nr = s->sch.sched_nr; + SLIST_INIT(&s->fsk_list); + /* initialize the hash table or create the single instance */ + s->fp = a->fp; /* si_new needs this */ + s->drain_bucket = 0; + if (s->sch.flags & DN_HAVE_MASK) { + s->siht = dn_ht_init(NULL, s->sch.buckets, + offsetof(struct dn_sch_inst, si_next), + si_hash, si_match, si_new); + if (s->siht == NULL) { + free(s, M_DUMMYNET); + return NULL; + } + } + s->fp = NULL; /* mark as a new scheduler */ + dn_cfg.schk_count++; + return s; +} + +/* + * Callback for sched delete. Notify all attached flowsets to + * detach from the scheduler, destroy the internal flowset, and + * all instances. The scheduler goes away too. + * arg is 0 (only detach flowsets and destroy instances) + * DN_DESTROY (detach & delete queues, delete schk) + * or DN_DELETE_FS (delete queues and flowsets, delete schk) + */ +static int +schk_delete_cb(void *obj, void *arg) +{ + struct dn_schk *s = obj; +#if 0 + int a = (int)arg; + ND("sched %d arg %s%s", + s->sch.sched_nr, + a&DN_DESTROY ? "DEL ":"", + a&DN_DELETE_FS ? "DEL_FS":""); +#endif + fsk_detach_list(&s->fsk_list, arg ? DN_DESTROY : 0); + /* no more flowset pointing to us now */ + if (s->sch.flags & DN_HAVE_MASK) { + dn_ht_scan(s->siht, si_destroy, NULL); + dn_ht_free(s->siht, 0); + } else if (s->siht) + si_destroy(s->siht, NULL); + if (s->profile) { + free(s->profile, M_DUMMYNET); + s->profile = NULL; + } + s->siht = NULL; + if (s->fp->destroy) + s->fp->destroy(s); + bzero(s, sizeof(*s)); // safety + free(obj, M_DUMMYNET); + dn_cfg.schk_count--; + return DNHT_SCAN_DEL; +} + +/* + * called on a 'sched X delete' command. Deletes a single scheduler. + * This is done by removing from the schedhash, unlinking all + * flowsets and deleting their traffic. + */ +static int +delete_schk(int i) +{ + struct dn_schk *s; + + s = dn_ht_find(dn_cfg.schedhash, i, DNHT_REMOVE, NULL); + ND("%d %p", i, s); + if (!s) + return EINVAL; + delete_fs(i + DN_MAX_ID, 1); /* first delete internal fs */ + /* then detach flowsets, delete traffic */ + schk_delete_cb(s, (void*)(uintptr_t)DN_DESTROY); + return 0; +} +/*--- end of schk hashtable support ---*/ + +static int +copy_obj(char **start, char *end, void *_o, const char *msg, int i) +{ + struct dn_id *o = _o; + int have = end - *start; + + if (have < o->len || o->len == 0 || o->type == 0) { + D("(WARN) type %d %s %d have %d need %d", + o->type, msg, i, have, o->len); + return 1; + } + ND("type %d %s %d len %d", o->type, msg, i, o->len); + bcopy(_o, *start, o->len); + if (o->type == DN_LINK) { + /* Adjust burst parameter for link */ + struct dn_link *l = (struct dn_link *)*start; + l->burst = div64(l->burst, 8 * hz); + l->delay = l->delay * 1000 / hz; + } else if (o->type == DN_SCH) { + /* Set id->id to the number of instances */ + struct dn_schk *s = _o; + struct dn_id *id = (struct dn_id *)(*start); + id->id = (s->sch.flags & DN_HAVE_MASK) ? + dn_ht_entries(s->siht) : (s->siht ? 1 : 0); + } + *start += o->len; + return 0; +} + +/* Specific function to copy a queue. + * Copies only the user-visible part of a queue (which is in + * a struct dn_flow), and sets len accordingly. + */ +static int +copy_obj_q(char **start, char *end, void *_o, const char *msg, int i) +{ + struct dn_id *o = _o; + int have = end - *start; + int len = sizeof(struct dn_flow); /* see above comment */ + + if (have < len || o->len == 0 || o->type != DN_QUEUE) { + D("ERROR type %d %s %d have %d need %d", + o->type, msg, i, have, len); + return 1; + } + ND("type %d %s %d len %d", o->type, msg, i, len); + bcopy(_o, *start, len); + ((struct dn_id*)(*start))->len = len; + *start += len; + return 0; +} + +static int +copy_q_cb(void *obj, void *arg) +{ + struct dn_queue *q = obj; + struct copy_args *a = arg; + struct dn_flow *ni = (struct dn_flow *)(*a->start); + if (copy_obj_q(a->start, a->end, &q->ni, "queue", -1)) + return DNHT_SCAN_END; + ni->oid.type = DN_FLOW; /* override the DN_QUEUE */ + ni->oid.id = si_hash((uintptr_t)&ni->fid, 0, NULL); + return 0; +} + +static int +copy_q(struct copy_args *a, struct dn_fsk *fs, int flags) +{ + if (!fs->qht) + return 0; + if (fs->fs.flags & DN_QHT_HASH) + dn_ht_scan(fs->qht, copy_q_cb, a); + else + copy_q_cb(fs->qht, a); + return 0; +} + +/* + * This routine only copies the initial part of a profile ? XXX + */ +static int +copy_profile(struct copy_args *a, struct dn_profile *p) +{ + int have = a->end - *a->start; + /* XXX here we check for max length */ + int profile_len = sizeof(struct dn_profile) - + ED_MAX_SAMPLES_NO*sizeof(int); + + if (p == NULL) + return 0; + if (have < profile_len) { + D("error have %d need %d", have, profile_len); + return 1; + } + bcopy(p, *a->start, profile_len); + ((struct dn_id *)(*a->start))->len = profile_len; + *a->start += profile_len; + return 0; +} + +static int +copy_flowset(struct copy_args *a, struct dn_fsk *fs, int flags) +{ + struct dn_fs *ufs = (struct dn_fs *)(*a->start); + if (!fs) + return 0; + ND("flowset %d", fs->fs.fs_nr); + if (copy_obj(a->start, a->end, &fs->fs, "flowset", fs->fs.fs_nr)) + return DNHT_SCAN_END; + ufs->oid.id = (fs->fs.flags & DN_QHT_HASH) ? + dn_ht_entries(fs->qht) : (fs->qht ? 1 : 0); + if (flags) { /* copy queues */ + copy_q(a, fs, 0); + } + return 0; +} + +static int +copy_si_cb(void *obj, void *arg) +{ + struct dn_sch_inst *si = obj; + struct copy_args *a = arg; + struct dn_flow *ni = (struct dn_flow *)(*a->start); + if (copy_obj(a->start, a->end, &si->ni, "inst", + si->sched->sch.sched_nr)) + return DNHT_SCAN_END; + ni->oid.type = DN_FLOW; /* override the DN_SCH_I */ + ni->oid.id = si_hash((uintptr_t)si, DNHT_KEY_IS_OBJ, NULL); + return 0; +} + +static int +copy_si(struct copy_args *a, struct dn_schk *s, int flags) +{ + if (s->sch.flags & DN_HAVE_MASK) + dn_ht_scan(s->siht, copy_si_cb, a); + else if (s->siht) + copy_si_cb(s->siht, a); + return 0; +} + +/* + * compute a list of children of a scheduler and copy up + */ +static int +copy_fsk_list(struct copy_args *a, struct dn_schk *s, int flags) +{ + struct dn_fsk *fs; + struct dn_id *o; + uint32_t *p; + + int n = 0, space = sizeof(*o); + SLIST_FOREACH(fs, &s->fsk_list, sch_chain) { + if (fs->fs.fs_nr < DN_MAX_ID) + n++; + } + space += n * sizeof(uint32_t); + DX(3, "sched %d has %d flowsets", s->sch.sched_nr, n); + if (a->end - *(a->start) < space) + return DNHT_SCAN_END; + o = (struct dn_id *)(*(a->start)); + o->len = space; + *a->start += o->len; + o->type = DN_TEXT; + p = (uint32_t *)(o+1); + SLIST_FOREACH(fs, &s->fsk_list, sch_chain) + if (fs->fs.fs_nr < DN_MAX_ID) + *p++ = fs->fs.fs_nr; + return 0; +} + +static int +copy_data_helper(void *_o, void *_arg) +{ + struct copy_args *a = _arg; + uint32_t *r = a->extra->r; /* start of first range */ + uint32_t *lim; /* first invalid pointer */ + int n; + + lim = (uint32_t *)((char *)(a->extra) + a->extra->o.len); + + if (a->type == DN_LINK || a->type == DN_SCH) { + /* pipe|sched show, we receive a dn_schk */ + struct dn_schk *s = _o; + + n = s->sch.sched_nr; + if (a->type == DN_SCH && n >= DN_MAX_ID) + return 0; /* not a scheduler */ + if (a->type == DN_LINK && n <= DN_MAX_ID) + return 0; /* not a pipe */ + + /* see if the object is within one of our ranges */ + for (;r < lim; r += 2) { + if (n < r[0] || n > r[1]) + continue; + /* Found a valid entry, copy and we are done */ + if (a->flags & DN_C_LINK) { + if (copy_obj(a->start, a->end, + &s->link, "link", n)) + return DNHT_SCAN_END; + if (copy_profile(a, s->profile)) + return DNHT_SCAN_END; + if (copy_flowset(a, s->fs, 0)) + return DNHT_SCAN_END; + } + if (a->flags & DN_C_SCH) { + if (copy_obj(a->start, a->end, + &s->sch, "sched", n)) + return DNHT_SCAN_END; + /* list all attached flowsets */ + if (copy_fsk_list(a, s, 0)) + return DNHT_SCAN_END; + } + if (a->flags & DN_C_FLOW) + copy_si(a, s, 0); + break; + } + } else if (a->type == DN_FS) { + /* queue show, skip internal flowsets */ + struct dn_fsk *fs = _o; + + n = fs->fs.fs_nr; + if (n >= DN_MAX_ID) + return 0; + /* see if the object is within one of our ranges */ + for (;r < lim; r += 2) { + if (n < r[0] || n > r[1]) + continue; + if (copy_flowset(a, fs, 0)) + return DNHT_SCAN_END; + copy_q(a, fs, 0); + break; /* we are done */ + } + } + return 0; +} + +static inline struct dn_schk * +locate_scheduler(int i) +{ + return dn_ht_find(dn_cfg.schedhash, i, 0, NULL); +} + +/* + * red parameters are in fixed point arithmetic. + */ +static int +config_red(struct dn_fsk *fs) +{ + int64_t s, idle, weight, w0; + int t, i; + + fs->w_q = fs->fs.w_q; + fs->max_p = fs->fs.max_p; + ND("called"); + /* Doing stuff that was in userland */ + i = fs->sched->link.bandwidth; + s = (i <= 0) ? 0 : + hz * dn_cfg.red_avg_pkt_size * 8 * SCALE(1) / i; + + idle = div64((s * 3) , fs->w_q); /* s, fs->w_q scaled; idle not scaled */ + fs->lookup_step = div64(idle , dn_cfg.red_lookup_depth); + /* fs->lookup_step not scaled, */ + if (!fs->lookup_step) + fs->lookup_step = 1; + w0 = weight = SCALE(1) - fs->w_q; //fs->w_q scaled + + for (t = fs->lookup_step; t > 1; --t) + weight = SCALE_MUL(weight, w0); + fs->lookup_weight = (int)(weight); // scaled + + /* Now doing stuff that was in kerneland */ + fs->min_th = SCALE(fs->fs.min_th); + fs->max_th = SCALE(fs->fs.max_th); + + fs->c_1 = fs->max_p / (fs->fs.max_th - fs->fs.min_th); + fs->c_2 = SCALE_MUL(fs->c_1, SCALE(fs->fs.min_th)); + + if (fs->fs.flags & DN_IS_GENTLE_RED) { + fs->c_3 = (SCALE(1) - fs->max_p) / fs->fs.max_th; + fs->c_4 = SCALE(1) - 2 * fs->max_p; + } + + /* If the lookup table already exist, free and create it again. */ + if (fs->w_q_lookup) { + free(fs->w_q_lookup, M_DUMMYNET); + fs->w_q_lookup = NULL; + } + if (dn_cfg.red_lookup_depth == 0) { + printf("\ndummynet: net.inet.ip.dummynet.red_lookup_depth" + "must be > 0\n"); + fs->fs.flags &= ~DN_IS_RED; + fs->fs.flags &= ~DN_IS_GENTLE_RED; + return (EINVAL); + } + fs->lookup_depth = dn_cfg.red_lookup_depth; + fs->w_q_lookup = (u_int *)malloc(fs->lookup_depth * sizeof(int), + M_DUMMYNET, M_NOWAIT); + if (fs->w_q_lookup == NULL) { + printf("dummynet: sorry, cannot allocate red lookup table\n"); + fs->fs.flags &= ~DN_IS_RED; + fs->fs.flags &= ~DN_IS_GENTLE_RED; + return(ENOSPC); + } + + /* Fill the lookup table with (1 - w_q)^x */ + fs->w_q_lookup[0] = SCALE(1) - fs->w_q; + + for (i = 1; i < fs->lookup_depth; i++) + fs->w_q_lookup[i] = + SCALE_MUL(fs->w_q_lookup[i - 1], fs->lookup_weight); + + if (dn_cfg.red_avg_pkt_size < 1) + dn_cfg.red_avg_pkt_size = 512; + fs->avg_pkt_size = dn_cfg.red_avg_pkt_size; + if (dn_cfg.red_max_pkt_size < 1) + dn_cfg.red_max_pkt_size = 1500; + fs->max_pkt_size = dn_cfg.red_max_pkt_size; + ND("exit"); + return 0; +} + +/* Scan all flowset attached to this scheduler and update red */ +static void +update_red(struct dn_schk *s) +{ + struct dn_fsk *fs; + SLIST_FOREACH(fs, &s->fsk_list, sch_chain) { + if (fs && (fs->fs.flags & DN_IS_RED)) + config_red(fs); + } +} + +/* attach flowset to scheduler s, possibly requeue */ +static void +fsk_attach(struct dn_fsk *fs, struct dn_schk *s) +{ + ND("remove fs %d from fsunlinked, link to sched %d", + fs->fs.fs_nr, s->sch.sched_nr); + SLIST_REMOVE(&dn_cfg.fsu, fs, dn_fsk, sch_chain); + fs->sched = s; + SLIST_INSERT_HEAD(&s->fsk_list, fs, sch_chain); + if (s->fp->new_fsk) + s->fp->new_fsk(fs); + /* XXX compute fsk_mask */ + fs->fsk_mask = fs->fs.flow_mask; + if (fs->sched->sch.flags & DN_HAVE_MASK) + flow_id_or(&fs->sched->sch.sched_mask, &fs->fsk_mask); + if (fs->qht) { + /* + * we must drain qht according to the old + * type, and reinsert according to the new one. + * The requeue is complex -- in general we need to + * reclassify every single packet. + * For the time being, let's hope qht is never set + * when we reach this point. + */ + D("XXX TODO requeue from fs %d to sch %d", + fs->fs.fs_nr, s->sch.sched_nr); + fs->qht = NULL; + } + /* set the new type for qht */ + if (nonzero_mask(&fs->fsk_mask)) + fs->fs.flags |= DN_QHT_HASH; + else + fs->fs.flags &= ~DN_QHT_HASH; + + /* XXX config_red() can fail... */ + if (fs->fs.flags & DN_IS_RED) + config_red(fs); +} + +/* update all flowsets which may refer to this scheduler */ +static void +update_fs(struct dn_schk *s) +{ + struct dn_fsk *fs, *tmp; + + SLIST_FOREACH_SAFE(fs, &dn_cfg.fsu, sch_chain, tmp) { + if (s->sch.sched_nr != fs->fs.sched_nr) { + D("fs %d for sch %d not %d still unlinked", + fs->fs.fs_nr, fs->fs.sched_nr, + s->sch.sched_nr); + continue; + } + fsk_attach(fs, s); + } +} + +/* + * Configuration -- to preserve backward compatibility we use + * the following scheme (N is 65536) + * NUMBER SCHED LINK FLOWSET + * 1 .. N-1 (1)WFQ (2)WFQ (3)queue + * N+1 .. 2N-1 (4)FIFO (5)FIFO (6)FIFO for sched 1..N-1 + * 2N+1 .. 3N-1 -- -- (7)FIFO for sched N+1..2N-1 + * + * "pipe i config" configures #1, #2 and #3 + * "sched i config" configures #1 and possibly #6 + * "queue i config" configures #3 + * #1 is configured with 'pipe i config' or 'sched i config' + * #2 is configured with 'pipe i config', and created if not + * existing with 'sched i config' + * #3 is configured with 'queue i config' + * #4 is automatically configured after #1, can only be FIFO + * #5 is automatically configured after #2 + * #6 is automatically created when #1 is !MULTIQUEUE, + * and can be updated. + * #7 is automatically configured after #2 + */ + +/* + * configure a link (and its FIFO instance) + */ +static int +config_link(struct dn_link *p, struct dn_id *arg) +{ + int i; + + if (p->oid.len != sizeof(*p)) { + D("invalid pipe len %d", p->oid.len); + return EINVAL; + } + i = p->link_nr; + if (i <= 0 || i >= DN_MAX_ID) + return EINVAL; + /* + * The config program passes parameters as follows: + * bw = bits/second (0 means no limits), + * delay = ms, must be translated into ticks. + * qsize = slots/bytes + * burst ??? + */ + p->delay = (p->delay * hz) / 1000; + /* Scale burst size: bytes -> bits * hz */ + p->burst *= 8 * hz; + + DN_BH_WLOCK(); + /* do it twice, base link and FIFO link */ + for (; i < 2*DN_MAX_ID; i += DN_MAX_ID) { + struct dn_schk *s = locate_scheduler(i); + if (s == NULL) { + DN_BH_WUNLOCK(); + D("sched %d not found", i); + return EINVAL; + } + /* remove profile if exists */ + if (s->profile) { + free(s->profile, M_DUMMYNET); + s->profile = NULL; + } + /* copy all parameters */ + s->link.oid = p->oid; + s->link.link_nr = i; + s->link.delay = p->delay; + if (s->link.bandwidth != p->bandwidth) { + /* XXX bandwidth changes, need to update red params */ + s->link.bandwidth = p->bandwidth; + update_red(s); + } + s->link.burst = p->burst; + schk_reset_credit(s); + } + dn_cfg.id++; + DN_BH_WUNLOCK(); + return 0; +} + +/* + * configure a flowset. Can be called from inside with locked=1, + */ +static struct dn_fsk * +config_fs(struct dn_fs *nfs, struct dn_id *arg, int locked) +{ + int i; + struct dn_fsk *fs; + + if (nfs->oid.len != sizeof(*nfs)) { + D("invalid flowset len %d", nfs->oid.len); + return NULL; + } + i = nfs->fs_nr; + if (i <= 0 || i >= 3*DN_MAX_ID) + return NULL; + ND("flowset %d", i); + /* XXX other sanity checks */ + if (nfs->flags & DN_QSIZE_BYTES) { + ipdn_bound_var(&nfs->qsize, 16384, + 1500, dn_cfg.byte_limit, NULL); // "queue byte size"); + } else { + ipdn_bound_var(&nfs->qsize, 50, + 1, dn_cfg.slot_limit, NULL); // "queue slot size"); + } + if (nfs->flags & DN_HAVE_MASK) { + /* make sure we have some buckets */ + ipdn_bound_var((int *)&nfs->buckets, dn_cfg.hash_size, + 1, dn_cfg.max_hash_size, "flowset buckets"); + } else { + nfs->buckets = 1; /* we only need 1 */ + } + if (!locked) + DN_BH_WLOCK(); + do { /* exit with break when done */ + struct dn_schk *s; + int flags = nfs->sched_nr ? DNHT_INSERT : 0; + int j; + int oldc = dn_cfg.fsk_count; + fs = dn_ht_find(dn_cfg.fshash, i, flags, NULL); + if (fs == NULL) { + D("missing sched for flowset %d", i); + break; + } + /* grab some defaults from the existing one */ + if (nfs->sched_nr == 0) /* reuse */ + nfs->sched_nr = fs->fs.sched_nr; + for (j = 0; j < sizeof(nfs->par)/sizeof(nfs->par[0]); j++) { + if (nfs->par[j] == -1) /* reuse */ + nfs->par[j] = fs->fs.par[j]; + } + if (bcmp(&fs->fs, nfs, sizeof(*nfs)) == 0) { + ND("flowset %d unchanged", i); + break; /* no change, nothing to do */ + } + if (oldc != dn_cfg.fsk_count) /* new item */ + dn_cfg.id++; + s = locate_scheduler(nfs->sched_nr); + /* detach from old scheduler if needed, preserving + * queues if we need to reattach. Then update the + * configuration, and possibly attach to the new sched. + */ + DX(2, "fs %d changed sched %d@%p to %d@%p", + fs->fs.fs_nr, + fs->fs.sched_nr, fs->sched, nfs->sched_nr, s); + if (fs->sched) { + int flags = s ? DN_DETACH : (DN_DETACH | DN_DESTROY); + flags |= DN_DESTROY; /* XXX temporary */ + fsk_detach(fs, flags); + } + fs->fs = *nfs; /* copy configuration */ + if (s != NULL) + fsk_attach(fs, s); + } while (0); + if (!locked) + DN_BH_WUNLOCK(); + return fs; +} + +/* + * config/reconfig a scheduler and its FIFO variant. + * For !MULTIQUEUE schedulers, also set up the flowset. + * + * On reconfigurations (detected because s->fp is set), + * detach existing flowsets preserving traffic, preserve link, + * and delete the old scheduler creating a new one. + */ +static int +config_sched(struct dn_sch *_nsch, struct dn_id *arg) +{ + struct dn_schk *s; + struct schk_new_arg a; /* argument for schk_new */ + int i; + struct dn_link p; /* copy of oldlink */ + struct dn_profile *pf = NULL; /* copy of old link profile */ + /* Used to preserv mask parameter */ + struct ipfw_flow_id new_mask; + int new_buckets = 0; + int new_flags = 0; + int pipe_cmd; + int err = ENOMEM; + + a.sch = _nsch; + if (a.sch->oid.len != sizeof(*a.sch)) { + D("bad sched len %d", a.sch->oid.len); + return EINVAL; + } + i = a.sch->sched_nr; + if (i <= 0 || i >= DN_MAX_ID) + return EINVAL; + /* make sure we have some buckets */ + if (a.sch->flags & DN_HAVE_MASK) + ipdn_bound_var((int *)&a.sch->buckets, dn_cfg.hash_size, + 1, dn_cfg.max_hash_size, "sched buckets"); + /* XXX other sanity checks */ + bzero(&p, sizeof(p)); + + pipe_cmd = a.sch->flags & DN_PIPE_CMD; + a.sch->flags &= ~DN_PIPE_CMD; //XXX do it even if is not set? + if (pipe_cmd) { + /* Copy mask parameter */ + new_mask = a.sch->sched_mask; + new_buckets = a.sch->buckets; + new_flags = a.sch->flags; + } + DN_BH_WLOCK(); +again: /* run twice, for wfq and fifo */ + /* + * lookup the type. If not supplied, use the previous one + * or default to WF2Q+. Otherwise, return an error. + */ + dn_cfg.id++; + a.fp = find_sched_type(a.sch->oid.subtype, a.sch->name); + if (a.fp != NULL) { + /* found. Lookup or create entry */ + s = dn_ht_find(dn_cfg.schedhash, i, DNHT_INSERT, &a); + } else if (a.sch->oid.subtype == 0 && !a.sch->name[0]) { + /* No type. search existing s* or retry with WF2Q+ */ + s = dn_ht_find(dn_cfg.schedhash, i, 0, &a); + if (s != NULL) { + a.fp = s->fp; + /* Scheduler exists, skip to FIFO scheduler + * if command was pipe config... + */ + if (pipe_cmd) + goto next; + } else { + /* New scheduler, create a wf2q+ with no mask + * if command was pipe config... + */ + if (pipe_cmd) { + /* clear mask parameter */ + bzero(&a.sch->sched_mask, sizeof(new_mask)); + a.sch->buckets = 0; + a.sch->flags &= ~DN_HAVE_MASK; + } + a.sch->oid.subtype = DN_SCHED_WF2QP; + goto again; + } + } else { + D("invalid scheduler type %d %s", + a.sch->oid.subtype, a.sch->name); + err = EINVAL; + goto error; + } + /* normalize name and subtype */ + a.sch->oid.subtype = a.fp->type; + bzero(a.sch->name, sizeof(a.sch->name)); + strlcpy(a.sch->name, a.fp->name, sizeof(a.sch->name)); + if (s == NULL) { + D("cannot allocate scheduler %d", i); + goto error; + } + /* restore existing link if any */ + if (p.link_nr) { + s->link = p; + if (!pf || pf->link_nr != p.link_nr) { /* no saved value */ + s->profile = NULL; /* XXX maybe not needed */ + } else { + s->profile = malloc(sizeof(struct dn_profile), + M_DUMMYNET, M_NOWAIT | M_ZERO); + if (s->profile == NULL) { + D("cannot allocate profile"); + goto error; //XXX + } + bcopy(pf, s->profile, sizeof(*pf)); + } + } + p.link_nr = 0; + if (s->fp == NULL) { + DX(2, "sched %d new type %s", i, a.fp->name); + } else if (s->fp != a.fp || + bcmp(a.sch, &s->sch, sizeof(*a.sch)) ) { + /* already existing. */ + DX(2, "sched %d type changed from %s to %s", + i, s->fp->name, a.fp->name); + DX(4, " type/sub %d/%d -> %d/%d", + s->sch.oid.type, s->sch.oid.subtype, + a.sch->oid.type, a.sch->oid.subtype); + if (s->link.link_nr == 0) + D("XXX WARNING link 0 for sched %d", i); + p = s->link; /* preserve link */ + if (s->profile) {/* preserve profile */ + if (!pf) + pf = malloc(sizeof(*pf), + M_DUMMYNET, M_NOWAIT | M_ZERO); + if (pf) /* XXX should issue a warning otherwise */ + bcopy(s->profile, pf, sizeof(*pf)); + } + /* remove from the hash */ + dn_ht_find(dn_cfg.schedhash, i, DNHT_REMOVE, NULL); + /* Detach flowsets, preserve queues. */ + // schk_delete_cb(s, NULL); + // XXX temporarily, kill queues + schk_delete_cb(s, (void *)DN_DESTROY); + goto again; + } else { + DX(4, "sched %d unchanged type %s", i, a.fp->name); + } + /* complete initialization */ + s->sch = *a.sch; + s->fp = a.fp; + s->cfg = arg; + // XXX schk_reset_credit(s); + /* create the internal flowset if needed, + * trying to reuse existing ones if available + */ + if (!(s->fp->flags & DN_MULTIQUEUE) && !s->fs) { + s->fs = dn_ht_find(dn_cfg.fshash, i, 0, NULL); + if (!s->fs) { + struct dn_fs fs; + bzero(&fs, sizeof(fs)); + set_oid(&fs.oid, DN_FS, sizeof(fs)); + fs.fs_nr = i + DN_MAX_ID; + fs.sched_nr = i; + s->fs = config_fs(&fs, NULL, 1 /* locked */); + } + if (!s->fs) { + schk_delete_cb(s, (void *)DN_DESTROY); + D("error creating internal fs for %d", i); + goto error; + } + } + /* call init function after the flowset is created */ + if (s->fp->config) + s->fp->config(s); + update_fs(s); +next: + if (i < DN_MAX_ID) { /* now configure the FIFO instance */ + i += DN_MAX_ID; + if (pipe_cmd) { + /* Restore mask parameter for FIFO */ + a.sch->sched_mask = new_mask; + a.sch->buckets = new_buckets; + a.sch->flags = new_flags; + } else { + /* sched config shouldn't modify the FIFO scheduler */ + if (dn_ht_find(dn_cfg.schedhash, i, 0, &a) != NULL) { + /* FIFO already exist, don't touch it */ + err = 0; /* and this is not an error */ + goto error; + } + } + a.sch->sched_nr = i; + a.sch->oid.subtype = DN_SCHED_FIFO; + bzero(a.sch->name, sizeof(a.sch->name)); + goto again; + } + err = 0; +error: + DN_BH_WUNLOCK(); + if (pf) + free(pf, M_DUMMYNET); + return err; +} + +/* + * attach a profile to a link + */ +static int +config_profile(struct dn_profile *pf, struct dn_id *arg) +{ + struct dn_schk *s; + int i, olen, err = 0; + + if (pf->oid.len < sizeof(*pf)) { + D("short profile len %d", pf->oid.len); + return EINVAL; + } + i = pf->link_nr; + if (i <= 0 || i >= DN_MAX_ID) + return EINVAL; + /* XXX other sanity checks */ + DN_BH_WLOCK(); + for (; i < 2*DN_MAX_ID; i += DN_MAX_ID) { + s = locate_scheduler(i); + + if (s == NULL) { + err = EINVAL; + break; + } + dn_cfg.id++; + /* + * If we had a profile and the new one does not fit, + * or it is deleted, then we need to free memory. + */ + if (s->profile && (pf->samples_no == 0 || + s->profile->oid.len < pf->oid.len)) { + free(s->profile, M_DUMMYNET); + s->profile = NULL; + } + if (pf->samples_no == 0) + continue; + /* + * new profile, possibly allocate memory + * and copy data. + */ + if (s->profile == NULL) + s->profile = malloc(pf->oid.len, + M_DUMMYNET, M_NOWAIT | M_ZERO); + if (s->profile == NULL) { + D("no memory for profile %d", i); + err = ENOMEM; + break; + } + /* preserve larger length XXX double check */ + olen = s->profile->oid.len; + if (olen < pf->oid.len) + olen = pf->oid.len; + bcopy(pf, s->profile, pf->oid.len); + s->profile->oid.len = olen; + } + DN_BH_WUNLOCK(); + return err; +} + +/* + * Delete all objects: + */ +static void +dummynet_flush(void) +{ + + /* delete all schedulers and related links/queues/flowsets */ + dn_ht_scan(dn_cfg.schedhash, schk_delete_cb, + (void *)(uintptr_t)DN_DELETE_FS); + /* delete all remaining (unlinked) flowsets */ + DX(4, "still %d unlinked fs", dn_cfg.fsk_count); + dn_ht_free(dn_cfg.fshash, DNHT_REMOVE); + fsk_detach_list(&dn_cfg.fsu, DN_DELETE_FS); + /* Reinitialize system heap... */ + heap_init(&dn_cfg.evheap, 16, offsetof(struct dn_id, id)); +} + +/* + * Main handler for configuration. We are guaranteed to be called + * with an oid which is at least a dn_id. + * - the first object is the command (config, delete, flush, ...) + * - config_link must be issued after the corresponding config_sched + * - parameters (DN_TXT) for an object must preceed the object + * processed on a config_sched. + */ +int +do_config(void *p, int l) +{ + struct dn_id *next, *o; + int err = 0, err2 = 0; + struct dn_id *arg = NULL; + uintptr_t *a; + + o = p; + if (o->id != DN_API_VERSION) { + D("invalid api version got %d need %d", + o->id, DN_API_VERSION); + return EINVAL; + } + for (; l >= sizeof(*o); o = next) { + struct dn_id *prev = arg; + if (o->len < sizeof(*o) || l < o->len) { + D("bad len o->len %d len %d", o->len, l); + err = EINVAL; + break; + } + l -= o->len; + next = (struct dn_id *)((char *)o + o->len); + err = 0; + switch (o->type) { + default: + D("cmd %d not implemented", o->type); + break; + +#ifdef EMULATE_SYSCTL + /* sysctl emulation. + * if we recognize the command, jump to the correct + * handler and return + */ + case DN_SYSCTL_SET: + err = kesysctl_emu_set(p, l); + return err; +#endif + + case DN_CMD_CONFIG: /* simply a header */ + break; + + case DN_CMD_DELETE: + /* the argument is in the first uintptr_t after o */ + a = (uintptr_t *)(o+1); + if (o->len < sizeof(*o) + sizeof(*a)) { + err = EINVAL; + break; + } + switch (o->subtype) { + case DN_LINK: + /* delete base and derived schedulers */ + DN_BH_WLOCK(); + err = delete_schk(*a); + err2 = delete_schk(*a + DN_MAX_ID); + DN_BH_WUNLOCK(); + if (!err) + err = err2; + break; + + default: + D("invalid delete type %d", + o->subtype); + err = EINVAL; + break; + + case DN_FS: + err = (*a <1 || *a >= DN_MAX_ID) ? + EINVAL : delete_fs(*a, 0) ; + break; + } + break; + + case DN_CMD_FLUSH: + DN_BH_WLOCK(); + dummynet_flush(); + DN_BH_WUNLOCK(); + break; + case DN_TEXT: /* store argument the next block */ + prev = NULL; + arg = o; + break; + case DN_LINK: + err = config_link((struct dn_link *)o, arg); + break; + case DN_PROFILE: + err = config_profile((struct dn_profile *)o, arg); + break; + case DN_SCH: + err = config_sched((struct dn_sch *)o, arg); + break; + case DN_FS: + err = (NULL==config_fs((struct dn_fs *)o, arg, 0)); + break; + } + if (prev) + arg = NULL; + if (err != 0) + break; + } + return err; +} + +static int +compute_space(struct dn_id *cmd, struct copy_args *a) +{ + int x = 0, need = 0; + int profile_size = sizeof(struct dn_profile) - + ED_MAX_SAMPLES_NO*sizeof(int); + + /* NOTE about compute space: + * NP = dn_cfg.schk_count + * NSI = dn_cfg.si_count + * NF = dn_cfg.fsk_count + * NQ = dn_cfg.queue_count + * - ipfw pipe show + * (NP/2)*(dn_link + dn_sch + dn_id + dn_fs) only half scheduler + * link, scheduler template, flowset + * integrated in scheduler and header + * for flowset list + * (NSI)*(dn_flow) all scheduler instance (includes + * the queue instance) + * - ipfw sched show + * (NP/2)*(dn_link + dn_sch + dn_id + dn_fs) only half scheduler + * link, scheduler template, flowset + * integrated in scheduler and header + * for flowset list + * (NSI * dn_flow) all scheduler instances + * (NF * sizeof(uint_32)) space for flowset list linked to scheduler + * (NQ * dn_queue) all queue [XXXfor now not listed] + * - ipfw queue show + * (NF * dn_fs) all flowset + * (NQ * dn_queue) all queues + */ + switch (cmd->subtype) { + default: + return -1; + /* XXX where do LINK and SCH differ ? */ + /* 'ipfw sched show' could list all queues associated to + * a scheduler. This feature for now is disabled + */ + case DN_LINK: /* pipe show */ + x = DN_C_LINK | DN_C_SCH | DN_C_FLOW; + need += dn_cfg.schk_count * + (sizeof(struct dn_fs) + profile_size) / 2; + need += dn_cfg.fsk_count * sizeof(uint32_t); + break; + case DN_SCH: /* sched show */ + need += dn_cfg.schk_count * + (sizeof(struct dn_fs) + profile_size) / 2; + need += dn_cfg.fsk_count * sizeof(uint32_t); + x = DN_C_SCH | DN_C_LINK | DN_C_FLOW; + break; + case DN_FS: /* queue show */ + x = DN_C_FS | DN_C_QUEUE; + break; + case DN_GET_COMPAT: /* compatibility mode */ + need = dn_compat_calc_size(); + break; + } + a->flags = x; + if (x & DN_C_SCH) { + need += dn_cfg.schk_count * sizeof(struct dn_sch) / 2; + /* NOT also, each fs might be attached to a sched */ + need += dn_cfg.schk_count * sizeof(struct dn_id) / 2; + } + if (x & DN_C_FS) + need += dn_cfg.fsk_count * sizeof(struct dn_fs); + if (x & DN_C_LINK) { + need += dn_cfg.schk_count * sizeof(struct dn_link) / 2; + } + /* + * When exporting a queue to userland, only pass up the + * struct dn_flow, which is the only visible part. + */ + + if (x & DN_C_QUEUE) + need += dn_cfg.queue_count * sizeof(struct dn_flow); + if (x & DN_C_FLOW) + need += dn_cfg.si_count * (sizeof(struct dn_flow)); + return need; +} + +/* + * If compat != NULL dummynet_get is called in compatibility mode. + * *compat will be the pointer to the buffer to pass to ipfw + */ +int +dummynet_get(struct sockopt *sopt, void **compat) +{ + int have, i, need, error; + char *start = NULL, *buf; + size_t sopt_valsize; + struct dn_id *cmd; + struct copy_args a; + struct copy_range r; + int l = sizeof(struct dn_id); + + bzero(&a, sizeof(a)); + bzero(&r, sizeof(r)); + + /* save and restore original sopt_valsize around copyin */ + sopt_valsize = sopt->sopt_valsize; + + cmd = &r.o; + + if (!compat) { + /* copy at least an oid, and possibly a full object */ + error = sooptcopyin(sopt, cmd, sizeof(r), sizeof(*cmd)); + sopt->sopt_valsize = sopt_valsize; + if (error) + goto done; + l = cmd->len; +#ifdef EMULATE_SYSCTL + /* sysctl emulation. */ + if (cmd->type == DN_SYSCTL_GET) + return kesysctl_emu_get(sopt); +#endif + if (l > sizeof(r)) { + /* request larger than default, allocate buffer */ + cmd = malloc(l, M_DUMMYNET, M_WAITOK); + error = sooptcopyin(sopt, cmd, l, l); + sopt->sopt_valsize = sopt_valsize; + if (error) + goto done; + } + } else { /* compatibility */ + error = 0; + cmd->type = DN_CMD_GET; + cmd->len = sizeof(struct dn_id); + cmd->subtype = DN_GET_COMPAT; + // cmd->id = sopt_valsize; + D("compatibility mode"); + } + a.extra = (struct copy_range *)cmd; + if (cmd->len == sizeof(*cmd)) { /* no range, create a default */ + uint32_t *rp = (uint32_t *)(cmd + 1); + cmd->len += 2* sizeof(uint32_t); + rp[0] = 1; + rp[1] = DN_MAX_ID - 1; + if (cmd->subtype == DN_LINK) { + rp[0] += DN_MAX_ID; + rp[1] += DN_MAX_ID; + } + } + /* Count space (under lock) and allocate (outside lock). + * Exit with lock held if we manage to get enough buffer. + * Try a few times then give up. + */ + for (have = 0, i = 0; i < 10; i++) { + DN_BH_WLOCK(); + need = compute_space(cmd, &a); + + /* if there is a range, ignore value from compute_space() */ + if (l > sizeof(*cmd)) + need = sopt_valsize - sizeof(*cmd); + + if (need < 0) { + DN_BH_WUNLOCK(); + error = EINVAL; + goto done; + } + need += sizeof(*cmd); + cmd->id = need; + if (have >= need) + break; + + DN_BH_WUNLOCK(); + if (start) + free(start, M_DUMMYNET); + start = NULL; + if (need > sopt_valsize) + break; + + have = need; + start = malloc(have, M_DUMMYNET, M_WAITOK | M_ZERO); + } + + if (start == NULL) { + if (compat) { + *compat = NULL; + error = 1; // XXX + } else { + error = sooptcopyout(sopt, cmd, sizeof(*cmd)); + } + goto done; + } + ND("have %d:%d sched %d, %d:%d links %d, %d:%d flowsets %d, " + "%d:%d si %d, %d:%d queues %d", + dn_cfg.schk_count, sizeof(struct dn_sch), DN_SCH, + dn_cfg.schk_count, sizeof(struct dn_link), DN_LINK, + dn_cfg.fsk_count, sizeof(struct dn_fs), DN_FS, + dn_cfg.si_count, sizeof(struct dn_flow), DN_SCH_I, + dn_cfg.queue_count, sizeof(struct dn_queue), DN_QUEUE); + sopt->sopt_valsize = sopt_valsize; + a.type = cmd->subtype; + + if (compat == NULL) { + bcopy(cmd, start, sizeof(*cmd)); + ((struct dn_id*)(start))->len = sizeof(struct dn_id); + buf = start + sizeof(*cmd); + } else + buf = start; + a.start = &buf; + a.end = start + have; + /* start copying other objects */ + if (compat) { + a.type = DN_COMPAT_PIPE; + dn_ht_scan(dn_cfg.schedhash, copy_data_helper_compat, &a); + a.type = DN_COMPAT_QUEUE; + dn_ht_scan(dn_cfg.fshash, copy_data_helper_compat, &a); + } else if (a.type == DN_FS) { + dn_ht_scan(dn_cfg.fshash, copy_data_helper, &a); + } else { + dn_ht_scan(dn_cfg.schedhash, copy_data_helper, &a); + } + DN_BH_WUNLOCK(); + + if (compat) { + *compat = start; + sopt->sopt_valsize = buf - start; + /* free() is done by ip_dummynet_compat() */ + start = NULL; //XXX hack + } else { + error = sooptcopyout(sopt, start, buf - start); + } +done: + if (cmd && cmd != &r.o) + free(cmd, M_DUMMYNET); + if (start) + free(start, M_DUMMYNET); + return error; +} + +/* Callback called on scheduler instance to delete it if idle */ +static int +drain_scheduler_cb(void *_si, void *arg) +{ + struct dn_sch_inst *si = _si; + + if ((si->kflags & DN_ACTIVE) || si->dline.mq.head != NULL) + return 0; + + if (si->sched->fp->flags & DN_MULTIQUEUE) { + if (si->q_count == 0) + return si_destroy(si, NULL); + else + return 0; + } else { /* !DN_MULTIQUEUE */ + if ((si+1)->ni.length == 0) + return si_destroy(si, NULL); + else + return 0; + } + return 0; /* unreachable */ +} + +/* Callback called on scheduler to check if it has instances */ +static int +drain_scheduler_sch_cb(void *_s, void *arg) +{ + struct dn_schk *s = _s; + + if (s->sch.flags & DN_HAVE_MASK) { + dn_ht_scan_bucket(s->siht, &s->drain_bucket, + drain_scheduler_cb, NULL); + s->drain_bucket++; + } else { + if (s->siht) { + if (drain_scheduler_cb(s->siht, NULL) == DNHT_SCAN_DEL) + s->siht = NULL; + } + } + return 0; +} + +/* Called every tick, try to delete a 'bucket' of scheduler */ +void +dn_drain_scheduler(void) +{ + dn_ht_scan_bucket(dn_cfg.schedhash, &dn_cfg.drain_sch, + drain_scheduler_sch_cb, NULL); + dn_cfg.drain_sch++; +} + +/* Callback called on queue to delete if it is idle */ +static int +drain_queue_cb(void *_q, void *arg) +{ + struct dn_queue *q = _q; + + if (q->ni.length == 0) { + dn_delete_queue(q, DN_DESTROY); + return DNHT_SCAN_DEL; /* queue is deleted */ + } + + return 0; /* queue isn't deleted */ +} + +/* Callback called on flowset used to check if it has queues */ +static int +drain_queue_fs_cb(void *_fs, void *arg) +{ + struct dn_fsk *fs = _fs; + + if (fs->fs.flags & DN_QHT_HASH) { + /* Flowset has a hash table for queues */ + dn_ht_scan_bucket(fs->qht, &fs->drain_bucket, + drain_queue_cb, NULL); + fs->drain_bucket++; + } else { + /* No hash table for this flowset, null the pointer + * if the queue is deleted + */ + if (fs->qht) { + if (drain_queue_cb(fs->qht, NULL) == DNHT_SCAN_DEL) + fs->qht = NULL; + } + } + return 0; +} + +/* Called every tick, try to delete a 'bucket' of queue */ +void +dn_drain_queue(void) +{ + /* scan a bucket of flowset */ + dn_ht_scan_bucket(dn_cfg.fshash, &dn_cfg.drain_fs, + drain_queue_fs_cb, NULL); + dn_cfg.drain_fs++; +} + +/* + * Handler for the various dummynet socket options + */ +static int +ip_dn_ctl(struct sockopt *sopt) +{ + void *p = NULL; + int error, l; + + error = priv_check(sopt->sopt_td, PRIV_NETINET_DUMMYNET); + if (error) + return (error); + + /* Disallow sets in really-really secure mode. */ + if (sopt->sopt_dir == SOPT_SET) { + error = securelevel_ge(sopt->sopt_td->td_ucred, 3); + if (error) + return (error); + } + + switch (sopt->sopt_name) { + default : + D("dummynet: unknown option %d", sopt->sopt_name); + error = EINVAL; + break; + + case IP_DUMMYNET_FLUSH: + case IP_DUMMYNET_CONFIGURE: + case IP_DUMMYNET_DEL: /* remove a pipe or queue */ + case IP_DUMMYNET_GET: + D("dummynet: compat option %d", sopt->sopt_name); + error = ip_dummynet_compat(sopt); + break; + + case IP_DUMMYNET3 : + if (sopt->sopt_dir == SOPT_GET) { + error = dummynet_get(sopt, NULL); + break; + } + l = sopt->sopt_valsize; + if (l < sizeof(struct dn_id) || l > 12000) { + D("argument len %d invalid", l); + break; + } + p = malloc(l, M_TEMP, M_WAITOK); // XXX can it fail ? + error = sooptcopyin(sopt, p, l, l); + if (error) + break ; + error = do_config(p, l); + break; + } + + if (p != NULL) + free(p, M_TEMP); + + return error ; +} + + +static void +ip_dn_init(void) +{ + if (dn_cfg.init_done) + return; + printf("DUMMYNET %p with IPv6 initialized (100409)\n", curvnet); + dn_cfg.init_done = 1; + /* Set defaults here. MSVC does not accept initializers, + * and this is also useful for vimages + */ + /* queue limits */ + dn_cfg.slot_limit = 100; /* Foot shooting limit for queues. */ + dn_cfg.byte_limit = 1024 * 1024; + dn_cfg.expire = 1; + + /* RED parameters */ + dn_cfg.red_lookup_depth = 256; /* default lookup table depth */ + dn_cfg.red_avg_pkt_size = 512; /* default medium packet size */ + dn_cfg.red_max_pkt_size = 1500; /* default max packet size */ + + /* hash tables */ + dn_cfg.max_hash_size = 65536; /* max in the hash tables */ + dn_cfg.hash_size = 64; /* default hash size */ + + /* create hash tables for schedulers and flowsets. + * In both we search by key and by pointer. + */ + dn_cfg.schedhash = dn_ht_init(NULL, dn_cfg.hash_size, + offsetof(struct dn_schk, schk_next), + schk_hash, schk_match, schk_new); + dn_cfg.fshash = dn_ht_init(NULL, dn_cfg.hash_size, + offsetof(struct dn_fsk, fsk_next), + fsk_hash, fsk_match, fsk_new); + + /* bucket index to drain object */ + dn_cfg.drain_fs = 0; + dn_cfg.drain_sch = 0; + + heap_init(&dn_cfg.evheap, 16, offsetof(struct dn_id, id)); + SLIST_INIT(&dn_cfg.fsu); + SLIST_INIT(&dn_cfg.schedlist); + + DN_LOCK_INIT(); + + TASK_INIT(&dn_task, 0, dummynet_task, curvnet); + dn_tq = taskqueue_create("dummynet", M_WAITOK, + taskqueue_thread_enqueue, &dn_tq); + taskqueue_start_threads(&dn_tq, 1, PI_NET, "dummynet"); + + callout_init(&dn_timeout, CALLOUT_MPSAFE); + callout_reset(&dn_timeout, 1, dummynet, NULL); + + /* Initialize curr_time adjustment mechanics. */ + getmicrouptime(&dn_cfg.prev_t); +} + +#ifdef KLD_MODULE +static void +ip_dn_destroy(int last) +{ + callout_drain(&dn_timeout); + + DN_BH_WLOCK(); + if (last) { + ND("removing last instance\n"); + ip_dn_ctl_ptr = NULL; + ip_dn_io_ptr = NULL; + } + + dummynet_flush(); + DN_BH_WUNLOCK(); + taskqueue_drain(dn_tq, &dn_task); + taskqueue_free(dn_tq); + + dn_ht_free(dn_cfg.schedhash, 0); + dn_ht_free(dn_cfg.fshash, 0); + heap_free(&dn_cfg.evheap); + + DN_LOCK_DESTROY(); +} +#endif /* KLD_MODULE */ + +static int +dummynet_modevent(module_t mod, int type, void *data) +{ + + if (type == MOD_LOAD) { + if (ip_dn_io_ptr) { + printf("DUMMYNET already loaded\n"); + return EEXIST ; + } + ip_dn_init(); + ip_dn_ctl_ptr = ip_dn_ctl; + ip_dn_io_ptr = dummynet_io; + return 0; + } else if (type == MOD_UNLOAD) { +#if !defined(KLD_MODULE) + printf("dummynet statically compiled, cannot unload\n"); + return EINVAL ; +#else + ip_dn_destroy(1 /* last */); + return 0; +#endif + } else + return EOPNOTSUPP; +} + +/* modevent helpers for the modules */ +static int +load_dn_sched(struct dn_alg *d) +{ + struct dn_alg *s; + + if (d == NULL) + return 1; /* error */ + ip_dn_init(); /* just in case, we need the lock */ + + /* Check that mandatory funcs exists */ + if (d->enqueue == NULL || d->dequeue == NULL) { + D("missing enqueue or dequeue for %s", d->name); + return 1; + } + + /* Search if scheduler already exists */ + DN_BH_WLOCK(); + SLIST_FOREACH(s, &dn_cfg.schedlist, next) { + if (strcmp(s->name, d->name) == 0) { + D("%s already loaded", d->name); + break; /* scheduler already exists */ + } + } + if (s == NULL) + SLIST_INSERT_HEAD(&dn_cfg.schedlist, d, next); + DN_BH_WUNLOCK(); + D("dn_sched %s %sloaded", d->name, s ? "not ":""); + return s ? 1 : 0; +} + +static int +unload_dn_sched(struct dn_alg *s) +{ + struct dn_alg *tmp, *r; + int err = EINVAL; + + ND("called for %s", s->name); + + DN_BH_WLOCK(); + SLIST_FOREACH_SAFE(r, &dn_cfg.schedlist, next, tmp) { + if (strcmp(s->name, r->name) != 0) + continue; + ND("ref_count = %d", r->ref_count); + err = (r->ref_count != 0) ? EBUSY : 0; + if (err == 0) + SLIST_REMOVE(&dn_cfg.schedlist, r, dn_alg, next); + break; + } + DN_BH_WUNLOCK(); + D("dn_sched %s %sunloaded", s->name, err ? "not ":""); + return err; +} + +int +dn_sched_modevent(module_t mod, int cmd, void *arg) +{ + struct dn_alg *sch = arg; + + if (cmd == MOD_LOAD) + return load_dn_sched(sch); + else if (cmd == MOD_UNLOAD) + return unload_dn_sched(sch); + else + return EINVAL; +} + +static moduledata_t dummynet_mod = { + "dummynet", dummynet_modevent, NULL +}; + +#define DN_SI_SUB SI_SUB_PROTO_IFATTACHDOMAIN +#define DN_MODEV_ORD (SI_ORDER_ANY - 128) /* after ipfw */ +DECLARE_MODULE(dummynet, dummynet_mod, DN_SI_SUB, DN_MODEV_ORD); +MODULE_DEPEND(dummynet, ipfw, 2, 2, 2); +MODULE_VERSION(dummynet, 3); + +/* + * Starting up. Done in order after dummynet_modevent() has been called. + * VNET_SYSINIT is also called for each existing vnet and each new vnet. + */ +//VNET_SYSINIT(vnet_dn_init, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_init, NULL); + +/* + * Shutdown handlers up shop. These are done in REVERSE ORDER, but still + * after dummynet_modevent() has been called. Not called on reboot. + * VNET_SYSUNINIT is also called for each exiting vnet as it exits. + * or when the module is unloaded. + */ +//VNET_SYSUNINIT(vnet_dn_uninit, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_destroy, NULL); + +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c new file mode 100644 index 0000000000000..e43a0ef24b852 --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -0,0 +1,2791 @@ +/*- + * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa + * + * 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 +__FBSDID("$FreeBSD$"); + +/* + * The FreeBSD IP packet firewall, main file + */ + +#include "opt_ipfw.h" +#include "opt_ipdivert.h" +#include "opt_inet.h" +#ifndef INET +#error "IPFIREWALL requires INET" +#endif /* INET */ +#include "opt_inet6.h" +#include "opt_ipsec.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for ETHERTYPE_IP */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef INET6 +#include +#include +#include +#endif + +#include + +#include /* XXX for in_cksum */ + +#ifdef MAC +#include +#endif + +/* + * static variables followed by global ones. + * All ipfw global variables are here. + */ + +/* ipfw_vnet_ready controls when we are open for business */ +static VNET_DEFINE(int, ipfw_vnet_ready) = 0; +#define V_ipfw_vnet_ready VNET(ipfw_vnet_ready) + +static VNET_DEFINE(int, fw_deny_unknown_exthdrs); +#define V_fw_deny_unknown_exthdrs VNET(fw_deny_unknown_exthdrs) + +static VNET_DEFINE(int, fw_permit_single_frag6) = 1; +#define V_fw_permit_single_frag6 VNET(fw_permit_single_frag6) + +#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT +static int default_to_accept = 1; +#else +static int default_to_accept; +#endif + +VNET_DEFINE(int, autoinc_step); +VNET_DEFINE(int, fw_one_pass) = 1; + +VNET_DEFINE(unsigned int, fw_tables_max); +/* Use 128 tables by default */ +static unsigned int default_fw_tables = IPFW_TABLES_DEFAULT; + +/* + * Each rule belongs to one of 32 different sets (0..31). + * The variable set_disable contains one bit per set. + * If the bit is set, all rules in the corresponding set + * are disabled. Set RESVD_SET(31) is reserved for the default rule + * and rules that are not deleted by the flush command, + * and CANNOT be disabled. + * Rules in set RESVD_SET can only be deleted individually. + */ +VNET_DEFINE(u_int32_t, set_disable); +#define V_set_disable VNET(set_disable) + +VNET_DEFINE(int, fw_verbose); +/* counter for ipfw_log(NULL...) */ +VNET_DEFINE(u_int64_t, norule_counter); +VNET_DEFINE(int, verbose_limit); + +/* layer3_chain contains the list of rules for layer 3 */ +VNET_DEFINE(struct ip_fw_chain, layer3_chain); + +ipfw_nat_t *ipfw_nat_ptr = NULL; +struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); +ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; +ipfw_nat_cfg_t *ipfw_nat_del_ptr; +ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; +ipfw_nat_cfg_t *ipfw_nat_get_log_ptr; + +#ifdef SYSCTL_NODE +uint32_t dummy_def = IPFW_DEFAULT_RULE; +static int sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS); + +SYSBEGIN(f3) + +SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); +SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, one_pass, + CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_one_pass), 0, + "Only do a single pass through ipfw when using dummynet(4)"); +SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, autoinc_step, + CTLFLAG_RW, &VNET_NAME(autoinc_step), 0, + "Rule number auto-increment step"); +SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, verbose, + CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_verbose), 0, + "Log matches to ipfw rules"); +SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, + CTLFLAG_RW, &VNET_NAME(verbose_limit), 0, + "Set upper limit of matches of ipfw rules logged"); +SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD, + &dummy_def, 0, + "The default/max possible rule number."); +SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max, + CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU", + "Maximum number of tables"); +SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN, + &default_to_accept, 0, + "Make the default rule accept all packets."); +TUNABLE_INT("net.inet.ip.fw.default_to_accept", &default_to_accept); +TUNABLE_INT("net.inet.ip.fw.tables_max", (int *)&default_fw_tables); +SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, static_count, + CTLFLAG_RD, &VNET_NAME(layer3_chain.n_rules), 0, + "Number of static rules"); + +#ifdef INET6 +SYSCTL_DECL(_net_inet6_ip6); +SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); +SYSCTL_VNET_INT(_net_inet6_ip6_fw, OID_AUTO, deny_unknown_exthdrs, + CTLFLAG_RW | CTLFLAG_SECURE, &VNET_NAME(fw_deny_unknown_exthdrs), 0, + "Deny packets with unknown IPv6 Extension Headers"); +SYSCTL_VNET_INT(_net_inet6_ip6_fw, OID_AUTO, permit_single_frag6, + CTLFLAG_RW | CTLFLAG_SECURE, &VNET_NAME(fw_permit_single_frag6), 0, + "Permit single packet IPv6 fragments"); +#endif /* INET6 */ + +SYSEND + +#endif /* SYSCTL_NODE */ + + +/* + * Some macros used in the various matching options. + * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T + * Other macros just cast void * into the appropriate type + */ +#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) +#define TCP(p) ((struct tcphdr *)(p)) +#define SCTP(p) ((struct sctphdr *)(p)) +#define UDP(p) ((struct udphdr *)(p)) +#define ICMP(p) ((struct icmphdr *)(p)) +#define ICMP6(p) ((struct icmp6_hdr *)(p)) + +static __inline int +icmptype_match(struct icmphdr *icmp, ipfw_insn_u32 *cmd) +{ + int type = icmp->icmp_type; + + return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<icmp_type; + + return (type <= ICMP_MAXTYPE && (TT & (1<arg1 or cmd->d[0]. + * + * We scan options and store the bits we find set. We succeed if + * + * (want_set & ~bits) == 0 && (want_clear & ~bits) == want_clear + * + * The code is sometimes optimized not to store additional variables. + */ + +static int +flags_match(ipfw_insn *cmd, u_int8_t bits) +{ + u_char want_clear; + bits = ~bits; + + if ( ((cmd->arg1 & 0xff) & bits) != 0) + return 0; /* some bits we want set were clear */ + want_clear = (cmd->arg1 >> 8) & 0xff; + if ( (want_clear & bits) != want_clear) + return 0; /* some bits we want clear were set */ + return 1; +} + +static int +ipopts_match(struct ip *ip, ipfw_insn *cmd) +{ + int optlen, bits = 0; + u_char *cp = (u_char *)(ip + 1); + int x = (ip->ip_hl << 2) - sizeof (struct ip); + + for (; x > 0; x -= optlen, cp += optlen) { + int opt = cp[IPOPT_OPTVAL]; + + if (opt == IPOPT_EOL) + break; + if (opt == IPOPT_NOP) + optlen = 1; + else { + optlen = cp[IPOPT_OLEN]; + if (optlen <= 0 || optlen > x) + return 0; /* invalid or truncated */ + } + switch (opt) { + + default: + break; + + case IPOPT_LSRR: + bits |= IP_FW_IPOPT_LSRR; + break; + + case IPOPT_SSRR: + bits |= IP_FW_IPOPT_SSRR; + break; + + case IPOPT_RR: + bits |= IP_FW_IPOPT_RR; + break; + + case IPOPT_TS: + bits |= IP_FW_IPOPT_TS; + break; + } + } + return (flags_match(cmd, bits)); +} + +static int +tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) +{ + int optlen, bits = 0; + u_char *cp = (u_char *)(tcp + 1); + int x = (tcp->th_off << 2) - sizeof(struct tcphdr); + + for (; x > 0; x -= optlen, cp += optlen) { + int opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + optlen = cp[1]; + if (optlen <= 0) + break; + } + + switch (opt) { + + default: + break; + + case TCPOPT_MAXSEG: + bits |= IP_FW_TCPOPT_MSS; + break; + + case TCPOPT_WINDOW: + bits |= IP_FW_TCPOPT_WINDOW; + break; + + case TCPOPT_SACK_PERMITTED: + case TCPOPT_SACK: + bits |= IP_FW_TCPOPT_SACK; + break; + + case TCPOPT_TIMESTAMP: + bits |= IP_FW_TCPOPT_TS; + break; + + } + } + return (flags_match(cmd, bits)); +} + +static int +iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uint32_t *tablearg) +{ + if (ifp == NULL) /* no iface with this packet, match fails */ + return 0; + /* Check by name or by IP address */ + if (cmd->name[0] != '\0') { /* match by name */ + if (cmd->name[0] == '\1') /* use tablearg to match */ + return ipfw_lookup_table_extended(chain, cmd->p.glob, + ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE); + /* Check name */ + if (cmd->p.glob) { + if (fnmatch(cmd->name, ifp->if_xname, 0) == 0) + return(1); + } else { + if (strncmp(ifp->if_xname, cmd->name, IFNAMSIZ) == 0) + return(1); + } + } else { +#ifdef __FreeBSD__ /* and OSX too ? */ + struct ifaddr *ia; + + if_addr_rlock(ifp); + TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) { + if (ia->ifa_addr->sa_family != AF_INET) + continue; + if (cmd->p.ip.s_addr == ((struct sockaddr_in *) + (ia->ifa_addr))->sin_addr.s_addr) { + if_addr_runlock(ifp); + return(1); /* match */ + } + } + if_addr_runlock(ifp); +#endif /* __FreeBSD__ */ + } + return(0); /* no match, fail ... */ +} + +/* + * The verify_path function checks if a route to the src exists and + * if it is reachable via ifp (when provided). + * + * The 'verrevpath' option checks that the interface that an IP packet + * arrives on is the same interface that traffic destined for the + * packet's source address would be routed out of. + * The 'versrcreach' option just checks that the source address is + * reachable via any route (except default) in the routing table. + * These two are a measure to block forged packets. This is also + * commonly known as "anti-spoofing" or Unicast Reverse Path + * Forwarding (Unicast RFP) in Cisco-ese. The name of the knobs + * is purposely reminiscent of the Cisco IOS command, + * + * ip verify unicast reverse-path + * ip verify unicast source reachable-via any + * + * which implements the same functionality. But note that the syntax + * is misleading, and the check may be performed on all IP packets + * whether unicast, multicast, or broadcast. + */ +static int +verify_path(struct in_addr src, struct ifnet *ifp, u_int fib) +{ +#ifndef __FreeBSD__ + return 0; +#else + struct route ro; + struct sockaddr_in *dst; + + bzero(&ro, sizeof(ro)); + + dst = (struct sockaddr_in *)&(ro.ro_dst); + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = src; + in_rtalloc_ign(&ro, 0, fib); + + if (ro.ro_rt == NULL) + return 0; + + /* + * If ifp is provided, check for equality with rtentry. + * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp, + * in order to pass packets injected back by if_simloop(): + * if useloopback == 1 routing entry (via lo0) for our own address + * may exist, so we need to handle routing assymetry. + */ + if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) { + RTFREE(ro.ro_rt); + return 0; + } + + /* if no ifp provided, check if rtentry is not default route */ + if (ifp == NULL && + satosin(rt_key(ro.ro_rt))->sin_addr.s_addr == INADDR_ANY) { + RTFREE(ro.ro_rt); + return 0; + } + + /* or if this is a blackhole/reject route */ + if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { + RTFREE(ro.ro_rt); + return 0; + } + + /* found valid route */ + RTFREE(ro.ro_rt); + return 1; +#endif /* __FreeBSD__ */ +} + +#ifdef INET6 +/* + * ipv6 specific rules here... + */ +static __inline int +icmp6type_match (int type, ipfw_insn_u32 *cmd) +{ + return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) ); +} + +static int +flow6id_match( int curr_flow, ipfw_insn_u32 *cmd ) +{ + int i; + for (i=0; i <= cmd->o.arg1; ++i ) + if (curr_flow == cmd->d[i] ) + return 1; + return 0; +} + +/* support for IP6_*_ME opcodes */ +static int +search_ip6_addr_net (struct in6_addr * ip6_addr) +{ + struct ifnet *mdc; + struct ifaddr *mdc2; + struct in6_ifaddr *fdm; + struct in6_addr copia; + + TAILQ_FOREACH(mdc, &V_ifnet, if_link) { + if_addr_rlock(mdc); + TAILQ_FOREACH(mdc2, &mdc->if_addrhead, ifa_link) { + if (mdc2->ifa_addr->sa_family == AF_INET6) { + fdm = (struct in6_ifaddr *)mdc2; + copia = fdm->ia_addr.sin6_addr; + /* need for leaving scope_id in the sock_addr */ + in6_clearscope(&copia); + if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia)) { + if_addr_runlock(mdc); + return 1; + } + } + } + if_addr_runlock(mdc); + } + return 0; +} + +static int +verify_path6(struct in6_addr *src, struct ifnet *ifp, u_int fib) +{ + struct route_in6 ro; + struct sockaddr_in6 *dst; + + bzero(&ro, sizeof(ro)); + + dst = (struct sockaddr_in6 * )&(ro.ro_dst); + dst->sin6_family = AF_INET6; + dst->sin6_len = sizeof(*dst); + dst->sin6_addr = *src; + + in6_rtalloc_ign(&ro, 0, fib); + if (ro.ro_rt == NULL) + return 0; + + /* + * if ifp is provided, check for equality with rtentry + * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp, + * to support the case of sending packets to an address of our own. + * (where the former interface is the first argument of if_simloop() + * (=ifp), the latter is lo0) + */ + if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) { + RTFREE(ro.ro_rt); + return 0; + } + + /* if no ifp provided, check if rtentry is not default route */ + if (ifp == NULL && + IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(ro.ro_rt))->sin6_addr)) { + RTFREE(ro.ro_rt); + return 0; + } + + /* or if this is a blackhole/reject route */ + if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { + RTFREE(ro.ro_rt); + return 0; + } + + /* found valid route */ + RTFREE(ro.ro_rt); + return 1; + +} + +static int +is_icmp6_query(int icmp6_type) +{ + if ((icmp6_type <= ICMP6_MAXTYPE) && + (icmp6_type == ICMP6_ECHO_REQUEST || + icmp6_type == ICMP6_MEMBERSHIP_QUERY || + icmp6_type == ICMP6_WRUREQUEST || + icmp6_type == ICMP6_FQDN_QUERY || + icmp6_type == ICMP6_NI_QUERY)) + return (1); + + return (0); +} + +static void +send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6) +{ + struct mbuf *m; + + m = args->m; + if (code == ICMP6_UNREACH_RST && args->f_id.proto == IPPROTO_TCP) { + struct tcphdr *tcp; + tcp = (struct tcphdr *)((char *)ip6 + hlen); + + if ((tcp->th_flags & TH_RST) == 0) { + struct mbuf *m0; + m0 = ipfw_send_pkt(args->m, &(args->f_id), + ntohl(tcp->th_seq), ntohl(tcp->th_ack), + tcp->th_flags | TH_RST); + if (m0 != NULL) + ip6_output(m0, NULL, NULL, 0, NULL, NULL, + NULL); + } + FREE_PKT(m); + } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ +#if 0 + /* + * Unlike above, the mbufs need to line up with the ip6 hdr, + * as the contents are read. We need to m_adj() the + * needed amount. + * The mbuf will however be thrown away so we can adjust it. + * Remember we did an m_pullup on it already so we + * can make some assumptions about contiguousness. + */ + if (args->L3offset) + m_adj(m, args->L3offset); +#endif + icmp6_error(m, ICMP6_DST_UNREACH, code, 0); + } else + FREE_PKT(m); + + args->m = NULL; +} + +#endif /* INET6 */ + + +/* + * sends a reject message, consuming the mbuf passed as an argument. + */ +static void +send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip) +{ + +#if 0 + /* XXX When ip is not guaranteed to be at mtod() we will + * need to account for this */ + * The mbuf will however be thrown away so we can adjust it. + * Remember we did an m_pullup on it already so we + * can make some assumptions about contiguousness. + */ + if (args->L3offset) + m_adj(m, args->L3offset); +#endif + if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */ + /* We need the IP header in host order for icmp_error(). */ + SET_HOST_IPLEN(ip); + icmp_error(args->m, ICMP_UNREACH, code, 0L, 0); + } else if (args->f_id.proto == IPPROTO_TCP) { + struct tcphdr *const tcp = + L3HDR(struct tcphdr, mtod(args->m, struct ip *)); + if ( (tcp->th_flags & TH_RST) == 0) { + struct mbuf *m; + m = ipfw_send_pkt(args->m, &(args->f_id), + ntohl(tcp->th_seq), ntohl(tcp->th_ack), + tcp->th_flags | TH_RST); + if (m != NULL) + ip_output(m, NULL, NULL, 0, NULL, NULL); + } + FREE_PKT(args->m); + } else + FREE_PKT(args->m); + args->m = NULL; +} + +/* + * Support for uid/gid/jail lookup. These tests are expensive + * (because we may need to look into the list of active sockets) + * so we cache the results. ugid_lookupp is 0 if we have not + * yet done a lookup, 1 if we succeeded, and -1 if we tried + * and failed. The function always returns the match value. + * We could actually spare the variable and use *uc, setting + * it to '(void *)check_uidgid if we have no info, NULL if + * we tried and failed, or any other value if successful. + */ +static int +check_uidgid(ipfw_insn_u32 *insn, struct ip_fw_args *args, int *ugid_lookupp, + struct ucred **uc) +{ +#ifndef __FreeBSD__ + /* XXX */ + return cred_check(insn, proto, oif, + dst_ip, dst_port, src_ip, src_port, + (struct bsd_ucred *)uc, ugid_lookupp, ((struct mbuf *)inp)->m_skb); +#else /* FreeBSD */ + struct in_addr src_ip, dst_ip; + struct inpcbinfo *pi; + struct ipfw_flow_id *id; + struct inpcb *pcb, *inp; + struct ifnet *oif; + int lookupflags; + int match; + + id = &args->f_id; + inp = args->inp; + oif = args->oif; + + /* + * Check to see if the UDP or TCP stack supplied us with + * the PCB. If so, rather then holding a lock and looking + * up the PCB, we can use the one that was supplied. + */ + if (inp && *ugid_lookupp == 0) { + INP_LOCK_ASSERT(inp); + if (inp->inp_socket != NULL) { + *uc = crhold(inp->inp_cred); + *ugid_lookupp = 1; + } else + *ugid_lookupp = -1; + } + /* + * If we have already been here and the packet has no + * PCB entry associated with it, then we can safely + * assume that this is a no match. + */ + if (*ugid_lookupp == -1) + return (0); + if (id->proto == IPPROTO_TCP) { + lookupflags = 0; + pi = &V_tcbinfo; + } else if (id->proto == IPPROTO_UDP) { + lookupflags = INPLOOKUP_WILDCARD; + pi = &V_udbinfo; + } else + return 0; + lookupflags |= INPLOOKUP_RLOCKPCB; + match = 0; + if (*ugid_lookupp == 0) { + if (id->addr_type == 6) { +#ifdef INET6 + if (oif == NULL) + pcb = in6_pcblookup_mbuf(pi, + &id->src_ip6, htons(id->src_port), + &id->dst_ip6, htons(id->dst_port), + lookupflags, oif, args->m); + else + pcb = in6_pcblookup_mbuf(pi, + &id->dst_ip6, htons(id->dst_port), + &id->src_ip6, htons(id->src_port), + lookupflags, oif, args->m); +#else + *ugid_lookupp = -1; + return (0); +#endif + } else { + src_ip.s_addr = htonl(id->src_ip); + dst_ip.s_addr = htonl(id->dst_ip); + if (oif == NULL) + pcb = in_pcblookup_mbuf(pi, + src_ip, htons(id->src_port), + dst_ip, htons(id->dst_port), + lookupflags, oif, args->m); + else + pcb = in_pcblookup_mbuf(pi, + dst_ip, htons(id->dst_port), + src_ip, htons(id->src_port), + lookupflags, oif, args->m); + } + if (pcb != NULL) { + INP_RLOCK_ASSERT(pcb); + *uc = crhold(pcb->inp_cred); + *ugid_lookupp = 1; + INP_RUNLOCK(pcb); + } + if (*ugid_lookupp == 0) { + /* + * We tried and failed, set the variable to -1 + * so we will not try again on this packet. + */ + *ugid_lookupp = -1; + return (0); + } + } + if (insn->o.opcode == O_UID) + match = ((*uc)->cr_uid == (uid_t)insn->d[0]); + else if (insn->o.opcode == O_GID) + match = groupmember((gid_t)insn->d[0], *uc); + else if (insn->o.opcode == O_JAIL) + match = ((*uc)->cr_prison->pr_id == (int)insn->d[0]); + return (match); +#endif /* __FreeBSD__ */ +} + +/* + * Helper function to set args with info on the rule after the matching + * one. slot is precise, whereas we guess rule_id as they are + * assigned sequentially. + */ +static inline void +set_match(struct ip_fw_args *args, int slot, + struct ip_fw_chain *chain) +{ + args->rule.chain_id = chain->id; + args->rule.slot = slot + 1; /* we use 0 as a marker */ + args->rule.rule_id = 1 + chain->map[slot]->id; + args->rule.rulenum = chain->map[slot]->rulenum; +} + +/* + * The main check routine for the firewall. + * + * All arguments are in args so we can modify them and return them + * back to the caller. + * + * Parameters: + * + * args->m (in/out) The packet; we set to NULL when/if we nuke it. + * Starts with the IP header. + * args->eh (in) Mac header if present, NULL for layer3 packet. + * args->L3offset Number of bytes bypassed if we came from L2. + * e.g. often sizeof(eh) ** NOTYET ** + * args->oif Outgoing interface, NULL if packet is incoming. + * The incoming interface is in the mbuf. (in) + * args->divert_rule (in/out) + * Skip up to the first rule past this rule number; + * upon return, non-zero port number for divert or tee. + * + * args->rule Pointer to the last matching rule (in/out) + * args->next_hop Socket we are forwarding to (out). + * args->next_hop6 IPv6 next hop we are forwarding to (out). + * args->f_id Addresses grabbed from the packet (out) + * args->rule.info a cookie depending on rule action + * + * Return value: + * + * IP_FW_PASS the packet must be accepted + * IP_FW_DENY the packet must be dropped + * IP_FW_DIVERT divert packet, port in m_tag + * IP_FW_TEE tee packet, port in m_tag + * IP_FW_DUMMYNET to dummynet, pipe in args->cookie + * IP_FW_NETGRAPH into netgraph, cookie args->cookie + * args->rule contains the matching rule, + * args->rule.info has additional information. + * + */ +int +ipfw_chk(struct ip_fw_args *args) +{ + + /* + * Local variables holding state while processing a packet: + * + * IMPORTANT NOTE: to speed up the processing of rules, there + * are some assumption on the values of the variables, which + * are documented here. Should you change them, please check + * the implementation of the various instructions to make sure + * that they still work. + * + * args->eh The MAC header. It is non-null for a layer2 + * packet, it is NULL for a layer-3 packet. + * **notyet** + * args->L3offset Offset in the packet to the L3 (IP or equiv.) header. + * + * m | args->m Pointer to the mbuf, as received from the caller. + * It may change if ipfw_chk() does an m_pullup, or if it + * consumes the packet because it calls send_reject(). + * XXX This has to change, so that ipfw_chk() never modifies + * or consumes the buffer. + * ip is the beginning of the ip(4 or 6) header. + * Calculated by adding the L3offset to the start of data. + * (Until we start using L3offset, the packet is + * supposed to start with the ip header). + */ + struct mbuf *m = args->m; + struct ip *ip = mtod(m, struct ip *); + + /* + * For rules which contain uid/gid or jail constraints, cache + * a copy of the users credentials after the pcb lookup has been + * executed. This will speed up the processing of rules with + * these types of constraints, as well as decrease contention + * on pcb related locks. + */ +#ifndef __FreeBSD__ + struct bsd_ucred ucred_cache; +#else + struct ucred *ucred_cache = NULL; +#endif + int ucred_lookup = 0; + + /* + * oif | args->oif If NULL, ipfw_chk has been called on the + * inbound path (ether_input, ip_input). + * If non-NULL, ipfw_chk has been called on the outbound path + * (ether_output, ip_output). + */ + struct ifnet *oif = args->oif; + + int f_pos = 0; /* index of current rule in the array */ + int retval = 0; + + /* + * hlen The length of the IP header. + */ + u_int hlen = 0; /* hlen >0 means we have an IP pkt */ + + /* + * offset The offset of a fragment. offset != 0 means that + * we have a fragment at this offset of an IPv4 packet. + * offset == 0 means that (if this is an IPv4 packet) + * this is the first or only fragment. + * For IPv6 offset|ip6f_mf == 0 means there is no Fragment Header + * or there is a single packet fragement (fragement header added + * without needed). We will treat a single packet fragment as if + * there was no fragment header (or log/block depending on the + * V_fw_permit_single_frag6 sysctl setting). + */ + u_short offset = 0; + u_short ip6f_mf = 0; + + /* + * Local copies of addresses. They are only valid if we have + * an IP packet. + * + * proto The protocol. Set to 0 for non-ip packets, + * or to the protocol read from the packet otherwise. + * proto != 0 means that we have an IPv4 packet. + * + * src_port, dst_port port numbers, in HOST format. Only + * valid for TCP and UDP packets. + * + * src_ip, dst_ip ip addresses, in NETWORK format. + * Only valid for IPv4 packets. + */ + uint8_t proto; + uint16_t src_port = 0, dst_port = 0; /* NOTE: host format */ + struct in_addr src_ip, dst_ip; /* NOTE: network format */ + uint16_t iplen=0; + int pktlen; + uint16_t etype = 0; /* Host order stored ether type */ + + /* + * dyn_dir = MATCH_UNKNOWN when rules unchecked, + * MATCH_NONE when checked and not matched (q = NULL), + * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) + */ + int dyn_dir = MATCH_UNKNOWN; + ipfw_dyn_rule *q = NULL; + struct ip_fw_chain *chain = &V_layer3_chain; + + /* + * We store in ulp a pointer to the upper layer protocol header. + * In the ipv4 case this is easy to determine from the header, + * but for ipv6 we might have some additional headers in the middle. + * ulp is NULL if not found. + */ + void *ulp = NULL; /* upper layer protocol pointer. */ + + /* XXX ipv6 variables */ + int is_ipv6 = 0; + uint8_t icmp6_type = 0; + uint16_t ext_hd = 0; /* bits vector for extension header filtering */ + /* end of ipv6 variables */ + + int is_ipv4 = 0; + + int done = 0; /* flag to exit the outer loop */ + + if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready)) + return (IP_FW_PASS); /* accept */ + + dst_ip.s_addr = 0; /* make sure it is initialized */ + src_ip.s_addr = 0; /* make sure it is initialized */ + pktlen = m->m_pkthdr.len; + args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */ + proto = args->f_id.proto = 0; /* mark f_id invalid */ + /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */ + +/* + * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, + * then it sets p to point at the offset "len" in the mbuf. WARNING: the + * pointer might become stale after other pullups (but we never use it + * this way). + */ +#define PULLUP_TO(_len, p, T) PULLUP_LEN(_len, p, sizeof(T)) +#define PULLUP_LEN(_len, p, T) \ +do { \ + int x = (_len) + T; \ + if ((m)->m_len < x) { \ + args->m = m = m_pullup(m, x); \ + if (m == NULL) \ + goto pullup_failed; \ + } \ + p = (mtod(m, char *) + (_len)); \ +} while (0) + + /* + * if we have an ether header, + */ + if (args->eh) + etype = ntohs(args->eh->ether_type); + + /* Identify IP packets and fill up variables. */ + if (pktlen >= sizeof(struct ip6_hdr) && + (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) { + struct ip6_hdr *ip6 = (struct ip6_hdr *)ip; + is_ipv6 = 1; + args->f_id.addr_type = 6; + hlen = sizeof(struct ip6_hdr); + proto = ip6->ip6_nxt; + + /* Search extension headers to find upper layer protocols */ + while (ulp == NULL && offset == 0) { + switch (proto) { + case IPPROTO_ICMPV6: + PULLUP_TO(hlen, ulp, struct icmp6_hdr); + icmp6_type = ICMP6(ulp)->icmp6_type; + break; + + case IPPROTO_TCP: + PULLUP_TO(hlen, ulp, struct tcphdr); + dst_port = TCP(ulp)->th_dport; + src_port = TCP(ulp)->th_sport; + /* save flags for dynamic rules */ + args->f_id._flags = TCP(ulp)->th_flags; + break; + + case IPPROTO_SCTP: + PULLUP_TO(hlen, ulp, struct sctphdr); + src_port = SCTP(ulp)->src_port; + dst_port = SCTP(ulp)->dest_port; + break; + + case IPPROTO_UDP: + PULLUP_TO(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; + break; + + case IPPROTO_HOPOPTS: /* RFC 2460 */ + PULLUP_TO(hlen, ulp, struct ip6_hbh); + ext_hd |= EXT_HOPOPTS; + hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; + proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; + ulp = NULL; + break; + + case IPPROTO_ROUTING: /* RFC 2460 */ + PULLUP_TO(hlen, ulp, struct ip6_rthdr); + switch (((struct ip6_rthdr *)ulp)->ip6r_type) { + case 0: + ext_hd |= EXT_RTHDR0; + break; + case 2: + ext_hd |= EXT_RTHDR2; + break; + default: + if (V_fw_verbose) + printf("IPFW2: IPV6 - Unknown " + "Routing Header type(%d)\n", + ((struct ip6_rthdr *) + ulp)->ip6r_type); + if (V_fw_deny_unknown_exthdrs) + return (IP_FW_DENY); + break; + } + ext_hd |= EXT_ROUTING; + hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; + proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; + ulp = NULL; + break; + + case IPPROTO_FRAGMENT: /* RFC 2460 */ + PULLUP_TO(hlen, ulp, struct ip6_frag); + ext_hd |= EXT_FRAGMENT; + hlen += sizeof (struct ip6_frag); + proto = ((struct ip6_frag *)ulp)->ip6f_nxt; + offset = ((struct ip6_frag *)ulp)->ip6f_offlg & + IP6F_OFF_MASK; + ip6f_mf = ((struct ip6_frag *)ulp)->ip6f_offlg & + IP6F_MORE_FRAG; + if (V_fw_permit_single_frag6 == 0 && + offset == 0 && ip6f_mf == 0) { + if (V_fw_verbose) + printf("IPFW2: IPV6 - Invalid " + "Fragment Header\n"); + if (V_fw_deny_unknown_exthdrs) + return (IP_FW_DENY); + break; + } + args->f_id.extra = + ntohl(((struct ip6_frag *)ulp)->ip6f_ident); + ulp = NULL; + break; + + case IPPROTO_DSTOPTS: /* RFC 2460 */ + PULLUP_TO(hlen, ulp, struct ip6_hbh); + ext_hd |= EXT_DSTOPTS; + hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; + proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; + ulp = NULL; + break; + + case IPPROTO_AH: /* RFC 2402 */ + PULLUP_TO(hlen, ulp, struct ip6_ext); + ext_hd |= EXT_AH; + hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2; + proto = ((struct ip6_ext *)ulp)->ip6e_nxt; + ulp = NULL; + break; + + case IPPROTO_ESP: /* RFC 2406 */ + PULLUP_TO(hlen, ulp, uint32_t); /* SPI, Seq# */ + /* Anything past Seq# is variable length and + * data past this ext. header is encrypted. */ + ext_hd |= EXT_ESP; + break; + + case IPPROTO_NONE: /* RFC 2460 */ + /* + * Packet ends here, and IPv6 header has + * already been pulled up. If ip6e_len!=0 + * then octets must be ignored. + */ + ulp = ip; /* non-NULL to get out of loop. */ + break; + + case IPPROTO_OSPFIGP: + /* XXX OSPF header check? */ + PULLUP_TO(hlen, ulp, struct ip6_ext); + break; + + case IPPROTO_PIM: + /* XXX PIM header check? */ + PULLUP_TO(hlen, ulp, struct pim); + break; + + case IPPROTO_CARP: + PULLUP_TO(hlen, ulp, struct carp_header); + if (((struct carp_header *)ulp)->carp_version != + CARP_VERSION) + return (IP_FW_DENY); + if (((struct carp_header *)ulp)->carp_type != + CARP_ADVERTISEMENT) + return (IP_FW_DENY); + break; + + case IPPROTO_IPV6: /* RFC 2893 */ + PULLUP_TO(hlen, ulp, struct ip6_hdr); + break; + + case IPPROTO_IPV4: /* RFC 2893 */ + PULLUP_TO(hlen, ulp, struct ip); + break; + + default: + if (V_fw_verbose) + printf("IPFW2: IPV6 - Unknown " + "Extension Header(%d), ext_hd=%x\n", + proto, ext_hd); + if (V_fw_deny_unknown_exthdrs) + return (IP_FW_DENY); + PULLUP_TO(hlen, ulp, struct ip6_ext); + break; + } /*switch */ + } + ip = mtod(m, struct ip *); + ip6 = (struct ip6_hdr *)ip; + args->f_id.src_ip6 = ip6->ip6_src; + args->f_id.dst_ip6 = ip6->ip6_dst; + args->f_id.src_ip = 0; + args->f_id.dst_ip = 0; + args->f_id.flow_id6 = ntohl(ip6->ip6_flow); + } else if (pktlen >= sizeof(struct ip) && + (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) { + is_ipv4 = 1; + hlen = ip->ip_hl << 2; + args->f_id.addr_type = 4; + + /* + * Collect parameters into local variables for faster matching. + */ + proto = ip->ip_p; + src_ip = ip->ip_src; + dst_ip = ip->ip_dst; + offset = ntohs(ip->ip_off) & IP_OFFMASK; + iplen = ntohs(ip->ip_len); + pktlen = iplen < pktlen ? iplen : pktlen; + + if (offset == 0) { + switch (proto) { + case IPPROTO_TCP: + PULLUP_TO(hlen, ulp, struct tcphdr); + dst_port = TCP(ulp)->th_dport; + src_port = TCP(ulp)->th_sport; + /* save flags for dynamic rules */ + args->f_id._flags = TCP(ulp)->th_flags; + break; + + case IPPROTO_SCTP: + PULLUP_TO(hlen, ulp, struct sctphdr); + src_port = SCTP(ulp)->src_port; + dst_port = SCTP(ulp)->dest_port; + break; + + case IPPROTO_UDP: + PULLUP_TO(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; + break; + + case IPPROTO_ICMP: + PULLUP_TO(hlen, ulp, struct icmphdr); + //args->f_id.flags = ICMP(ulp)->icmp_type; + break; + + default: + break; + } + } + + ip = mtod(m, struct ip *); + args->f_id.src_ip = ntohl(src_ip.s_addr); + args->f_id.dst_ip = ntohl(dst_ip.s_addr); + } +#undef PULLUP_TO + if (proto) { /* we may have port numbers, store them */ + args->f_id.proto = proto; + args->f_id.src_port = src_port = ntohs(src_port); + args->f_id.dst_port = dst_port = ntohs(dst_port); + } + + IPFW_RLOCK(chain); + if (! V_ipfw_vnet_ready) { /* shutting down, leave NOW. */ + IPFW_RUNLOCK(chain); + return (IP_FW_PASS); /* accept */ + } + if (args->rule.slot) { + /* + * Packet has already been tagged as a result of a previous + * match on rule args->rule aka args->rule_id (PIPE, QUEUE, + * REASS, NETGRAPH, DIVERT/TEE...) + * Validate the slot and continue from the next one + * if still present, otherwise do a lookup. + */ + f_pos = (args->rule.chain_id == chain->id) ? + args->rule.slot : + ipfw_find_rule(chain, args->rule.rulenum, + args->rule.rule_id); + } else { + f_pos = 0; + } + + /* + * Now scan the rules, and parse microinstructions for each rule. + * We have two nested loops and an inner switch. Sometimes we + * need to break out of one or both loops, or re-enter one of + * the loops with updated variables. Loop variables are: + * + * f_pos (outer loop) points to the current rule. + * On output it points to the matching rule. + * done (outer loop) is used as a flag to break the loop. + * l (inner loop) residual length of current rule. + * cmd points to the current microinstruction. + * + * We break the inner loop by setting l=0 and possibly + * cmdlen=0 if we don't want to advance cmd. + * We break the outer loop by setting done=1 + * We can restart the inner loop by setting l>0 and f_pos, f, cmd + * as needed. + */ + for (; f_pos < chain->n_rules; f_pos++) { + ipfw_insn *cmd; + uint32_t tablearg = 0; + int l, cmdlen, skip_or; /* skip rest of OR block */ + struct ip_fw *f; + + f = chain->map[f_pos]; + if (V_set_disable & (1 << f->set) ) + continue; + + skip_or = 0; + for (l = f->cmd_len, cmd = f->cmd ; l > 0 ; + l -= cmdlen, cmd += cmdlen) { + int match; + + /* + * check_body is a jump target used when we find a + * CHECK_STATE, and need to jump to the body of + * the target rule. + */ + +/* check_body: */ + cmdlen = F_LEN(cmd); + /* + * An OR block (insn_1 || .. || insn_n) has the + * F_OR bit set in all but the last instruction. + * The first match will set "skip_or", and cause + * the following instructions to be skipped until + * past the one with the F_OR bit clear. + */ + if (skip_or) { /* skip this instruction */ + if ((cmd->len & F_OR) == 0) + skip_or = 0; /* next one is good */ + continue; + } + match = 0; /* set to 1 if we succeed */ + + switch (cmd->opcode) { + /* + * The first set of opcodes compares the packet's + * fields with some pattern, setting 'match' if a + * match is found. At the end of the loop there is + * logic to deal with F_NOT and F_OR flags associated + * with the opcode. + */ + case O_NOP: + match = 1; + break; + + case O_FORWARD_MAC: + printf("ipfw: opcode %d unimplemented\n", + cmd->opcode); + break; + + case O_GID: + case O_UID: + case O_JAIL: + /* + * We only check offset == 0 && proto != 0, + * as this ensures that we have a + * packet with the ports info. + */ + if (offset != 0) + break; + if (proto == IPPROTO_TCP || + proto == IPPROTO_UDP) + match = check_uidgid( + (ipfw_insn_u32 *)cmd, + args, &ucred_lookup, +#ifdef __FreeBSD__ + &ucred_cache); +#else + (void *)&ucred_cache); +#endif + break; + + case O_RECV: + match = iface_match(m->m_pkthdr.rcvif, + (ipfw_insn_if *)cmd, chain, &tablearg); + break; + + case O_XMIT: + match = iface_match(oif, (ipfw_insn_if *)cmd, + chain, &tablearg); + break; + + case O_VIA: + match = iface_match(oif ? oif : + m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd, + chain, &tablearg); + break; + + case O_MACADDR2: + if (args->eh != NULL) { /* have MAC header */ + u_int32_t *want = (u_int32_t *) + ((ipfw_insn_mac *)cmd)->addr; + u_int32_t *mask = (u_int32_t *) + ((ipfw_insn_mac *)cmd)->mask; + u_int32_t *hdr = (u_int32_t *)args->eh; + + match = + ( want[0] == (hdr[0] & mask[0]) && + want[1] == (hdr[1] & mask[1]) && + want[2] == (hdr[2] & mask[2]) ); + } + break; + + case O_MAC_TYPE: + if (args->eh != NULL) { + u_int16_t *p = + ((ipfw_insn_u16 *)cmd)->ports; + int i; + + for (i = cmdlen - 1; !match && i>0; + i--, p += 2) + match = (etype >= p[0] && + etype <= p[1]); + } + break; + + case O_FRAG: + match = (offset != 0); + break; + + case O_IN: /* "out" is "not in" */ + match = (oif == NULL); + break; + + case O_LAYER2: + match = (args->eh != NULL); + break; + + case O_DIVERTED: + { + /* For diverted packets, args->rule.info + * contains the divert port (in host format) + * reason and direction. + */ + uint32_t i = args->rule.info; + match = (i&IPFW_IS_MASK) == IPFW_IS_DIVERT && + cmd->arg1 & ((i & IPFW_INFO_IN) ? 1 : 2); + } + break; + + case O_PROTO: + /* + * We do not allow an arg of 0 so the + * check of "proto" only suffices. + */ + match = (proto == cmd->arg1); + break; + + case O_IP_SRC: + match = is_ipv4 && + (((ipfw_insn_ip *)cmd)->addr.s_addr == + src_ip.s_addr); + break; + + case O_IP_SRC_LOOKUP: + case O_IP_DST_LOOKUP: + if (is_ipv4) { + uint32_t key = + (cmd->opcode == O_IP_DST_LOOKUP) ? + dst_ip.s_addr : src_ip.s_addr; + uint32_t v = 0; + + if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { + /* generic lookup. The key must be + * in 32bit big-endian format. + */ + v = ((ipfw_insn_u32 *)cmd)->d[1]; + if (v == 0) + key = dst_ip.s_addr; + else if (v == 1) + key = src_ip.s_addr; + else if (v == 6) /* dscp */ + key = (ip->ip_tos >> 2) & 0x3f; + else if (offset != 0) + break; + else if (proto != IPPROTO_TCP && + proto != IPPROTO_UDP) + break; + else if (v == 2) + key = htonl(dst_port); + else if (v == 3) + key = htonl(src_port); + else if (v == 4 || v == 5) { + check_uidgid( + (ipfw_insn_u32 *)cmd, + args, &ucred_lookup, +#ifdef __FreeBSD__ + &ucred_cache); + if (v == 4 /* O_UID */) + key = ucred_cache->cr_uid; + else if (v == 5 /* O_JAIL */) + key = ucred_cache->cr_prison->pr_id; +#else /* !__FreeBSD__ */ + (void *)&ucred_cache); + if (v ==4 /* O_UID */) + key = ucred_cache.uid; + else if (v == 5 /* O_JAIL */) + key = ucred_cache.xid; +#endif /* !__FreeBSD__ */ + key = htonl(key); + } else + break; + } + match = ipfw_lookup_table(chain, + cmd->arg1, key, &v); + if (!match) + break; + if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) + match = + ((ipfw_insn_u32 *)cmd)->d[0] == v; + else + tablearg = v; + } else if (is_ipv6) { + uint32_t v = 0; + void *pkey = (cmd->opcode == O_IP_DST_LOOKUP) ? + &args->f_id.dst_ip6: &args->f_id.src_ip6; + match = ipfw_lookup_table_extended(chain, + cmd->arg1, pkey, &v, + IPFW_TABLE_CIDR); + if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) + match = ((ipfw_insn_u32 *)cmd)->d[0] == v; + if (match) + tablearg = v; + } + break; + + case O_IP_SRC_MASK: + case O_IP_DST_MASK: + if (is_ipv4) { + uint32_t a = + (cmd->opcode == O_IP_DST_MASK) ? + dst_ip.s_addr : src_ip.s_addr; + uint32_t *p = ((ipfw_insn_u32 *)cmd)->d; + int i = cmdlen-1; + + for (; !match && i>0; i-= 2, p+= 2) + match = (p[0] == (a & p[1])); + } + break; + + case O_IP_SRC_ME: + if (is_ipv4) { + struct ifnet *tif; + + INADDR_TO_IFP(src_ip, tif); + match = (tif != NULL); + break; + } +#ifdef INET6 + /* FALLTHROUGH */ + case O_IP6_SRC_ME: + match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6); +#endif + break; + + case O_IP_DST_SET: + case O_IP_SRC_SET: + if (is_ipv4) { + u_int32_t *d = (u_int32_t *)(cmd+1); + u_int32_t addr = + cmd->opcode == O_IP_DST_SET ? + args->f_id.dst_ip : + args->f_id.src_ip; + + if (addr < d[0]) + break; + addr -= d[0]; /* subtract base */ + match = (addr < cmd->arg1) && + ( d[ 1 + (addr>>5)] & + (1<<(addr & 0x1f)) ); + } + break; + + case O_IP_DST: + match = is_ipv4 && + (((ipfw_insn_ip *)cmd)->addr.s_addr == + dst_ip.s_addr); + break; + + case O_IP_DST_ME: + if (is_ipv4) { + struct ifnet *tif; + + INADDR_TO_IFP(dst_ip, tif); + match = (tif != NULL); + break; + } +#ifdef INET6 + /* FALLTHROUGH */ + case O_IP6_DST_ME: + match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6); +#endif + break; + + + case O_IP_SRCPORT: + case O_IP_DSTPORT: + /* + * offset == 0 && proto != 0 is enough + * to guarantee that we have a + * packet with port info. + */ + if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP) + && offset == 0) { + u_int16_t x = + (cmd->opcode == O_IP_SRCPORT) ? + src_port : dst_port ; + u_int16_t *p = + ((ipfw_insn_u16 *)cmd)->ports; + int i; + + for (i = cmdlen - 1; !match && i>0; + i--, p += 2) + match = (x>=p[0] && x<=p[1]); + } + break; + + case O_ICMPTYPE: + match = (offset == 0 && proto==IPPROTO_ICMP && + icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); + break; + +#ifdef INET6 + case O_ICMP6TYPE: + match = is_ipv6 && offset == 0 && + proto==IPPROTO_ICMPV6 && + icmp6type_match( + ICMP6(ulp)->icmp6_type, + (ipfw_insn_u32 *)cmd); + break; +#endif /* INET6 */ + + case O_IPOPT: + match = (is_ipv4 && + ipopts_match(ip, cmd) ); + break; + + case O_IPVER: + match = (is_ipv4 && + cmd->arg1 == ip->ip_v); + break; + + case O_IPID: + case O_IPLEN: + case O_IPTTL: + if (is_ipv4) { /* only for IP packets */ + uint16_t x; + uint16_t *p; + int i; + + if (cmd->opcode == O_IPLEN) + x = iplen; + else if (cmd->opcode == O_IPTTL) + x = ip->ip_ttl; + else /* must be IPID */ + x = ntohs(ip->ip_id); + if (cmdlen == 1) { + match = (cmd->arg1 == x); + break; + } + /* otherwise we have ranges */ + p = ((ipfw_insn_u16 *)cmd)->ports; + i = cmdlen - 1; + for (; !match && i>0; i--, p += 2) + match = (x >= p[0] && x <= p[1]); + } + break; + + case O_IPPRECEDENCE: + match = (is_ipv4 && + (cmd->arg1 == (ip->ip_tos & 0xe0)) ); + break; + + case O_IPTOS: + match = (is_ipv4 && + flags_match(cmd, ip->ip_tos)); + break; + + case O_TCPDATALEN: + if (proto == IPPROTO_TCP && offset == 0) { + struct tcphdr *tcp; + uint16_t x; + uint16_t *p; + int i; + + tcp = TCP(ulp); + x = iplen - + ((ip->ip_hl + tcp->th_off) << 2); + if (cmdlen == 1) { + match = (cmd->arg1 == x); + break; + } + /* otherwise we have ranges */ + p = ((ipfw_insn_u16 *)cmd)->ports; + i = cmdlen - 1; + for (; !match && i>0; i--, p += 2) + match = (x >= p[0] && x <= p[1]); + } + break; + + case O_TCPFLAGS: + match = (proto == IPPROTO_TCP && offset == 0 && + flags_match(cmd, TCP(ulp)->th_flags)); + break; + + case O_TCPOPTS: + PULLUP_LEN(hlen, ulp, (TCP(ulp)->th_off << 2)); + match = (proto == IPPROTO_TCP && offset == 0 && + tcpopts_match(TCP(ulp), cmd)); + break; + + case O_TCPSEQ: + match = (proto == IPPROTO_TCP && offset == 0 && + ((ipfw_insn_u32 *)cmd)->d[0] == + TCP(ulp)->th_seq); + break; + + case O_TCPACK: + match = (proto == IPPROTO_TCP && offset == 0 && + ((ipfw_insn_u32 *)cmd)->d[0] == + TCP(ulp)->th_ack); + break; + + case O_TCPWIN: + if (proto == IPPROTO_TCP && offset == 0) { + uint16_t x; + uint16_t *p; + int i; + + x = ntohs(TCP(ulp)->th_win); + if (cmdlen == 1) { + match = (cmd->arg1 == x); + break; + } + /* Otherwise we have ranges. */ + p = ((ipfw_insn_u16 *)cmd)->ports; + i = cmdlen - 1; + for (; !match && i > 0; i--, p += 2) + match = (x >= p[0] && x <= p[1]); + } + break; + + case O_ESTAB: + /* reject packets which have SYN only */ + /* XXX should i also check for TH_ACK ? */ + match = (proto == IPPROTO_TCP && offset == 0 && + (TCP(ulp)->th_flags & + (TH_RST | TH_ACK | TH_SYN)) != TH_SYN); + break; + + case O_ALTQ: { + struct pf_mtag *at; + struct m_tag *mtag; + ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; + + /* + * ALTQ uses mbuf tags from another + * packet filtering system - pf(4). + * We allocate a tag in its format + * and fill it in, pretending to be pf(4). + */ + match = 1; + at = pf_find_mtag(m); + if (at != NULL && at->qid != 0) + break; + mtag = m_tag_get(PACKET_TAG_PF, + sizeof(struct pf_mtag), M_NOWAIT | M_ZERO); + if (mtag == NULL) { + /* + * Let the packet fall back to the + * default ALTQ. + */ + break; + } + m_tag_prepend(m, mtag); + at = (struct pf_mtag *)(mtag + 1); + at->qid = altq->qid; + at->hdr = ip; + break; + } + + case O_LOG: + ipfw_log(f, hlen, args, m, + oif, offset | ip6f_mf, tablearg, ip); + match = 1; + break; + + case O_PROB: + match = (random()<((ipfw_insn_u32 *)cmd)->d[0]); + break; + + case O_VERREVPATH: + /* Outgoing packets automatically pass/match */ + match = ((oif != NULL) || + (m->m_pkthdr.rcvif == NULL) || + ( +#ifdef INET6 + is_ipv6 ? + verify_path6(&(args->f_id.src_ip6), + m->m_pkthdr.rcvif, args->f_id.fib) : +#endif + verify_path(src_ip, m->m_pkthdr.rcvif, + args->f_id.fib))); + break; + + case O_VERSRCREACH: + /* Outgoing packets automatically pass/match */ + match = (hlen > 0 && ((oif != NULL) || +#ifdef INET6 + is_ipv6 ? + verify_path6(&(args->f_id.src_ip6), + NULL, args->f_id.fib) : +#endif + verify_path(src_ip, NULL, args->f_id.fib))); + break; + + case O_ANTISPOOF: + /* Outgoing packets automatically pass/match */ + if (oif == NULL && hlen > 0 && + ( (is_ipv4 && in_localaddr(src_ip)) +#ifdef INET6 + || (is_ipv6 && + in6_localaddr(&(args->f_id.src_ip6))) +#endif + )) + match = +#ifdef INET6 + is_ipv6 ? verify_path6( + &(args->f_id.src_ip6), + m->m_pkthdr.rcvif, + args->f_id.fib) : +#endif + verify_path(src_ip, + m->m_pkthdr.rcvif, + args->f_id.fib); + else + match = 1; + break; + + case O_IPSEC: +#ifdef IPSEC + match = (m_tag_find(m, + PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL); +#endif + /* otherwise no match */ + break; + +#ifdef INET6 + case O_IP6_SRC: + match = is_ipv6 && + IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6, + &((ipfw_insn_ip6 *)cmd)->addr6); + break; + + case O_IP6_DST: + match = is_ipv6 && + IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6, + &((ipfw_insn_ip6 *)cmd)->addr6); + break; + case O_IP6_SRC_MASK: + case O_IP6_DST_MASK: + if (is_ipv6) { + int i = cmdlen - 1; + struct in6_addr p; + struct in6_addr *d = + &((ipfw_insn_ip6 *)cmd)->addr6; + + for (; !match && i > 0; d += 2, + i -= F_INSN_SIZE(struct in6_addr) + * 2) { + p = (cmd->opcode == + O_IP6_SRC_MASK) ? + args->f_id.src_ip6: + args->f_id.dst_ip6; + APPLY_MASK(&p, &d[1]); + match = + IN6_ARE_ADDR_EQUAL(&d[0], + &p); + } + } + break; + + case O_FLOW6ID: + match = is_ipv6 && + flow6id_match(args->f_id.flow_id6, + (ipfw_insn_u32 *) cmd); + break; + + case O_EXT_HDR: + match = is_ipv6 && + (ext_hd & ((ipfw_insn *) cmd)->arg1); + break; + + case O_IP6: + match = is_ipv6; + break; +#endif + + case O_IP4: + match = is_ipv4; + break; + + case O_TAG: { + struct m_tag *mtag; + uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + + /* Packet is already tagged with this tag? */ + mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL); + + /* We have `untag' action when F_NOT flag is + * present. And we must remove this mtag from + * mbuf and reset `match' to zero (`match' will + * be inversed later). + * Otherwise we should allocate new mtag and + * push it into mbuf. + */ + if (cmd->len & F_NOT) { /* `untag' action */ + if (mtag != NULL) + m_tag_delete(m, mtag); + match = 0; + } else { + if (mtag == NULL) { + mtag = m_tag_alloc( MTAG_IPFW, + tag, 0, M_NOWAIT); + if (mtag != NULL) + m_tag_prepend(m, mtag); + } + match = 1; + } + break; + } + + case O_FIB: /* try match the specified fib */ + if (args->f_id.fib == cmd->arg1) + match = 1; + break; + + case O_SOCKARG: { + struct inpcb *inp = args->inp; + struct inpcbinfo *pi; + + if (is_ipv6) /* XXX can we remove this ? */ + break; + + if (proto == IPPROTO_TCP) + pi = &V_tcbinfo; + else if (proto == IPPROTO_UDP) + pi = &V_udbinfo; + else + break; + + /* + * XXXRW: so_user_cookie should almost + * certainly be inp_user_cookie? + */ + + /* For incomming packet, lookup up the + inpcb using the src/dest ip/port tuple */ + if (inp == NULL) { + inp = in_pcblookup(pi, + src_ip, htons(src_port), + dst_ip, htons(dst_port), + INPLOOKUP_RLOCKPCB, NULL); + if (inp != NULL) { + tablearg = + inp->inp_socket->so_user_cookie; + if (tablearg) + match = 1; + INP_RUNLOCK(inp); + } + } else { + if (inp->inp_socket) { + tablearg = + inp->inp_socket->so_user_cookie; + if (tablearg) + match = 1; + } + } + break; + } + + case O_TAGGED: { + struct m_tag *mtag; + uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + + if (cmdlen == 1) { + match = m_tag_locate(m, MTAG_IPFW, + tag, NULL) != NULL; + break; + } + + /* we have ranges */ + for (mtag = m_tag_first(m); + mtag != NULL && !match; + mtag = m_tag_next(m, mtag)) { + uint16_t *p; + int i; + + if (mtag->m_tag_cookie != MTAG_IPFW) + continue; + + p = ((ipfw_insn_u16 *)cmd)->ports; + i = cmdlen - 1; + for(; !match && i > 0; i--, p += 2) + match = + mtag->m_tag_id >= p[0] && + mtag->m_tag_id <= p[1]; + } + break; + } + + /* + * The second set of opcodes represents 'actions', + * i.e. the terminal part of a rule once the packet + * matches all previous patterns. + * Typically there is only one action for each rule, + * and the opcode is stored at the end of the rule + * (but there are exceptions -- see below). + * + * In general, here we set retval and terminate the + * outer loop (would be a 'break 3' in some language, + * but we need to set l=0, done=1) + * + * Exceptions: + * O_COUNT and O_SKIPTO actions: + * instead of terminating, we jump to the next rule + * (setting l=0), or to the SKIPTO target (setting + * f/f_len, cmd and l as needed), respectively. + * + * O_TAG, O_LOG and O_ALTQ action parameters: + * perform some action and set match = 1; + * + * O_LIMIT and O_KEEP_STATE: these opcodes are + * not real 'actions', and are stored right + * before the 'action' part of the rule. + * These opcodes try to install an entry in the + * state tables; if successful, we continue with + * the next opcode (match=1; break;), otherwise + * the packet must be dropped (set retval, + * break loops with l=0, done=1) + * + * O_PROBE_STATE and O_CHECK_STATE: these opcodes + * cause a lookup of the state table, and a jump + * to the 'action' part of the parent rule + * if an entry is found, or + * (CHECK_STATE only) a jump to the next rule if + * the entry is not found. + * The result of the lookup is cached so that + * further instances of these opcodes become NOPs. + * The jump to the next rule is done by setting + * l=0, cmdlen=0. + */ + case O_LIMIT: + case O_KEEP_STATE: + if (ipfw_install_state(f, + (ipfw_insn_limit *)cmd, args, tablearg)) { + /* error or limit violation */ + retval = IP_FW_DENY; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + } + match = 1; + break; + + case O_PROBE_STATE: + case O_CHECK_STATE: + /* + * dynamic rules are checked at the first + * keep-state or check-state occurrence, + * with the result being stored in dyn_dir. + * The compiler introduces a PROBE_STATE + * instruction for us when we have a + * KEEP_STATE (because PROBE_STATE needs + * to be run first). + */ + if (dyn_dir == MATCH_UNKNOWN && + (q = ipfw_lookup_dyn_rule(&args->f_id, + &dyn_dir, proto == IPPROTO_TCP ? + TCP(ulp) : NULL)) + != NULL) { + /* + * Found dynamic entry, update stats + * and jump to the 'action' part of + * the parent rule by setting + * f, cmd, l and clearing cmdlen. + */ + q->pcnt++; + q->bcnt += pktlen; + /* XXX we would like to have f_pos + * readily accessible in the dynamic + * rule, instead of having to + * lookup q->rule. + */ + f = q->rule; + f_pos = ipfw_find_rule(chain, + f->rulenum, f->id); + cmd = ACTION_PTR(f); + l = f->cmd_len - f->act_ofs; + ipfw_dyn_unlock(); + cmdlen = 0; + match = 1; + break; + } + /* + * Dynamic entry not found. If CHECK_STATE, + * skip to next rule, if PROBE_STATE just + * ignore and continue with next opcode. + */ + if (cmd->opcode == O_CHECK_STATE) + l = 0; /* exit inner loop */ + match = 1; + break; + + case O_ACCEPT: + retval = 0; /* accept */ + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + + case O_PIPE: + case O_QUEUE: + set_match(args, f_pos, chain); + args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + if (cmd->opcode == O_PIPE) + args->rule.info |= IPFW_IS_PIPE; + if (V_fw_one_pass) + args->rule.info |= IPFW_ONEPASS; + retval = IP_FW_DUMMYNET; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + + case O_DIVERT: + case O_TEE: + if (args->eh) /* not on layer 2 */ + break; + /* otherwise this is terminal */ + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + retval = (cmd->opcode == O_DIVERT) ? + IP_FW_DIVERT : IP_FW_TEE; + set_match(args, f_pos, chain); + args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + break; + + case O_COUNT: + f->pcnt++; /* update stats */ + f->bcnt += pktlen; + f->timestamp = time_uptime; + l = 0; /* exit inner loop */ + break; + + case O_SKIPTO: + f->pcnt++; /* update stats */ + f->bcnt += pktlen; + f->timestamp = time_uptime; + /* If possible use cached f_pos (in f->next_rule), + * whose version is written in f->next_rule + * (horrible hacks to avoid changing the ABI). + */ + if (cmd->arg1 != IP_FW_TABLEARG && + (uintptr_t)f->x_next == chain->id) { + f_pos = (uintptr_t)f->next_rule; + } else { + int i = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + /* make sure we do not jump backward */ + if (i <= f->rulenum) + i = f->rulenum + 1; + f_pos = ipfw_find_rule(chain, i, 0); + /* update the cache */ + if (cmd->arg1 != IP_FW_TABLEARG) { + f->next_rule = + (void *)(uintptr_t)f_pos; + f->x_next = + (void *)(uintptr_t)chain->id; + } + } + /* + * Skip disabled rules, and re-enter + * the inner loop with the correct + * f_pos, f, l and cmd. + * Also clear cmdlen and skip_or + */ + for (; f_pos < chain->n_rules - 1 && + (V_set_disable & + (1 << chain->map[f_pos]->set)); + f_pos++) + ; + /* Re-enter the inner loop at the skipto rule. */ + f = chain->map[f_pos]; + l = f->cmd_len; + cmd = f->cmd; + match = 1; + cmdlen = 0; + skip_or = 0; + continue; + break; /* not reached */ + + case O_CALLRETURN: { + /* + * Implementation of `subroutine' call/return, + * in the stack carried in an mbuf tag. This + * is different from `skipto' in that any call + * address is possible (`skipto' must prevent + * backward jumps to avoid endless loops). + * We have `return' action when F_NOT flag is + * present. The `m_tag_id' field is used as + * stack pointer. + */ + struct m_tag *mtag; + uint16_t jmpto, *stack; + +#define IS_CALL ((cmd->len & F_NOT) == 0) +#define IS_RETURN ((cmd->len & F_NOT) != 0) + /* + * Hand-rolled version of m_tag_locate() with + * wildcard `type'. + * If not already tagged, allocate new tag. + */ + mtag = m_tag_first(m); + while (mtag != NULL) { + if (mtag->m_tag_cookie == + MTAG_IPFW_CALL) + break; + mtag = m_tag_next(m, mtag); + } + if (mtag == NULL && IS_CALL) { + mtag = m_tag_alloc(MTAG_IPFW_CALL, 0, + IPFW_CALLSTACK_SIZE * + sizeof(uint16_t), M_NOWAIT); + if (mtag != NULL) + m_tag_prepend(m, mtag); + } + + /* + * On error both `call' and `return' just + * continue with next rule. + */ + if (IS_RETURN && (mtag == NULL || + mtag->m_tag_id == 0)) { + l = 0; /* exit inner loop */ + break; + } + if (IS_CALL && (mtag == NULL || + mtag->m_tag_id >= IPFW_CALLSTACK_SIZE)) { + printf("ipfw: call stack error, " + "go to next rule\n"); + l = 0; /* exit inner loop */ + break; + } + + f->pcnt++; /* update stats */ + f->bcnt += pktlen; + f->timestamp = time_uptime; + stack = (uint16_t *)(mtag + 1); + + /* + * The `call' action may use cached f_pos + * (in f->next_rule), whose version is written + * in f->next_rule. + * The `return' action, however, doesn't have + * fixed jump address in cmd->arg1 and can't use + * cache. + */ + if (IS_CALL) { + stack[mtag->m_tag_id] = f->rulenum; + mtag->m_tag_id++; + if (cmd->arg1 != IP_FW_TABLEARG && + (uintptr_t)f->x_next == chain->id) { + f_pos = (uintptr_t)f->next_rule; + } else { + jmpto = (cmd->arg1 == + IP_FW_TABLEARG) ? tablearg: + cmd->arg1; + f_pos = ipfw_find_rule(chain, + jmpto, 0); + /* update the cache */ + if (cmd->arg1 != + IP_FW_TABLEARG) { + f->next_rule = + (void *)(uintptr_t) + f_pos; + f->x_next = + (void *)(uintptr_t) + chain->id; + } + } + } else { /* `return' action */ + mtag->m_tag_id--; + jmpto = stack[mtag->m_tag_id] + 1; + f_pos = ipfw_find_rule(chain, jmpto, 0); + } + + /* + * Skip disabled rules, and re-enter + * the inner loop with the correct + * f_pos, f, l and cmd. + * Also clear cmdlen and skip_or + */ + for (; f_pos < chain->n_rules - 1 && + (V_set_disable & + (1 << chain->map[f_pos]->set)); f_pos++) + ; + /* Re-enter the inner loop at the dest rule. */ + f = chain->map[f_pos]; + l = f->cmd_len; + cmd = f->cmd; + cmdlen = 0; + skip_or = 0; + continue; + break; /* NOTREACHED */ + } +#undef IS_CALL +#undef IS_RETURN + + case O_REJECT: + /* + * Drop the packet and send a reject notice + * if the packet is not ICMP (or is an ICMP + * query), and it is not multicast/broadcast. + */ + if (hlen > 0 && is_ipv4 && offset == 0 && + (proto != IPPROTO_ICMP || + is_icmp_query(ICMP(ulp))) && + !(m->m_flags & (M_BCAST|M_MCAST)) && + !IN_MULTICAST(ntohl(dst_ip.s_addr))) { + send_reject(args, cmd->arg1, iplen, ip); + m = args->m; + } + /* FALLTHROUGH */ +#ifdef INET6 + case O_UNREACH6: + if (hlen > 0 && is_ipv6 && + ((offset & IP6F_OFF_MASK) == 0) && + (proto != IPPROTO_ICMPV6 || + (is_icmp6_query(icmp6_type) == 1)) && + !(m->m_flags & (M_BCAST|M_MCAST)) && + !IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) { + send_reject6( + args, cmd->arg1, hlen, + (struct ip6_hdr *)ip); + m = args->m; + } + /* FALLTHROUGH */ +#endif + case O_DENY: + retval = IP_FW_DENY; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + + case O_FORWARD_IP: + if (args->eh) /* not valid on layer2 pkts */ + break; + if (q == NULL || q->rule != f || + dyn_dir == MATCH_FORWARD) { + struct sockaddr_in *sa; + sa = &(((ipfw_insn_sa *)cmd)->sa); + if (sa->sin_addr.s_addr == INADDR_ANY) { + bcopy(sa, &args->hopstore, + sizeof(*sa)); + args->hopstore.sin_addr.s_addr = + htonl(tablearg); + args->next_hop = &args->hopstore; + } else { + args->next_hop = sa; + } + } + retval = IP_FW_PASS; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + +#ifdef INET6 + case O_FORWARD_IP6: + if (args->eh) /* not valid on layer2 pkts */ + break; + if (q == NULL || q->rule != f || + dyn_dir == MATCH_FORWARD) { + struct sockaddr_in6 *sin6; + + sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); + args->next_hop6 = sin6; + } + retval = IP_FW_PASS; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; +#endif + + case O_NETGRAPH: + case O_NGTEE: + set_match(args, f_pos, chain); + args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + if (V_fw_one_pass) + args->rule.info |= IPFW_ONEPASS; + retval = (cmd->opcode == O_NETGRAPH) ? + IP_FW_NETGRAPH : IP_FW_NGTEE; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + + case O_SETFIB: { + uint32_t fib; + + f->pcnt++; /* update stats */ + f->bcnt += pktlen; + f->timestamp = time_uptime; + fib = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg: + cmd->arg1; + if (fib >= rt_numfibs) + fib = 0; + M_SETFIB(m, fib); + args->f_id.fib = fib; + l = 0; /* exit inner loop */ + break; + } + + case O_NAT: + if (!IPFW_NAT_LOADED) { + retval = IP_FW_DENY; + } else { + struct cfg_nat *t; + int nat_id; + + set_match(args, f_pos, chain); + /* Check if this is 'global' nat rule */ + if (cmd->arg1 == 0) { + retval = ipfw_nat_ptr(args, NULL, m); + l = 0; + done = 1; + break; + } + t = ((ipfw_insn_nat *)cmd)->nat; + if (t == NULL) { + nat_id = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; + t = (*lookup_nat_ptr)(&chain->nat, nat_id); + + if (t == NULL) { + retval = IP_FW_DENY; + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + } + if (cmd->arg1 != IP_FW_TABLEARG) + ((ipfw_insn_nat *)cmd)->nat = t; + } + retval = ipfw_nat_ptr(args, t, m); + } + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ + break; + + case O_REASS: { + int ip_off; + + f->pcnt++; + f->bcnt += pktlen; + l = 0; /* in any case exit inner loop */ + ip_off = ntohs(ip->ip_off); + + /* if not fragmented, go to next rule */ + if ((ip_off & (IP_MF | IP_OFFMASK)) == 0) + break; + /* + * ip_reass() expects len & off in host + * byte order. + */ + SET_HOST_IPLEN(ip); + + args->m = m = ip_reass(m); + + /* + * do IP header checksum fixup. + */ + if (m == NULL) { /* fragment got swallowed */ + retval = IP_FW_DENY; + } else { /* good, packet complete */ + int hlen; + + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + SET_NET_IPLEN(ip); + ip->ip_sum = 0; + if (hlen == sizeof(struct ip)) + ip->ip_sum = in_cksum_hdr(ip); + else + ip->ip_sum = in_cksum(m, hlen); + retval = IP_FW_REASS; + set_match(args, f_pos, chain); + } + done = 1; /* exit outer loop */ + break; + } + + default: + panic("-- unknown opcode %d\n", cmd->opcode); + } /* end of switch() on opcodes */ + /* + * if we get here with l=0, then match is irrelevant. + */ + + if (cmd->len & F_NOT) + match = !match; + + if (match) { + if (cmd->len & F_OR) + skip_or = 1; + } else { + if (!(cmd->len & F_OR)) /* not an OR block, */ + break; /* try next rule */ + } + + } /* end of inner loop, scan opcodes */ +#undef PULLUP_LEN + + if (done) + break; + +/* next_rule:; */ /* try next rule */ + + } /* end of outer for, scan rules */ + + if (done) { + struct ip_fw *rule = chain->map[f_pos]; + /* Update statistics */ + rule->pcnt++; + rule->bcnt += pktlen; + rule->timestamp = time_uptime; + } else { + retval = IP_FW_DENY; + printf("ipfw: ouch!, skip past end of rules, denying packet\n"); + } + IPFW_RUNLOCK(chain); +#ifdef __FreeBSD__ + if (ucred_cache != NULL) + crfree(ucred_cache); +#endif + return (retval); + +pullup_failed: + if (V_fw_verbose) + printf("ipfw: pullup failed\n"); + return (IP_FW_DENY); +} + +/* + * Set maximum number of tables that can be used in given VNET ipfw instance. + */ +#ifdef SYSCTL_NODE +static int +sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS) +{ + int error; + unsigned int ntables; + + ntables = V_fw_tables_max; + + error = sysctl_handle_int(oidp, &ntables, 0, req); + /* Read operation or some error */ + if ((error != 0) || (req->newptr == NULL)) + return (error); + + return (ipfw_resize_tables(&V_layer3_chain, ntables)); +} +#endif +/* + * Module and VNET glue + */ + +/* + * Stuff that must be initialised only on boot or module load + */ +static int +ipfw_init(void) +{ + int error = 0; + + ipfw_dyn_attach(); + /* + * Only print out this stuff the first time around, + * when called from the sysinit code. + */ + printf("ipfw2 " +#ifdef INET6 + "(+ipv6) " +#endif + "initialized, divert %s, nat %s, " + "rule-based forwarding " +#ifdef IPFIREWALL_FORWARD + "enabled, " +#else + "disabled, " +#endif + "default to %s, logging ", +#ifdef IPDIVERT + "enabled", +#else + "loadable", +#endif +#ifdef IPFIREWALL_NAT + "enabled", +#else + "loadable", +#endif + default_to_accept ? "accept" : "deny"); + + /* + * Note: V_xxx variables can be accessed here but the vnet specific + * initializer may not have been called yet for the VIMAGE case. + * Tuneables will have been processed. We will print out values for + * the default vnet. + * XXX This should all be rationalized AFTER 8.0 + */ + if (V_fw_verbose == 0) + printf("disabled\n"); + else if (V_verbose_limit == 0) + printf("unlimited\n"); + else + printf("limited to %d packets/entry by default\n", + V_verbose_limit); + + /* Check user-supplied table count for validness */ + if (default_fw_tables > IPFW_TABLES_MAX) + default_fw_tables = IPFW_TABLES_MAX; + + ipfw_log_bpf(1); /* init */ + return (error); +} + +/* + * Called for the removal of the last instance only on module unload. + */ +static void +ipfw_destroy(void) +{ + + ipfw_log_bpf(0); /* uninit */ + ipfw_dyn_detach(); + printf("IP firewall unloaded\n"); +} + +/* + * Stuff that must be initialized for every instance + * (including the first of course). + */ +static int +vnet_ipfw_init(const void *unused) +{ + int error; + struct ip_fw *rule = NULL; + struct ip_fw_chain *chain; + + chain = &V_layer3_chain; + + /* First set up some values that are compile time options */ + V_autoinc_step = 100; /* bounded to 1..1000 in add_rule() */ + V_fw_deny_unknown_exthdrs = 1; +#ifdef IPFIREWALL_VERBOSE + V_fw_verbose = 1; +#endif +#ifdef IPFIREWALL_VERBOSE_LIMIT + V_verbose_limit = IPFIREWALL_VERBOSE_LIMIT; +#endif +#ifdef IPFIREWALL_NAT + LIST_INIT(&chain->nat); +#endif + + /* insert the default rule and create the initial map */ + chain->n_rules = 1; + chain->static_len = sizeof(struct ip_fw); + chain->map = malloc(sizeof(struct ip_fw *), M_IPFW, M_WAITOK | M_ZERO); + if (chain->map) + rule = malloc(chain->static_len, M_IPFW, M_WAITOK | M_ZERO); + + /* Set initial number of tables */ + V_fw_tables_max = default_fw_tables; + error = ipfw_init_tables(chain); + if (error) { + printf("ipfw2: setting up tables failed\n"); + free(chain->map, M_IPFW); + free(rule, M_IPFW); + return (ENOSPC); + } + + /* fill and insert the default rule */ + rule->act_ofs = 0; + rule->rulenum = IPFW_DEFAULT_RULE; + rule->cmd_len = 1; + rule->set = RESVD_SET; + rule->cmd[0].len = 1; + rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY; + chain->rules = chain->default_rule = chain->map[0] = rule; + chain->id = rule->id = 1; + + IPFW_LOCK_INIT(chain); + ipfw_dyn_init(); + + /* First set up some values that are compile time options */ + V_ipfw_vnet_ready = 1; /* Open for business */ + + /* + * Hook the sockopt handler and pfil hooks for ipv4 and ipv6. + * Even if the latter two fail we still keep the module alive + * because the sockopt and layer2 paths are still useful. + * ipfw[6]_hook return 0 on success, ENOENT on failure, + * so we can ignore the exact return value and just set a flag. + * + * Note that V_fw[6]_enable are manipulated by a SYSCTL_PROC so + * changes in the underlying (per-vnet) variables trigger + * immediate hook()/unhook() calls. + * In layer2 we have the same behaviour, except that V_ether_ipfw + * is checked on each packet because there are no pfil hooks. + */ + V_ip_fw_ctl_ptr = ipfw_ctl; + error = ipfw_attach_hooks(1); + return (error); +} + +/* + * Called for the removal of each instance. + */ +static int +vnet_ipfw_uninit(const void *unused) +{ + struct ip_fw *reap, *rule; + struct ip_fw_chain *chain = &V_layer3_chain; + int i; + + V_ipfw_vnet_ready = 0; /* tell new callers to go away */ + /* + * disconnect from ipv4, ipv6, layer2 and sockopt. + * Then grab, release and grab again the WLOCK so we make + * sure the update is propagated and nobody will be in. + */ + (void)ipfw_attach_hooks(0 /* detach */); + V_ip_fw_ctl_ptr = NULL; + IPFW_UH_WLOCK(chain); + IPFW_UH_WUNLOCK(chain); + IPFW_UH_WLOCK(chain); + + IPFW_WLOCK(chain); + ipfw_dyn_uninit(0); /* run the callout_drain */ + IPFW_WUNLOCK(chain); + + ipfw_destroy_tables(chain); + reap = NULL; + IPFW_WLOCK(chain); + for (i = 0; i < chain->n_rules; i++) { + rule = chain->map[i]; + rule->x_next = reap; + reap = rule; + } + if (chain->map) + free(chain->map, M_IPFW); + IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); + if (reap != NULL) + ipfw_reap_rules(reap); + IPFW_LOCK_DESTROY(chain); + ipfw_dyn_uninit(1); /* free the remaining parts */ + return 0; +} + +/* + * Module event handler. + * In general we have the choice of handling most of these events by the + * event handler or by the (VNET_)SYS(UN)INIT handlers. I have chosen to + * use the SYSINIT handlers as they are more capable of expressing the + * flow of control during module and vnet operations, so this is just + * a skeleton. Note there is no SYSINIT equivalent of the module + * SHUTDOWN handler, but we don't have anything to do in that case anyhow. + */ +static int +ipfw_modevent(module_t mod, int type, void *unused) +{ + int err = 0; + + switch (type) { + case MOD_LOAD: + /* Called once at module load or + * system boot if compiled in. */ + break; + case MOD_QUIESCE: + /* Called before unload. May veto unloading. */ + break; + case MOD_UNLOAD: + /* Called during unload. */ + break; + case MOD_SHUTDOWN: + /* Called during system shutdown. */ + break; + default: + err = EOPNOTSUPP; + break; + } + return err; +} + +static moduledata_t ipfwmod = { + "ipfw", + ipfw_modevent, + 0 +}; + +/* Define startup order. */ +#define IPFW_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN +#define IPFW_MODEVENT_ORDER (SI_ORDER_ANY - 255) /* On boot slot in here. */ +#define IPFW_MODULE_ORDER (IPFW_MODEVENT_ORDER + 1) /* A little later. */ +#define IPFW_VNET_ORDER (IPFW_MODEVENT_ORDER + 2) /* Later still. */ + +DECLARE_MODULE(ipfw, ipfwmod, IPFW_SI_SUB_FIREWALL, IPFW_MODEVENT_ORDER); +MODULE_VERSION(ipfw, 2); +/* should declare some dependencies here */ + +/* + * Starting up. Done in order after ipfwmod() has been called. + * VNET_SYSINIT is also called for each existing vnet and each new vnet. + */ +SYSINIT(ipfw_init, IPFW_SI_SUB_FIREWALL, IPFW_MODULE_ORDER, + ipfw_init, NULL); +VNET_SYSINIT(vnet_ipfw_init, IPFW_SI_SUB_FIREWALL, IPFW_VNET_ORDER, + vnet_ipfw_init, NULL); + +/* + * Closing up shop. These are done in REVERSE ORDER, but still + * after ipfwmod() has been called. Not called on reboot. + * VNET_SYSUNINIT is also called for each exiting vnet as it exits. + * or when the module is unloaded. + */ +SYSUNINIT(ipfw_destroy, IPFW_SI_SUB_FIREWALL, IPFW_MODULE_ORDER, + ipfw_destroy, NULL); +VNET_SYSUNINIT(vnet_ipfw_uninit, IPFW_SI_SUB_FIREWALL, IPFW_VNET_ORDER, + vnet_ipfw_uninit, NULL); +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c new file mode 100644 index 0000000000000..28d2d5169b5cf --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -0,0 +1,1245 @@ +/*- + * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa + * + * 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 +__FBSDID("$FreeBSD$"); + +#define DEB(x) +#define DDB(x) x + +/* + * Dynamic rule support for ipfw + */ + +#include "opt_ipfw.h" +#include "opt_inet.h" +#ifndef INET +#error IPFIREWALL requires INET. +#endif /* INET */ +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for ETHERTYPE_IP */ +#include +#include + +#include +#include +#include /* ip_defttl */ +#include +#include +#include + +#include /* IN6_ARE_ADDR_EQUAL */ +#ifdef INET6 +#include +#include +#endif + +#include + +#include /* XXX for in_cksum */ + +#ifdef MAC +#include +#endif + +/* + * Description of dynamic rules. + * + * Dynamic rules are stored in lists accessed through a hash table + * (ipfw_dyn_v) whose size is curr_dyn_buckets. This value can + * be modified through the sysctl variable dyn_buckets which is + * updated when the table becomes empty. + * + * XXX currently there is only one list, ipfw_dyn. + * + * When a packet is received, its address fields are first masked + * with the mask defined for the rule, then hashed, then matched + * against the entries in the corresponding list. + * Dynamic rules can be used for different purposes: + * + stateful rules; + * + enforcing limits on the number of sessions; + * + in-kernel NAT (not implemented yet) + * + * The lifetime of dynamic rules is regulated by dyn_*_lifetime, + * measured in seconds and depending on the flags. + * + * The total number of dynamic rules is stored in dyn_count. + * The max number of dynamic rules is dyn_max. When we reach + * the maximum number of rules we do not create anymore. This is + * done to avoid consuming too much memory, but also too much + * time when searching on each packet (ideally, we should try instead + * to put a limit on the length of the list on each bucket...). + * + * Each dynamic rule holds a pointer to the parent ipfw rule so + * we know what action to perform. Dynamic rules are removed when + * the parent rule is deleted. XXX we should make them survive. + * + * There are some limitations with dynamic rules -- we do not + * obey the 'randomized match', and we do not do multiple + * passes through the firewall. XXX check the latter!!! + */ + +/* + * Static variables followed by global ones + */ +static VNET_DEFINE(ipfw_dyn_rule **, ipfw_dyn_v); +static VNET_DEFINE(u_int32_t, dyn_buckets); +static VNET_DEFINE(u_int32_t, curr_dyn_buckets); +static VNET_DEFINE(struct callout, ipfw_timeout); +#define V_ipfw_dyn_v VNET(ipfw_dyn_v) +#define V_dyn_buckets VNET(dyn_buckets) +#define V_curr_dyn_buckets VNET(curr_dyn_buckets) +#define V_ipfw_timeout VNET(ipfw_timeout) + +static uma_zone_t ipfw_dyn_rule_zone; +#ifndef __FreeBSD__ +DEFINE_SPINLOCK(ipfw_dyn_mtx); +#else +static struct mtx ipfw_dyn_mtx; /* mutex guarding dynamic rules */ +#endif + +#define IPFW_DYN_LOCK_INIT() \ + mtx_init(&ipfw_dyn_mtx, "IPFW dynamic rules", NULL, MTX_DEF) +#define IPFW_DYN_LOCK_DESTROY() mtx_destroy(&ipfw_dyn_mtx) +#define IPFW_DYN_LOCK() mtx_lock(&ipfw_dyn_mtx) +#define IPFW_DYN_UNLOCK() mtx_unlock(&ipfw_dyn_mtx) +#define IPFW_DYN_LOCK_ASSERT() mtx_assert(&ipfw_dyn_mtx, MA_OWNED) + +void +ipfw_dyn_unlock(void) +{ + IPFW_DYN_UNLOCK(); +} + +/* + * Timeouts for various events in handing dynamic rules. + */ +static VNET_DEFINE(u_int32_t, dyn_ack_lifetime); +static VNET_DEFINE(u_int32_t, dyn_syn_lifetime); +static VNET_DEFINE(u_int32_t, dyn_fin_lifetime); +static VNET_DEFINE(u_int32_t, dyn_rst_lifetime); +static VNET_DEFINE(u_int32_t, dyn_udp_lifetime); +static VNET_DEFINE(u_int32_t, dyn_short_lifetime); + +#define V_dyn_ack_lifetime VNET(dyn_ack_lifetime) +#define V_dyn_syn_lifetime VNET(dyn_syn_lifetime) +#define V_dyn_fin_lifetime VNET(dyn_fin_lifetime) +#define V_dyn_rst_lifetime VNET(dyn_rst_lifetime) +#define V_dyn_udp_lifetime VNET(dyn_udp_lifetime) +#define V_dyn_short_lifetime VNET(dyn_short_lifetime) + +/* + * Keepalives are sent if dyn_keepalive is set. They are sent every + * dyn_keepalive_period seconds, in the last dyn_keepalive_interval + * seconds of lifetime of a rule. + * dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower + * than dyn_keepalive_period. + */ + +static VNET_DEFINE(u_int32_t, dyn_keepalive_interval); +static VNET_DEFINE(u_int32_t, dyn_keepalive_period); +static VNET_DEFINE(u_int32_t, dyn_keepalive); + +#define V_dyn_keepalive_interval VNET(dyn_keepalive_interval) +#define V_dyn_keepalive_period VNET(dyn_keepalive_period) +#define V_dyn_keepalive VNET(dyn_keepalive) + +static VNET_DEFINE(u_int32_t, dyn_count); /* # of dynamic rules */ +static VNET_DEFINE(u_int32_t, dyn_max); /* max # of dynamic rules */ + +#define V_dyn_count VNET(dyn_count) +#define V_dyn_max VNET(dyn_max) + +#ifdef SYSCTL_NODE + +SYSBEGIN(f2) + +SYSCTL_DECL(_net_inet_ip_fw); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, + CTLFLAG_RW, &VNET_NAME(dyn_buckets), 0, + "Number of dyn. buckets"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets, + CTLFLAG_RD, &VNET_NAME(curr_dyn_buckets), 0, + "Current Number of dyn. buckets"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_count, + CTLFLAG_RD, &VNET_NAME(dyn_count), 0, + "Number of dyn. rules"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_max, + CTLFLAG_RW, &VNET_NAME(dyn_max), 0, + "Max number of dyn. rules"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime, + CTLFLAG_RW, &VNET_NAME(dyn_ack_lifetime), 0, + "Lifetime of dyn. rules for acks"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime, + CTLFLAG_RW, &VNET_NAME(dyn_syn_lifetime), 0, + "Lifetime of dyn. rules for syn"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime, + CTLFLAG_RW, &VNET_NAME(dyn_fin_lifetime), 0, + "Lifetime of dyn. rules for fin"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime, + CTLFLAG_RW, &VNET_NAME(dyn_rst_lifetime), 0, + "Lifetime of dyn. rules for rst"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime, + CTLFLAG_RW, &VNET_NAME(dyn_udp_lifetime), 0, + "Lifetime of dyn. rules for UDP"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, + CTLFLAG_RW, &VNET_NAME(dyn_short_lifetime), 0, + "Lifetime of dyn. rules for other situations"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, + CTLFLAG_RW, &VNET_NAME(dyn_keepalive), 0, + "Enable keepalives for dyn. rules"); + +SYSEND + +#endif /* SYSCTL_NODE */ + + +static __inline int +hash_packet6(struct ipfw_flow_id *id) +{ + u_int32_t i; + i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ + (id->src_ip6.__u6_addr.__u6_addr32[2]) ^ + (id->src_ip6.__u6_addr.__u6_addr32[3]) ^ + (id->dst_port) ^ (id->src_port); + return i; +} + +/* + * IMPORTANT: the hash function for dynamic rules must be commutative + * in source and destination (ip,port), because rules are bidirectional + * and we want to find both in the same bucket. + */ +static __inline int +hash_packet(struct ipfw_flow_id *id) +{ + u_int32_t i; + +#ifdef INET6 + if (IS_IP6_FLOW_ID(id)) + i = hash_packet6(id); + else +#endif /* INET6 */ + i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); + i &= (V_curr_dyn_buckets - 1); + return i; +} + +static __inline void +unlink_dyn_rule_print(struct ipfw_flow_id *id) +{ + struct in_addr da; +#ifdef INET6 + char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; +#else + char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; +#endif + +#ifdef INET6 + if (IS_IP6_FLOW_ID(id)) { + ip6_sprintf(src, &id->src_ip6); + ip6_sprintf(dst, &id->dst_ip6); + } else +#endif + { + da.s_addr = htonl(id->src_ip); + inet_ntop(AF_INET, &da, src, sizeof(src)); + da.s_addr = htonl(id->dst_ip); + inet_ntop(AF_INET, &da, dst, sizeof(dst)); + } + printf("ipfw: unlink entry %s %d -> %s %d, %d left\n", + src, id->src_port, dst, id->dst_port, V_dyn_count - 1); +} + +/** + * unlink a dynamic rule from a chain. prev is a pointer to + * the previous one, q is a pointer to the rule to delete, + * head is a pointer to the head of the queue. + * Modifies q and potentially also head. + */ +#define UNLINK_DYN_RULE(prev, head, q) { \ + ipfw_dyn_rule *old_q = q; \ + \ + /* remove a refcount to the parent */ \ + if (q->dyn_type == O_LIMIT) \ + q->parent->count--; \ + DEB(unlink_dyn_rule_print(&q->id);) \ + if (prev != NULL) \ + prev->next = q = q->next; \ + else \ + head = q = q->next; \ + V_dyn_count--; \ + uma_zfree(ipfw_dyn_rule_zone, old_q); } + +#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) + +/** + * Remove dynamic rules pointing to "rule", or all of them if rule == NULL. + * + * If keep_me == NULL, rules are deleted even if not expired, + * otherwise only expired rules are removed. + * + * The value of the second parameter is also used to point to identify + * a rule we absolutely do not want to remove (e.g. because we are + * holding a reference to it -- this is the case with O_LIMIT_PARENT + * rules). The pointer is only used for comparison, so any non-null + * value will do. + */ +static void +remove_dyn_rule(struct ip_fw *rule, ipfw_dyn_rule *keep_me) +{ + static u_int32_t last_remove = 0; + +#define FORCE (keep_me == NULL) + + ipfw_dyn_rule *prev, *q; + int i, pass = 0, max_pass = 0; + + IPFW_DYN_LOCK_ASSERT(); + + if (V_ipfw_dyn_v == NULL || V_dyn_count == 0) + return; + /* do not expire more than once per second, it is useless */ + if (!FORCE && last_remove == time_uptime) + return; + last_remove = time_uptime; + + /* + * because O_LIMIT refer to parent rules, during the first pass only + * remove child and mark any pending LIMIT_PARENT, and remove + * them in a second pass. + */ +next_pass: + for (i = 0 ; i < V_curr_dyn_buckets ; i++) { + for (prev=NULL, q = V_ipfw_dyn_v[i] ; q ; ) { + /* + * Logic can become complex here, so we split tests. + */ + if (q == keep_me) + goto next; + if (rule != NULL && rule != q->rule) + goto next; /* not the one we are looking for */ + if (q->dyn_type == O_LIMIT_PARENT) { + /* + * handle parent in the second pass, + * record we need one. + */ + max_pass = 1; + if (pass == 0) + goto next; + if (FORCE && q->count != 0 ) { + /* XXX should not happen! */ + printf("ipfw: OUCH! cannot remove rule," + " count %d\n", q->count); + } + } else { + if (!FORCE && + !TIME_LEQ( q->expire, time_uptime )) + goto next; + } + if (q->dyn_type != O_LIMIT_PARENT || !q->count) { + UNLINK_DYN_RULE(prev, V_ipfw_dyn_v[i], q); + continue; + } +next: + prev=q; + q=q->next; + } + } + if (pass++ < max_pass) + goto next_pass; +} + +void +ipfw_remove_dyn_children(struct ip_fw *rule) +{ + IPFW_DYN_LOCK(); + remove_dyn_rule(rule, NULL /* force removal */); + IPFW_DYN_UNLOCK(); +} + +/* + * Lookup a dynamic rule, locked version. + */ +static ipfw_dyn_rule * +lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction, + struct tcphdr *tcp) +{ + /* + * Stateful ipfw extensions. + * Lookup into dynamic session queue. + */ +#define MATCH_REVERSE 0 +#define MATCH_FORWARD 1 +#define MATCH_NONE 2 +#define MATCH_UNKNOWN 3 + int i, dir = MATCH_NONE; + ipfw_dyn_rule *prev, *q = NULL; + + IPFW_DYN_LOCK_ASSERT(); + + if (V_ipfw_dyn_v == NULL) + goto done; /* not found */ + i = hash_packet(pkt); + for (prev = NULL, q = V_ipfw_dyn_v[i]; q != NULL;) { + if (q->dyn_type == O_LIMIT_PARENT && q->count) + goto next; + if (TIME_LEQ(q->expire, time_uptime)) { /* expire entry */ + UNLINK_DYN_RULE(prev, V_ipfw_dyn_v[i], q); + continue; + } + if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT) + goto next; + + if (IS_IP6_FLOW_ID(pkt)) { + if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.src_ip6) && + IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.dst_ip6) && + pkt->src_port == q->id.src_port && + pkt->dst_port == q->id.dst_port) { + dir = MATCH_FORWARD; + break; + } + if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.dst_ip6) && + IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.src_ip6) && + pkt->src_port == q->id.dst_port && + pkt->dst_port == q->id.src_port) { + dir = MATCH_REVERSE; + break; + } + } else { + if (pkt->src_ip == q->id.src_ip && + pkt->dst_ip == q->id.dst_ip && + pkt->src_port == q->id.src_port && + pkt->dst_port == q->id.dst_port) { + dir = MATCH_FORWARD; + break; + } + if (pkt->src_ip == q->id.dst_ip && + pkt->dst_ip == q->id.src_ip && + pkt->src_port == q->id.dst_port && + pkt->dst_port == q->id.src_port) { + dir = MATCH_REVERSE; + break; + } + } +next: + prev = q; + q = q->next; + } + if (q == NULL) + goto done; /* q = NULL, not found */ + + if (prev != NULL) { /* found and not in front */ + prev->next = q->next; + q->next = V_ipfw_dyn_v[i]; + V_ipfw_dyn_v[i] = q; + } + if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */ + uint32_t ack; + u_char flags = pkt->_flags & (TH_FIN | TH_SYN | TH_RST); + +#define BOTH_SYN (TH_SYN | (TH_SYN << 8)) +#define BOTH_FIN (TH_FIN | (TH_FIN << 8)) +#define TCP_FLAGS (TH_FLAGS | (TH_FLAGS << 8)) +#define ACK_FWD 0x10000 /* fwd ack seen */ +#define ACK_REV 0x20000 /* rev ack seen */ + + q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8); + switch (q->state & TCP_FLAGS) { + case TH_SYN: /* opening */ + q->expire = time_uptime + V_dyn_syn_lifetime; + break; + + case BOTH_SYN: /* move to established */ + case BOTH_SYN | TH_FIN: /* one side tries to close */ + case BOTH_SYN | (TH_FIN << 8): +#define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0) + if (tcp == NULL) + break; + + ack = ntohl(tcp->th_ack); + if (dir == MATCH_FORWARD) { + if (q->ack_fwd == 0 || + _SEQ_GE(ack, q->ack_fwd)) { + q->ack_fwd = ack; + q->state |= ACK_FWD; + } + } else { + if (q->ack_rev == 0 || + _SEQ_GE(ack, q->ack_rev)) { + q->ack_rev = ack; + q->state |= ACK_REV; + } + } + if ((q->state & (ACK_FWD | ACK_REV)) == + (ACK_FWD | ACK_REV)) { + q->expire = time_uptime + V_dyn_ack_lifetime; + q->state &= ~(ACK_FWD | ACK_REV); + } + break; + + case BOTH_SYN | BOTH_FIN: /* both sides closed */ + if (V_dyn_fin_lifetime >= V_dyn_keepalive_period) + V_dyn_fin_lifetime = V_dyn_keepalive_period - 1; + q->expire = time_uptime + V_dyn_fin_lifetime; + break; + + default: +#if 0 + /* + * reset or some invalid combination, but can also + * occur if we use keep-state the wrong way. + */ + if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0) + printf("invalid state: 0x%x\n", q->state); +#endif + if (V_dyn_rst_lifetime >= V_dyn_keepalive_period) + V_dyn_rst_lifetime = V_dyn_keepalive_period - 1; + q->expire = time_uptime + V_dyn_rst_lifetime; + break; + } + } else if (pkt->proto == IPPROTO_UDP) { + q->expire = time_uptime + V_dyn_udp_lifetime; + } else { + /* other protocols */ + q->expire = time_uptime + V_dyn_short_lifetime; + } +done: + if (match_direction != NULL) + *match_direction = dir; + return (q); +} + +ipfw_dyn_rule * +ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, + struct tcphdr *tcp) +{ + ipfw_dyn_rule *q; + + IPFW_DYN_LOCK(); + q = lookup_dyn_rule_locked(pkt, match_direction, tcp); + if (q == NULL) + IPFW_DYN_UNLOCK(); + /* NB: return table locked when q is not NULL */ + return q; +} + +static void +realloc_dynamic_table(void) +{ + IPFW_DYN_LOCK_ASSERT(); + + /* + * Try reallocation, make sure we have a power of 2 and do + * not allow more than 64k entries. In case of overflow, + * default to 1024. + */ + + if (V_dyn_buckets > 65536) + V_dyn_buckets = 1024; + if ((V_dyn_buckets & (V_dyn_buckets-1)) != 0) { /* not a power of 2 */ + V_dyn_buckets = V_curr_dyn_buckets; /* reset */ + return; + } + V_curr_dyn_buckets = V_dyn_buckets; + if (V_ipfw_dyn_v != NULL) + free(V_ipfw_dyn_v, M_IPFW); + for (;;) { + V_ipfw_dyn_v = malloc(V_curr_dyn_buckets * sizeof(ipfw_dyn_rule *), + M_IPFW, M_NOWAIT | M_ZERO); + if (V_ipfw_dyn_v != NULL || V_curr_dyn_buckets <= 2) + break; + V_curr_dyn_buckets /= 2; + } +} + +/** + * Install state of type 'type' for a dynamic session. + * The hash table contains two type of rules: + * - regular rules (O_KEEP_STATE) + * - rules for sessions with limited number of sess per user + * (O_LIMIT). When they are created, the parent is + * increased by 1, and decreased on delete. In this case, + * the third parameter is the parent rule and not the chain. + * - "parent" rules for the above (O_LIMIT_PARENT). + */ +static ipfw_dyn_rule * +add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule) +{ + ipfw_dyn_rule *r; + int i; + + IPFW_DYN_LOCK_ASSERT(); + + if (V_ipfw_dyn_v == NULL || + (V_dyn_count == 0 && V_dyn_buckets != V_curr_dyn_buckets)) { + realloc_dynamic_table(); + if (V_ipfw_dyn_v == NULL) + return NULL; /* failed ! */ + } + i = hash_packet(id); + + r = uma_zalloc(ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); + if (r == NULL) { + printf ("ipfw: sorry cannot allocate state\n"); + return NULL; + } + + /* increase refcount on parent, and set pointer */ + if (dyn_type == O_LIMIT) { + ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule; + if ( parent->dyn_type != O_LIMIT_PARENT) + panic("invalid parent"); + parent->count++; + r->parent = parent; + rule = parent->rule; + } + + r->id = *id; + r->expire = time_uptime + V_dyn_syn_lifetime; + r->rule = rule; + r->dyn_type = dyn_type; + r->pcnt = r->bcnt = 0; + r->count = 0; + + r->bucket = i; + r->next = V_ipfw_dyn_v[i]; + V_ipfw_dyn_v[i] = r; + V_dyn_count++; + DEB({ + struct in_addr da; +#ifdef INET6 + char src[INET6_ADDRSTRLEN]; + char dst[INET6_ADDRSTRLEN]; +#else + char src[INET_ADDRSTRLEN]; + char dst[INET_ADDRSTRLEN]; +#endif + +#ifdef INET6 + if (IS_IP6_FLOW_ID(&(r->id))) { + ip6_sprintf(src, &r->id.src_ip6); + ip6_sprintf(dst, &r->id.dst_ip6); + } else +#endif + { + da.s_addr = htonl(r->id.src_ip); + inet_ntop(AF_INET, &da, src, sizeof(src)); + da.s_addr = htonl(r->id.dst_ip); + inet_ntop(AF_INET, &da, dst, sizeof(dst)); + } + printf("ipfw: add dyn entry ty %d %s %d -> %s %d, total %d\n", + dyn_type, src, r->id.src_port, dst, r->id.dst_port, + V_dyn_count); + }) + return r; +} + +/** + * lookup dynamic parent rule using pkt and rule as search keys. + * If the lookup fails, then install one. + */ +static ipfw_dyn_rule * +lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule) +{ + ipfw_dyn_rule *q; + int i; + + IPFW_DYN_LOCK_ASSERT(); + + if (V_ipfw_dyn_v) { + int is_v6 = IS_IP6_FLOW_ID(pkt); + i = hash_packet( pkt ); + for (q = V_ipfw_dyn_v[i] ; q != NULL ; q=q->next) + if (q->dyn_type == O_LIMIT_PARENT && + rule== q->rule && + pkt->proto == q->id.proto && + pkt->src_port == q->id.src_port && + pkt->dst_port == q->id.dst_port && + ( + (is_v6 && + IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.src_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.dst_ip6))) || + (!is_v6 && + pkt->src_ip == q->id.src_ip && + pkt->dst_ip == q->id.dst_ip) + ) + ) { + q->expire = time_uptime + V_dyn_short_lifetime; + DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);) + return q; + } + } + return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); +} + +/** + * Install dynamic state for rule type cmd->o.opcode + * + * Returns 1 (failure) if state is not installed because of errors or because + * session limitations are enforced. + */ +int +ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, + struct ip_fw_args *args, uint32_t tablearg) +{ + static int last_log; + ipfw_dyn_rule *q; + struct in_addr da; +#ifdef INET6 + char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2]; +#else + char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; +#endif + + src[0] = '\0'; + dst[0] = '\0'; + + IPFW_DYN_LOCK(); + + DEB( +#ifdef INET6 + if (IS_IP6_FLOW_ID(&(args->f_id))) { + ip6_sprintf(src, &args->f_id.src_ip6); + ip6_sprintf(dst, &args->f_id.dst_ip6); + } else +#endif + { + da.s_addr = htonl(args->f_id.src_ip); + inet_ntop(AF_INET, &da, src, sizeof(src)); + da.s_addr = htonl(args->f_id.dst_ip); + inet_ntop(AF_INET, &da, dst, sizeof(dst)); + } + printf("ipfw: %s: type %d %s %u -> %s %u\n", + __func__, cmd->o.opcode, src, args->f_id.src_port, + dst, args->f_id.dst_port); + src[0] = '\0'; + dst[0] = '\0'; + ) + + q = lookup_dyn_rule_locked(&args->f_id, NULL, NULL); + + if (q != NULL) { /* should never occur */ + DEB( + if (last_log != time_uptime) { + last_log = time_uptime; + printf("ipfw: %s: entry already present, done\n", + __func__); + }) + IPFW_DYN_UNLOCK(); + return (0); + } + + if (V_dyn_count >= V_dyn_max) + /* Run out of slots, try to remove any expired rule. */ + remove_dyn_rule(NULL, (ipfw_dyn_rule *)1); + + if (V_dyn_count >= V_dyn_max) { + if (last_log != time_uptime) { + last_log = time_uptime; + printf("ipfw: %s: Too many dynamic rules\n", __func__); + } + IPFW_DYN_UNLOCK(); + return (1); /* cannot install, notify caller */ + } + + switch (cmd->o.opcode) { + case O_KEEP_STATE: /* bidir rule */ + add_dyn_rule(&args->f_id, O_KEEP_STATE, rule); + break; + + case O_LIMIT: { /* limit number of sessions */ + struct ipfw_flow_id id; + ipfw_dyn_rule *parent; + uint32_t conn_limit; + uint16_t limit_mask = cmd->limit_mask; + + conn_limit = (cmd->conn_limit == IP_FW_TABLEARG) ? + tablearg : cmd->conn_limit; + + DEB( + if (cmd->conn_limit == IP_FW_TABLEARG) + printf("ipfw: %s: O_LIMIT rule, conn_limit: %u " + "(tablearg)\n", __func__, conn_limit); + else + printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", + __func__, conn_limit); + ) + + id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0; + id.proto = args->f_id.proto; + id.addr_type = args->f_id.addr_type; + id.fib = M_GETFIB(args->m); + + if (IS_IP6_FLOW_ID (&(args->f_id))) { + if (limit_mask & DYN_SRC_ADDR) + id.src_ip6 = args->f_id.src_ip6; + if (limit_mask & DYN_DST_ADDR) + id.dst_ip6 = args->f_id.dst_ip6; + } else { + if (limit_mask & DYN_SRC_ADDR) + id.src_ip = args->f_id.src_ip; + if (limit_mask & DYN_DST_ADDR) + id.dst_ip = args->f_id.dst_ip; + } + if (limit_mask & DYN_SRC_PORT) + id.src_port = args->f_id.src_port; + if (limit_mask & DYN_DST_PORT) + id.dst_port = args->f_id.dst_port; + if ((parent = lookup_dyn_parent(&id, rule)) == NULL) { + printf("ipfw: %s: add parent failed\n", __func__); + IPFW_DYN_UNLOCK(); + return (1); + } + + if (parent->count >= conn_limit) { + /* See if we can remove some expired rule. */ + remove_dyn_rule(rule, parent); + if (parent->count >= conn_limit) { + if (V_fw_verbose && last_log != time_uptime) { + last_log = time_uptime; +#ifdef INET6 + /* + * XXX IPv6 flows are not + * supported yet. + */ + if (IS_IP6_FLOW_ID(&(args->f_id))) { + char ip6buf[INET6_ADDRSTRLEN]; + snprintf(src, sizeof(src), + "[%s]", ip6_sprintf(ip6buf, + &args->f_id.src_ip6)); + snprintf(dst, sizeof(dst), + "[%s]", ip6_sprintf(ip6buf, + &args->f_id.dst_ip6)); + } else +#endif + { + da.s_addr = + htonl(args->f_id.src_ip); + inet_ntop(AF_INET, &da, src, + sizeof(src)); + da.s_addr = + htonl(args->f_id.dst_ip); + inet_ntop(AF_INET, &da, dst, + sizeof(dst)); + } + log(LOG_SECURITY | LOG_DEBUG, + "ipfw: %d %s %s:%u -> %s:%u, %s\n", + parent->rule->rulenum, + "drop session", + src, (args->f_id.src_port), + dst, (args->f_id.dst_port), + "too many entries"); + } + IPFW_DYN_UNLOCK(); + return (1); + } + } + add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent); + break; + } + default: + printf("ipfw: %s: unknown dynamic rule type %u\n", + __func__, cmd->o.opcode); + IPFW_DYN_UNLOCK(); + return (1); + } + + /* XXX just set lifetime */ + lookup_dyn_rule_locked(&args->f_id, NULL, NULL); + + IPFW_DYN_UNLOCK(); + return (0); +} + +/* + * Generate a TCP packet, containing either a RST or a keepalive. + * When flags & TH_RST, we are sending a RST packet, because of a + * "reset" action matched the packet. + * Otherwise we are sending a keepalive, and flags & TH_ + * The 'replyto' mbuf is the mbuf being replied to, if any, and is required + * so that MAC can label the reply appropriately. + */ +struct mbuf * +ipfw_send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, + u_int32_t ack, int flags) +{ + struct mbuf *m = NULL; /* stupid compiler */ + int len, dir; + struct ip *h = NULL; /* stupid compiler */ +#ifdef INET6 + struct ip6_hdr *h6 = NULL; +#endif + struct tcphdr *th = NULL; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + + M_SETFIB(m, id->fib); +#ifdef MAC + if (replyto != NULL) + mac_netinet_firewall_reply(replyto, m); + else + mac_netinet_firewall_send(m); +#else + (void)replyto; /* don't warn about unused arg */ +#endif + + switch (id->addr_type) { + case 4: + len = sizeof(struct ip) + sizeof(struct tcphdr); + break; +#ifdef INET6 + case 6: + len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); + break; +#endif + default: + /* XXX: log me?!? */ + FREE_PKT(m); + return (NULL); + } + dir = ((flags & (TH_SYN | TH_RST)) == TH_SYN); + + m->m_data += max_linkhdr; + m->m_flags |= M_SKIP_FIREWALL; + m->m_pkthdr.len = m->m_len = len; + m->m_pkthdr.rcvif = NULL; + bzero(m->m_data, len); + + switch (id->addr_type) { + case 4: + h = mtod(m, struct ip *); + + /* prepare for checksum */ + h->ip_p = IPPROTO_TCP; + h->ip_len = htons(sizeof(struct tcphdr)); + if (dir) { + h->ip_src.s_addr = htonl(id->src_ip); + h->ip_dst.s_addr = htonl(id->dst_ip); + } else { + h->ip_src.s_addr = htonl(id->dst_ip); + h->ip_dst.s_addr = htonl(id->src_ip); + } + + th = (struct tcphdr *)(h + 1); + break; +#ifdef INET6 + case 6: + h6 = mtod(m, struct ip6_hdr *); + + /* prepare for checksum */ + h6->ip6_nxt = IPPROTO_TCP; + h6->ip6_plen = htons(sizeof(struct tcphdr)); + if (dir) { + h6->ip6_src = id->src_ip6; + h6->ip6_dst = id->dst_ip6; + } else { + h6->ip6_src = id->dst_ip6; + h6->ip6_dst = id->src_ip6; + } + + th = (struct tcphdr *)(h6 + 1); + break; +#endif + } + + if (dir) { + th->th_sport = htons(id->src_port); + th->th_dport = htons(id->dst_port); + } else { + th->th_sport = htons(id->dst_port); + th->th_dport = htons(id->src_port); + } + th->th_off = sizeof(struct tcphdr) >> 2; + + if (flags & TH_RST) { + if (flags & TH_ACK) { + th->th_seq = htonl(ack); + th->th_flags = TH_RST; + } else { + if (flags & TH_SYN) + seq++; + th->th_ack = htonl(seq); + th->th_flags = TH_RST | TH_ACK; + } + } else { + /* + * Keepalive - use caller provided sequence numbers + */ + th->th_seq = htonl(seq); + th->th_ack = htonl(ack); + th->th_flags = TH_ACK; + } + + switch (id->addr_type) { + case 4: + th->th_sum = in_cksum(m, len); + + /* finish the ip header */ + h->ip_v = 4; + h->ip_hl = sizeof(*h) >> 2; + h->ip_tos = IPTOS_LOWDELAY; + h->ip_off = 0; + /* ip_len must be in host format for ip_output */ + h->ip_len = len; + h->ip_ttl = V_ip_defttl; + h->ip_sum = 0; + break; +#ifdef INET6 + case 6: + th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*h6), + sizeof(struct tcphdr)); + + /* finish the ip6 header */ + h6->ip6_vfc |= IPV6_VERSION; + h6->ip6_hlim = IPV6_DEFHLIM; + break; +#endif + } + + return (m); +} + +/* + * This procedure is only used to handle keepalives. It is invoked + * every dyn_keepalive_period + */ +static void +ipfw_tick(void * vnetx) +{ + struct mbuf *m0, *m, *mnext, **mtailp; +#ifdef INET6 + struct mbuf *m6, **m6_tailp; +#endif + int i; + ipfw_dyn_rule *q; +#ifdef VIMAGE + struct vnet *vp = vnetx; +#endif + + CURVNET_SET(vp); + if (V_dyn_keepalive == 0 || V_ipfw_dyn_v == NULL || V_dyn_count == 0) + goto done; + + /* + * We make a chain of packets to go out here -- not deferring + * until after we drop the IPFW dynamic rule lock would result + * in a lock order reversal with the normal packet input -> ipfw + * call stack. + */ + m0 = NULL; + mtailp = &m0; +#ifdef INET6 + m6 = NULL; + m6_tailp = &m6; +#endif + IPFW_DYN_LOCK(); + for (i = 0 ; i < V_curr_dyn_buckets ; i++) { + for (q = V_ipfw_dyn_v[i] ; q ; q = q->next ) { + if (q->dyn_type == O_LIMIT_PARENT) + continue; + if (q->id.proto != IPPROTO_TCP) + continue; + if ( (q->state & BOTH_SYN) != BOTH_SYN) + continue; + if (TIME_LEQ(time_uptime + V_dyn_keepalive_interval, + q->expire)) + continue; /* too early */ + if (TIME_LEQ(q->expire, time_uptime)) + continue; /* too late, rule expired */ + + m = (q->state & ACK_REV) ? NULL : + ipfw_send_pkt(NULL, &(q->id), q->ack_rev - 1, + q->ack_fwd, TH_SYN); + mnext = (q->state & ACK_FWD) ? NULL : + ipfw_send_pkt(NULL, &(q->id), q->ack_fwd - 1, + q->ack_rev, 0); + + switch (q->id.addr_type) { + case 4: + if (m != NULL) { + *mtailp = m; + mtailp = &(*mtailp)->m_nextpkt; + } + if (mnext != NULL) { + *mtailp = mnext; + mtailp = &(*mtailp)->m_nextpkt; + } + break; +#ifdef INET6 + case 6: + if (m != NULL) { + *m6_tailp = m; + m6_tailp = &(*m6_tailp)->m_nextpkt; + } + if (mnext != NULL) { + *m6_tailp = mnext; + m6_tailp = &(*m6_tailp)->m_nextpkt; + } + break; +#endif + } + } + } + IPFW_DYN_UNLOCK(); + for (m = m0; m != NULL; m = mnext) { + mnext = m->m_nextpkt; + m->m_nextpkt = NULL; + ip_output(m, NULL, NULL, 0, NULL, NULL); + } +#ifdef INET6 + for (m = m6; m != NULL; m = mnext) { + mnext = m->m_nextpkt; + m->m_nextpkt = NULL; + ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); + } +#endif +done: + callout_reset_on(&V_ipfw_timeout, V_dyn_keepalive_period * hz, + ipfw_tick, vnetx, 0); + CURVNET_RESTORE(); +} + +void +ipfw_dyn_attach(void) +{ + ipfw_dyn_rule_zone = uma_zcreate("IPFW dynamic rule", + sizeof(ipfw_dyn_rule), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + + IPFW_DYN_LOCK_INIT(); +} + +void +ipfw_dyn_detach(void) +{ + uma_zdestroy(ipfw_dyn_rule_zone); + IPFW_DYN_LOCK_DESTROY(); +} + +void +ipfw_dyn_init(void) +{ + V_ipfw_dyn_v = NULL; + V_dyn_buckets = 256; /* must be power of 2 */ + V_curr_dyn_buckets = 256; /* must be power of 2 */ + + V_dyn_ack_lifetime = 300; + V_dyn_syn_lifetime = 20; + V_dyn_fin_lifetime = 1; + V_dyn_rst_lifetime = 1; + V_dyn_udp_lifetime = 10; + V_dyn_short_lifetime = 5; + + V_dyn_keepalive_interval = 20; + V_dyn_keepalive_period = 5; + V_dyn_keepalive = 1; /* do send keepalives */ + + V_dyn_max = 4096; /* max # of dynamic rules */ + callout_init(&V_ipfw_timeout, CALLOUT_MPSAFE); + callout_reset_on(&V_ipfw_timeout, hz, ipfw_tick, curvnet, 0); +} + +void +ipfw_dyn_uninit(int pass) +{ + if (pass == 0) + callout_drain(&V_ipfw_timeout); + else { + if (V_ipfw_dyn_v != NULL) + free(V_ipfw_dyn_v, M_IPFW); + } +} + +int +ipfw_dyn_len(void) +{ + return (V_ipfw_dyn_v == NULL) ? 0 : + (V_dyn_count * sizeof(ipfw_dyn_rule)); +} + +void +ipfw_get_dynamic(char **pbp, const char *ep) +{ + ipfw_dyn_rule *p, *last = NULL; + char *bp; + int i; + + if (V_ipfw_dyn_v == NULL) + return; + bp = *pbp; + + IPFW_DYN_LOCK(); + for (i = 0 ; i < V_curr_dyn_buckets; i++) + for (p = V_ipfw_dyn_v[i] ; p != NULL; p = p->next) { + if (bp + sizeof *p <= ep) { + ipfw_dyn_rule *dst = + (ipfw_dyn_rule *)bp; + bcopy(p, dst, sizeof *p); + bcopy(&(p->rule->rulenum), &(dst->rule), + sizeof(p->rule->rulenum)); + /* + * store set number into high word of + * dst->rule pointer. + */ + bcopy(&(p->rule->set), + (char *)&dst->rule + + sizeof(p->rule->rulenum), + sizeof(p->rule->set)); + /* + * store a non-null value in "next". + * The userland code will interpret a + * NULL here as a marker + * for the last dynamic rule. + */ + bcopy(&dst, &dst->next, sizeof(dst)); + last = dst; + dst->expire = + TIME_LEQ(dst->expire, time_uptime) ? + 0 : dst->expire - time_uptime ; + bp += sizeof(ipfw_dyn_rule); + } + } + IPFW_DYN_UNLOCK(); + if (last != NULL) /* mark last dynamic rule */ + bzero(&last->next, sizeof(last)); + *pbp = bp; +} +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c new file mode 100644 index 0000000000000..3cbb5fa6c934a --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_log.c @@ -0,0 +1,553 @@ +/*- + * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa + * + * 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 +__FBSDID("$FreeBSD$"); + +/* + * Logging support for ipfw + */ + +#include "opt_ipfw.h" +#include "opt_inet.h" +#ifndef INET +#error IPFIREWALL requires INET. +#endif /* INET */ +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for ETHERTYPE_IP */ +#include +#include +#include +#include /* for IFT_PFLOG */ +#include /* for BPF */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef INET6 +#include /* ip6_sprintf() */ +#endif + +#include + +#ifdef MAC +#include +#endif + +/* + * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T + * Other macros just cast void * into the appropriate type + */ +#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) +#define TCP(p) ((struct tcphdr *)(p)) +#define SCTP(p) ((struct sctphdr *)(p)) +#define UDP(p) ((struct udphdr *)(p)) +#define ICMP(p) ((struct icmphdr *)(p)) +#define ICMP6(p) ((struct icmp6_hdr *)(p)) + +#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 +#define SNP(buf) buf, sizeof(buf) + +#ifdef WITHOUT_BPF +void +ipfw_log_bpf(int onoff) +{ +} +#else /* !WITHOUT_BPF */ +static struct ifnet *log_if; /* hook to attach to bpf */ +static struct rwlock log_if_lock; +#define LOGIF_LOCK_INIT(x) rw_init(&log_if_lock, "ipfw log_if lock") +#define LOGIF_LOCK_DESTROY(x) rw_destroy(&log_if_lock) +#define LOGIF_RLOCK(x) rw_rlock(&log_if_lock) +#define LOGIF_RUNLOCK(x) rw_runlock(&log_if_lock) +#define LOGIF_WLOCK(x) rw_wlock(&log_if_lock) +#define LOGIF_WUNLOCK(x) rw_wunlock(&log_if_lock) + +#define IPFWNAME "ipfw" + +/* we use this dummy function for all ifnet callbacks */ +static int +log_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr) +{ + return EINVAL; +} + +static int +ipfw_log_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct route *ro) +{ + if (m != NULL) + FREE_PKT(m); + return EINVAL; +} + +static void +ipfw_log_start(struct ifnet* ifp) +{ + panic("ipfw_log_start() must not be called"); +} + +static const u_char ipfwbroadcastaddr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int +ipfw_log_clone_match(struct if_clone *ifc, const char *name) +{ + + return (strncmp(name, IPFWNAME, sizeof(IPFWNAME) - 1) == 0); +} + +static int +ipfw_log_clone_create(struct if_clone *ifc, char *name, size_t len, + caddr_t params) +{ + int error; + int unit; + struct ifnet *ifp; + + error = ifc_name2unit(name, &unit); + if (error) + return (error); + + error = ifc_alloc_unit(ifc, &unit); + if (error) + return (error); + + ifp = if_alloc(IFT_PFLOG); + if (ifp == NULL) { + ifc_free_unit(ifc, unit); + return (ENOSPC); + } + ifp->if_dname = IPFWNAME; + ifp->if_dunit = unit; + snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", IPFWNAME, unit); + strlcpy(name, ifp->if_xname, len); + ifp->if_mtu = 65536; + ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = (void *)log_dummy; + ifp->if_ioctl = log_dummy; + ifp->if_start = ipfw_log_start; + ifp->if_output = ipfw_log_output; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 14; + ifp->if_broadcastaddr = ipfwbroadcastaddr; + ifp->if_baudrate = IF_Mbps(10); + + LOGIF_WLOCK(); + if (log_if == NULL) + log_if = ifp; + else { + LOGIF_WUNLOCK(); + if_free(ifp); + ifc_free_unit(ifc, unit); + return (EEXIST); + } + LOGIF_WUNLOCK(); + if_attach(ifp); + bpfattach(ifp, DLT_EN10MB, 14); + + return (0); +} + +static int +ipfw_log_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) +{ + int unit; + + if (ifp == NULL) + return (0); + + LOGIF_WLOCK(); + if (log_if != NULL && ifp == log_if) + log_if = NULL; + else { + LOGIF_WUNLOCK(); + return (EINVAL); + } + LOGIF_WUNLOCK(); + + unit = ifp->if_dunit; + bpfdetach(ifp); + if_detach(ifp); + if_free(ifp); + ifc_free_unit(ifc, unit); + + return (0); +} + +static struct if_clone ipfw_log_cloner = IFC_CLONE_INITIALIZER( + IPFWNAME, NULL, IF_MAXUNIT, + NULL, ipfw_log_clone_match, ipfw_log_clone_create, ipfw_log_clone_destroy); + +void +ipfw_log_bpf(int onoff) +{ + + if (onoff) { + LOGIF_LOCK_INIT(); + if_clone_attach(&ipfw_log_cloner); + } else { + if_clone_detach(&ipfw_log_cloner); + LOGIF_LOCK_DESTROY(); + } +} +#endif /* !WITHOUT_BPF */ + +/* + * We enter here when we have a rule with O_LOG. + * XXX this function alone takes about 2Kbytes of code! + */ +void +ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, + struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg, + struct ip *ip) +{ + char *action; + int limit_reached = 0; + char action2[92], proto[128], fragment[32]; + + if (V_fw_verbose == 0) { +#ifndef WITHOUT_BPF + LOGIF_RLOCK(); + if (log_if == NULL || log_if->if_bpf == NULL) { + LOGIF_RUNLOCK(); + return; + } + + if (args->eh) /* layer2, use orig hdr */ + BPF_MTAP2(log_if, args->eh, ETHER_HDR_LEN, m); + else + /* Add fake header. Later we will store + * more info in the header. + */ + BPF_MTAP2(log_if, "DDDDDDSSSSSS\x08\x00", ETHER_HDR_LEN, m); + LOGIF_RUNLOCK(); +#endif /* !WITHOUT_BPF */ + return; + } + /* the old 'log' function */ + fragment[0] = '\0'; + proto[0] = '\0'; + + if (f == NULL) { /* bogus pkt */ + if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit) + return; + V_norule_counter++; + if (V_norule_counter == V_verbose_limit) + limit_reached = V_verbose_limit; + action = "Refuse"; + } else { /* O_LOG is the first action, find the real one */ + ipfw_insn *cmd = ACTION_PTR(f); + ipfw_insn_log *l = (ipfw_insn_log *)cmd; + + if (l->max_log != 0 && l->log_left == 0) + return; + l->log_left--; + if (l->log_left == 0) + limit_reached = l->max_log; + cmd += F_LEN(cmd); /* point to first action */ + if (cmd->opcode == O_ALTQ) { + ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; + + snprintf(SNPARGS(action2, 0), "Altq %d", + altq->qid); + cmd += F_LEN(cmd); + } + if (cmd->opcode == O_PROB) + cmd += F_LEN(cmd); + + if (cmd->opcode == O_TAG) + cmd += F_LEN(cmd); + + action = action2; + switch (cmd->opcode) { + case O_DENY: + action = "Deny"; + break; + + case O_REJECT: + if (cmd->arg1==ICMP_REJECT_RST) + action = "Reset"; + else if (cmd->arg1==ICMP_UNREACH_HOST) + action = "Reject"; + else + snprintf(SNPARGS(action2, 0), "Unreach %d", + cmd->arg1); + break; + + case O_UNREACH6: + if (cmd->arg1==ICMP6_UNREACH_RST) + action = "Reset"; + else + snprintf(SNPARGS(action2, 0), "Unreach %d", + cmd->arg1); + break; + + case O_ACCEPT: + action = "Accept"; + break; + case O_COUNT: + action = "Count"; + break; + case O_DIVERT: + snprintf(SNPARGS(action2, 0), "Divert %d", + cmd->arg1); + break; + case O_TEE: + snprintf(SNPARGS(action2, 0), "Tee %d", + cmd->arg1); + break; + case O_SETFIB: + snprintf(SNPARGS(action2, 0), "SetFib %d", + cmd->arg1); + break; + case O_SKIPTO: + snprintf(SNPARGS(action2, 0), "SkipTo %d", + cmd->arg1); + break; + case O_PIPE: + snprintf(SNPARGS(action2, 0), "Pipe %d", + cmd->arg1); + break; + case O_QUEUE: + snprintf(SNPARGS(action2, 0), "Queue %d", + cmd->arg1); + break; + case O_FORWARD_IP: { + ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd; + int len; + struct in_addr dummyaddr; + if (sa->sa.sin_addr.s_addr == INADDR_ANY) + dummyaddr.s_addr = htonl(tablearg); + else + dummyaddr.s_addr = sa->sa.sin_addr.s_addr; + + len = snprintf(SNPARGS(action2, 0), "Forward to %s", + inet_ntoa(dummyaddr)); + + if (sa->sa.sin_port) + snprintf(SNPARGS(action2, len), ":%d", + sa->sa.sin_port); + } + break; +#ifdef INET6 + case O_FORWARD_IP6: { + char buf[INET6_ADDRSTRLEN]; + ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd; + int len; + + len = snprintf(SNPARGS(action2, 0), "Forward to [%s]", + ip6_sprintf(buf, &sa->sa.sin6_addr)); + + if (sa->sa.sin6_port) + snprintf(SNPARGS(action2, len), ":%u", + sa->sa.sin6_port); + } + break; +#endif + case O_NETGRAPH: + snprintf(SNPARGS(action2, 0), "Netgraph %d", + cmd->arg1); + break; + case O_NGTEE: + snprintf(SNPARGS(action2, 0), "Ngtee %d", + cmd->arg1); + break; + case O_NAT: + action = "Nat"; + break; + case O_REASS: + action = "Reass"; + break; + case O_CALLRETURN: + if (cmd->len & F_NOT) + action = "Return"; + else + snprintf(SNPARGS(action2, 0), "Call %d", + cmd->arg1); + break; + default: + action = "UNKNOWN"; + break; + } + } + + if (hlen == 0) { /* non-ip */ + snprintf(SNPARGS(proto, 0), "MAC"); + + } else { + int len; +#ifdef INET6 + char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2]; +#else + char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; +#endif + struct icmphdr *icmp; + struct tcphdr *tcp; + struct udphdr *udp; +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; + struct icmp6_hdr *icmp6; + u_short ip6f_mf; +#endif + src[0] = '\0'; + dst[0] = '\0'; +#ifdef INET6 + ip6f_mf = offset & IP6F_MORE_FRAG; + offset &= IP6F_OFF_MASK; + + if (IS_IP6_FLOW_ID(&(args->f_id))) { + char ip6buf[INET6_ADDRSTRLEN]; + snprintf(src, sizeof(src), "[%s]", + ip6_sprintf(ip6buf, &args->f_id.src_ip6)); + snprintf(dst, sizeof(dst), "[%s]", + ip6_sprintf(ip6buf, &args->f_id.dst_ip6)); + + ip6 = (struct ip6_hdr *)ip; + tcp = (struct tcphdr *)(((char *)ip) + hlen); + udp = (struct udphdr *)(((char *)ip) + hlen); + } else +#endif + { + tcp = L3HDR(struct tcphdr, ip); + udp = L3HDR(struct udphdr, ip); + + inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src)); + inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst)); + } + + switch (args->f_id.proto) { + case IPPROTO_TCP: + len = snprintf(SNPARGS(proto, 0), "TCP %s", src); + if (offset == 0) + snprintf(SNPARGS(proto, len), ":%d %s:%d", + ntohs(tcp->th_sport), + dst, + ntohs(tcp->th_dport)); + else + snprintf(SNPARGS(proto, len), " %s", dst); + break; + + case IPPROTO_UDP: + len = snprintf(SNPARGS(proto, 0), "UDP %s", src); + if (offset == 0) + snprintf(SNPARGS(proto, len), ":%d %s:%d", + ntohs(udp->uh_sport), + dst, + ntohs(udp->uh_dport)); + else + snprintf(SNPARGS(proto, len), " %s", dst); + break; + + case IPPROTO_ICMP: + icmp = L3HDR(struct icmphdr, ip); + if (offset == 0) + len = snprintf(SNPARGS(proto, 0), + "ICMP:%u.%u ", + icmp->icmp_type, icmp->icmp_code); + else + len = snprintf(SNPARGS(proto, 0), "ICMP "); + len += snprintf(SNPARGS(proto, len), "%s", src); + snprintf(SNPARGS(proto, len), " %s", dst); + break; +#ifdef INET6 + case IPPROTO_ICMPV6: + icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen); + if (offset == 0) + len = snprintf(SNPARGS(proto, 0), + "ICMPv6:%u.%u ", + icmp6->icmp6_type, icmp6->icmp6_code); + else + len = snprintf(SNPARGS(proto, 0), "ICMPv6 "); + len += snprintf(SNPARGS(proto, len), "%s", src); + snprintf(SNPARGS(proto, len), " %s", dst); + break; +#endif + default: + len = snprintf(SNPARGS(proto, 0), "P:%d %s", + args->f_id.proto, src); + snprintf(SNPARGS(proto, len), " %s", dst); + break; + } + +#ifdef INET6 + if (IS_IP6_FLOW_ID(&(args->f_id))) { + if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG)) + snprintf(SNPARGS(fragment, 0), + " (frag %08x:%d@%d%s)", + args->f_id.extra, + ntohs(ip6->ip6_plen) - hlen, + ntohs(offset) << 3, ip6f_mf ? "+" : ""); + } else +#endif + { + int ipoff, iplen; + ipoff = ntohs(ip->ip_off); + iplen = ntohs(ip->ip_len); + if (ipoff & (IP_MF | IP_OFFMASK)) + snprintf(SNPARGS(fragment, 0), + " (frag %d:%d@%d%s)", + ntohs(ip->ip_id), iplen - (ip->ip_hl << 2), + offset << 3, + (ipoff & IP_MF) ? "+" : ""); + } + } +#ifdef __FreeBSD__ + if (oif || m->m_pkthdr.rcvif) + log(LOG_SECURITY | LOG_INFO, + "ipfw: %d %s %s %s via %s%s\n", + f ? f->rulenum : -1, + action, proto, oif ? "out" : "in", + oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname, + fragment); + else +#endif + log(LOG_SECURITY | LOG_INFO, + "ipfw: %d %s %s [no if info]%s\n", + f ? f->rulenum : -1, + action, proto, fragment); + if (limit_reached) + log(LOG_SECURITY | LOG_NOTICE, + "ipfw: limit %d reached on entry %d\n", + limit_reached, f ? f->rulenum : -1); +} +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c new file mode 100644 index 0000000000000..6318633195fd6 --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -0,0 +1,662 @@ +/*- + * Copyright (c) 2008 Paolo Pisati + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include /* XXX for in_cksum */ + +static VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); +#define V_ifaddr_event_tag VNET(ifaddr_event_tag) + +static void +ifaddr_change(void *arg __unused, struct ifnet *ifp) +{ + struct cfg_nat *ptr; + struct ifaddr *ifa; + struct ip_fw_chain *chain; + + chain = &V_layer3_chain; + IPFW_WLOCK(chain); + /* Check every nat entry... */ + LIST_FOREACH(ptr, &chain->nat, _next) { + /* ...using nic 'ifp->if_xname' as dynamic alias address. */ + if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) + continue; + if_addr_rlock(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + ptr->ip = ((struct sockaddr_in *) + (ifa->ifa_addr))->sin_addr; + LibAliasSetAddress(ptr->lib, ptr->ip); + } + if_addr_runlock(ifp); + } + IPFW_WUNLOCK(chain); +} + +/* + * delete the pointers for nat entry ix, or all of them if ix < 0 + */ +static void +flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) +{ + int i; + ipfw_insn_nat *cmd; + + IPFW_WLOCK_ASSERT(chain); + for (i = 0; i < chain->n_rules; i++) { + cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); + /* XXX skip log and the like ? */ + if (cmd->o.opcode == O_NAT && cmd->nat != NULL && + (ix < 0 || cmd->nat->id == ix)) + cmd->nat = NULL; + } +} + +static void +del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) +{ + struct cfg_redir *r, *tmp_r; + struct cfg_spool *s, *tmp_s; + int i, num; + + LIST_FOREACH_SAFE(r, head, _next, tmp_r) { + num = 1; /* Number of alias_link to delete. */ + switch (r->mode) { + case REDIR_PORT: + num = r->pport_cnt; + /* FALLTHROUGH */ + case REDIR_ADDR: + case REDIR_PROTO: + /* Delete all libalias redirect entry. */ + for (i = 0; i < num; i++) + LibAliasRedirectDelete(n->lib, r->alink[i]); + /* Del spool cfg if any. */ + LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { + LIST_REMOVE(s, _next); + free(s, M_IPFW); + } + free(r->alink, M_IPFW); + LIST_REMOVE(r, _next); + free(r, M_IPFW); + break; + default: + printf("unknown redirect mode: %u\n", r->mode); + /* XXX - panic?!?!? */ + break; + } + } +} + +static void +add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) +{ + struct cfg_redir *r, *ser_r; + struct cfg_spool *s, *ser_s; + int cnt, off, i; + + for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { + ser_r = (struct cfg_redir *)&buf[off]; + r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); + memcpy(r, ser_r, SOF_REDIR); + LIST_INIT(&r->spool_chain); + off += SOF_REDIR; + r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, + M_IPFW, M_WAITOK | M_ZERO); + switch (r->mode) { + case REDIR_ADDR: + r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, + r->paddr); + break; + case REDIR_PORT: + for (i = 0 ; i < r->pport_cnt; i++) { + /* If remotePort is all ports, set it to 0. */ + u_short remotePortCopy = r->rport + i; + if (r->rport_cnt == 1 && r->rport == 0) + remotePortCopy = 0; + r->alink[i] = LibAliasRedirectPort(ptr->lib, + r->laddr, htons(r->lport + i), r->raddr, + htons(remotePortCopy), r->paddr, + htons(r->pport + i), r->proto); + if (r->alink[i] == NULL) { + r->alink[0] = NULL; + break; + } + } + break; + case REDIR_PROTO: + r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, + r->raddr, r->paddr, r->proto); + break; + default: + printf("unknown redirect mode: %u\n", r->mode); + break; + } + /* XXX perhaps return an error instead of panic ? */ + if (r->alink[0] == NULL) + panic("LibAliasRedirect* returned NULL"); + /* LSNAT handling. */ + for (i = 0; i < r->spool_cnt; i++) { + ser_s = (struct cfg_spool *)&buf[off]; + s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); + memcpy(s, ser_s, SOF_SPOOL); + LibAliasAddServer(ptr->lib, r->alink[0], + s->addr, htons(s->port)); + off += SOF_SPOOL; + /* Hook spool entry. */ + LIST_INSERT_HEAD(&r->spool_chain, s, _next); + } + /* And finally hook this redir entry. */ + LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); + } +} + +static int +ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) +{ + struct mbuf *mcl; + struct ip *ip; + /* XXX - libalias duct tape */ + int ldt, retval, found; + struct ip_fw_chain *chain; + char *c; + + ldt = 0; + retval = 0; + mcl = m_megapullup(m, m->m_pkthdr.len); + if (mcl == NULL) { + args->m = NULL; + return (IP_FW_DENY); + } + ip = mtod(mcl, struct ip *); + + /* + * XXX - Libalias checksum offload 'duct tape': + * + * locally generated packets have only pseudo-header checksum + * calculated and libalias will break it[1], so mark them for + * later fix. Moreover there are cases when libalias modifies + * tcp packet data[2], mark them for later fix too. + * + * [1] libalias was never meant to run in kernel, so it does + * not have any knowledge about checksum offloading, and + * expects a packet with a full internet checksum. + * Unfortunately, packets generated locally will have just the + * pseudo header calculated, and when libalias tries to adjust + * the checksum it will actually compute a wrong value. + * + * [2] when libalias modifies tcp's data content, full TCP + * checksum has to be recomputed: the problem is that + * libalias does not have any idea about checksum offloading. + * To work around this, we do not do checksumming in LibAlias, + * but only mark the packets in th_x2 field. If we receive a + * marked packet, we calculate correct checksum for it + * aware of offloading. Why such a terrible hack instead of + * recalculating checksum for each packet? + * Because the previous checksum was not checked! + * Recalculating checksums for EVERY packet will hide ALL + * transmission errors. Yes, marked packets still suffer from + * this problem. But, sigh, natd(8) has this problem, too. + * + * TODO: -make libalias mbuf aware (so + * it can handle delayed checksum and tso) + */ + + if (mcl->m_pkthdr.rcvif == NULL && + mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) + ldt = 1; + + c = mtod(mcl, char *); + + /* Check if this is 'global' instance */ + if (t == NULL) { + if (args->oif == NULL) { + /* Wrong direction, skip processing */ + args->m = mcl; + return (IP_FW_NAT); + } + + found = 0; + chain = &V_layer3_chain; + IPFW_RLOCK(chain); + /* Check every nat entry... */ + LIST_FOREACH(t, &chain->nat, _next) { + if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) + continue; + retval = LibAliasOutTry(t->lib, c, + mcl->m_len + M_TRAILINGSPACE(mcl), 0); + if (retval == PKT_ALIAS_OK) { + /* Nat instance recognises state */ + found = 1; + break; + } + } + IPFW_RUNLOCK(chain); + if (found != 1) { + /* No instance found, return ignore */ + args->m = mcl; + return (IP_FW_NAT); + } + } else { + if (args->oif == NULL) + retval = LibAliasIn(t->lib, c, + mcl->m_len + M_TRAILINGSPACE(mcl)); + else + retval = LibAliasOut(t->lib, c, + mcl->m_len + M_TRAILINGSPACE(mcl)); + } + + /* + * We drop packet when: + * 1. libalias returns PKT_ALIAS_ERROR; + * 2. For incoming packets: + * a) for unresolved fragments; + * b) libalias returns PKT_ALIAS_IGNORED and + * PKT_ALIAS_DENY_INCOMING flag is set. + */ + if (retval == PKT_ALIAS_ERROR || + (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || + (retval == PKT_ALIAS_IGNORED && + (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { + /* XXX - should i add some logging? */ + m_free(mcl); + args->m = NULL; + return (IP_FW_DENY); + } + + if (retval == PKT_ALIAS_RESPOND) + mcl->m_flags |= M_SKIP_FIREWALL; + mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); + + /* + * XXX - libalias checksum offload + * 'duct tape' (see above) + */ + + if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && + ip->ip_p == IPPROTO_TCP) { + struct tcphdr *th; + + th = (struct tcphdr *)(ip + 1); + if (th->th_x2) + ldt = 1; + } + + if (ldt) { + struct tcphdr *th; + struct udphdr *uh; + u_short cksum; + + ip->ip_len = ntohs(ip->ip_len); + cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))); + + switch (ip->ip_p) { + case IPPROTO_TCP: + th = (struct tcphdr *)(ip + 1); + /* + * Maybe it was set in + * libalias... + */ + th->th_x2 = 0; + th->th_sum = cksum; + mcl->m_pkthdr.csum_data = + offsetof(struct tcphdr, th_sum); + break; + case IPPROTO_UDP: + uh = (struct udphdr *)(ip + 1); + uh->uh_sum = cksum; + mcl->m_pkthdr.csum_data = + offsetof(struct udphdr, uh_sum); + break; + } + /* No hw checksum offloading: do it ourselves */ + if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { + in_delayed_cksum(mcl); + mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + } + ip->ip_len = htons(ip->ip_len); + } + args->m = mcl; + return (IP_FW_NAT); +} + +static struct cfg_nat * +lookup_nat(struct nat_list *l, int nat_id) +{ + struct cfg_nat *res; + + LIST_FOREACH(res, l, _next) { + if (res->id == nat_id) + break; + } + return res; +} + +static int +ipfw_nat_cfg(struct sockopt *sopt) +{ + struct cfg_nat *cfg, *ptr; + char *buf; + struct ip_fw_chain *chain = &V_layer3_chain; + size_t len; + int gencnt, error = 0; + + len = sopt->sopt_valsize; + buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO); + if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0) + goto out; + + cfg = (struct cfg_nat *)buf; + if (cfg->id < 0) { + error = EINVAL; + goto out; + } + + /* + * Find/create nat rule. + */ + IPFW_WLOCK(chain); + gencnt = chain->gencnt; + ptr = lookup_nat(&chain->nat, cfg->id); + if (ptr == NULL) { + IPFW_WUNLOCK(chain); + /* New rule: allocate and init new instance. */ + ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); + ptr->lib = LibAliasInit(NULL); + LIST_INIT(&ptr->redir_chain); + } else { + /* Entry already present: temporarily unhook it. */ + LIST_REMOVE(ptr, _next); + flush_nat_ptrs(chain, cfg->id); + IPFW_WUNLOCK(chain); + } + + /* + * Basic nat configuration. + */ + ptr->id = cfg->id; + /* + * XXX - what if this rule doesn't nat any ip and just + * redirect? + * do we set aliasaddress to 0.0.0.0? + */ + ptr->ip = cfg->ip; + ptr->redir_cnt = cfg->redir_cnt; + ptr->mode = cfg->mode; + LibAliasSetMode(ptr->lib, cfg->mode, cfg->mode); + LibAliasSetAddress(ptr->lib, ptr->ip); + memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE); + + /* + * Redir and LSNAT configuration. + */ + /* Delete old cfgs. */ + del_redir_spool_cfg(ptr, &ptr->redir_chain); + /* Add new entries. */ + add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); + + IPFW_WLOCK(chain); + /* Extra check to avoid race with another ipfw_nat_cfg() */ + if (gencnt != chain->gencnt && + ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL)) + LIST_REMOVE(cfg, _next); + LIST_INSERT_HEAD(&chain->nat, ptr, _next); + chain->gencnt++; + IPFW_WUNLOCK(chain); + +out: + free(buf, M_TEMP); + return (error); +} + +static int +ipfw_nat_del(struct sockopt *sopt) +{ + struct cfg_nat *ptr; + struct ip_fw_chain *chain = &V_layer3_chain; + int i; + + sooptcopyin(sopt, &i, sizeof i, sizeof i); + /* XXX validate i */ + IPFW_WLOCK(chain); + ptr = lookup_nat(&chain->nat, i); + if (ptr == NULL) { + IPFW_WUNLOCK(chain); + return (EINVAL); + } + LIST_REMOVE(ptr, _next); + flush_nat_ptrs(chain, i); + IPFW_WUNLOCK(chain); + del_redir_spool_cfg(ptr, &ptr->redir_chain); + LibAliasUninit(ptr->lib); + free(ptr, M_IPFW); + return (0); +} + +static int +ipfw_nat_get_cfg(struct sockopt *sopt) +{ + struct ip_fw_chain *chain = &V_layer3_chain; + struct cfg_nat *n; + struct cfg_redir *r; + struct cfg_spool *s; + char *data; + int gencnt, nat_cnt, len, error; + + nat_cnt = 0; + len = sizeof(nat_cnt); + + IPFW_RLOCK(chain); +retry: + gencnt = chain->gencnt; + /* Estimate memory amount */ + LIST_FOREACH(n, &chain->nat, _next) { + nat_cnt++; + len += sizeof(struct cfg_nat); + LIST_FOREACH(r, &n->redir_chain, _next) { + len += sizeof(struct cfg_redir); + LIST_FOREACH(s, &r->spool_chain, _next) + len += sizeof(struct cfg_spool); + } + } + IPFW_RUNLOCK(chain); + + data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); + bcopy(&nat_cnt, data, sizeof(nat_cnt)); + + nat_cnt = 0; + len = sizeof(nat_cnt); + + IPFW_RLOCK(chain); + if (gencnt != chain->gencnt) { + free(data, M_TEMP); + goto retry; + } + /* Serialize all the data. */ + LIST_FOREACH(n, &chain->nat, _next) { + bcopy(n, &data[len], sizeof(struct cfg_nat)); + len += sizeof(struct cfg_nat); + LIST_FOREACH(r, &n->redir_chain, _next) { + bcopy(r, &data[len], sizeof(struct cfg_redir)); + len += sizeof(struct cfg_redir); + LIST_FOREACH(s, &r->spool_chain, _next) { + bcopy(s, &data[len], sizeof(struct cfg_spool)); + len += sizeof(struct cfg_spool); + } + } + } + IPFW_RUNLOCK(chain); + + error = sooptcopyout(sopt, data, len); + free(data, M_TEMP); + + return (error); +} + +static int +ipfw_nat_get_log(struct sockopt *sopt) +{ + uint8_t *data; + struct cfg_nat *ptr; + int i, size; + struct ip_fw_chain *chain; + + chain = &V_layer3_chain; + + IPFW_RLOCK(chain); + /* one pass to count, one to copy the data */ + i = 0; + LIST_FOREACH(ptr, &chain->nat, _next) { + if (ptr->lib->logDesc == NULL) + continue; + i++; + } + size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); + data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); + if (data == NULL) { + IPFW_RUNLOCK(chain); + return (ENOSPC); + } + i = 0; + LIST_FOREACH(ptr, &chain->nat, _next) { + if (ptr->lib->logDesc == NULL) + continue; + bcopy(&ptr->id, &data[i], sizeof(int)); + i += sizeof(int); + bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); + i += LIBALIAS_BUF_SIZE; + } + IPFW_RUNLOCK(chain); + sooptcopyout(sopt, data, size); + free(data, M_IPFW); + return(0); +} + +static void +ipfw_nat_init(void) +{ + + IPFW_WLOCK(&V_layer3_chain); + /* init ipfw hooks */ + ipfw_nat_ptr = ipfw_nat; + lookup_nat_ptr = lookup_nat; + ipfw_nat_cfg_ptr = ipfw_nat_cfg; + ipfw_nat_del_ptr = ipfw_nat_del; + ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; + ipfw_nat_get_log_ptr = ipfw_nat_get_log; + IPFW_WUNLOCK(&V_layer3_chain); + V_ifaddr_event_tag = EVENTHANDLER_REGISTER( + ifaddr_event, ifaddr_change, + NULL, EVENTHANDLER_PRI_ANY); +} + +static void +ipfw_nat_destroy(void) +{ + struct cfg_nat *ptr, *ptr_temp; + struct ip_fw_chain *chain; + + chain = &V_layer3_chain; + IPFW_WLOCK(chain); + LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { + LIST_REMOVE(ptr, _next); + del_redir_spool_cfg(ptr, &ptr->redir_chain); + LibAliasUninit(ptr->lib); + free(ptr, M_IPFW); + } + EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); + flush_nat_ptrs(chain, -1 /* flush all */); + /* deregister ipfw_nat */ + ipfw_nat_ptr = NULL; + lookup_nat_ptr = NULL; + ipfw_nat_cfg_ptr = NULL; + ipfw_nat_del_ptr = NULL; + ipfw_nat_get_cfg_ptr = NULL; + ipfw_nat_get_log_ptr = NULL; + IPFW_WUNLOCK(chain); +} + +static int +ipfw_nat_modevent(module_t mod, int type, void *unused) +{ + int err = 0; + + switch (type) { + case MOD_LOAD: + ipfw_nat_init(); + break; + + case MOD_UNLOAD: + ipfw_nat_destroy(); + break; + + default: + return EOPNOTSUPP; + break; + } + return err; +} + +static moduledata_t ipfw_nat_mod = { + "ipfw_nat", + ipfw_nat_modevent, + 0 +}; + +DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); +MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); +MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); +MODULE_VERSION(ipfw_nat, 1); +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c new file mode 100644 index 0000000000000..4ab9316b5e8ba --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -0,0 +1,590 @@ +/*- + * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_ipfw.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#ifndef INET +#error IPFIREWALL requires INET. +#endif /* INET */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef INET6 +#include +#include +#endif + +#include + +#include + +#include + +static VNET_DEFINE(int, fw_enable) = 1; +#define V_fw_enable VNET(fw_enable) + +#ifdef INET6 +static VNET_DEFINE(int, fw6_enable) = 1; +#define V_fw6_enable VNET(fw6_enable) +#endif + +static VNET_DEFINE(int, fwlink_enable) = 0; +#define V_fwlink_enable VNET(fwlink_enable) + +int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); + +/* Forward declarations. */ +static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int); +static int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int, + struct inpcb *); +static int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int, + struct inpcb *); + +#ifdef SYSCTL_NODE + +SYSBEGIN(f1) + +SYSCTL_DECL(_net_inet_ip_fw); +SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, enable, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_enable), 0, + ipfw_chg_hook, "I", "Enable ipfw"); +#ifdef INET6 +SYSCTL_DECL(_net_inet6_ip6_fw); +SYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_AUTO, enable, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw6_enable), 0, + ipfw_chg_hook, "I", "Enable ipfw+6"); +#endif /* INET6 */ + +SYSCTL_DECL(_net_link_ether); +SYSCTL_VNET_PROC(_net_link_ether, OID_AUTO, ipfw, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fwlink_enable), 0, + ipfw_chg_hook, "I", "Pass ether pkts through firewall"); + +SYSEND + +#endif /* SYSCTL_NODE */ + +/* + * The pfilter hook to pass packets to ipfw_chk and then to + * dummynet, divert, netgraph or other modules. + * The packet may be consumed. + */ +static int +ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, + struct inpcb *inp) +{ + struct ip_fw_args args; + struct m_tag *tag; + int ipfw; + int ret; + + /* all the processing now uses ip_len in net format */ + if (mtod(*m0, struct ip *)->ip_v == 4) + SET_NET_IPLEN(mtod(*m0, struct ip *)); + + /* convert dir to IPFW values */ + dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; + bzero(&args, sizeof(args)); + +again: + /* + * extract and remove the tag if present. If we are left + * with onepass, optimize the outgoing path. + */ + tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); + if (tag != NULL) { + args.rule = *((struct ipfw_rule_ref *)(tag+1)); + m_tag_delete(*m0, tag); + if (args.rule.info & IPFW_ONEPASS) { + if (mtod(*m0, struct ip *)->ip_v == 4) + SET_HOST_IPLEN(mtod(*m0, struct ip *)); + return (0); + } + } + + args.m = *m0; + args.oif = dir == DIR_OUT ? ifp : NULL; + args.inp = inp; + + ipfw = ipfw_chk(&args); + *m0 = args.m; + + KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", + __func__)); + + /* breaking out of the switch means drop */ + ret = 0; /* default return value for pass */ + switch (ipfw) { + case IP_FW_PASS: + /* next_hop may be set by ipfw_chk */ + if (args.next_hop == NULL && args.next_hop6 == NULL) + break; /* pass */ +#if !defined(IPFIREWALL_FORWARD) || (!defined(INET6) && !defined(INET)) + ret = EACCES; +#else + { + struct m_tag *fwd_tag; + size_t len; + + KASSERT(args.next_hop == NULL || args.next_hop6 == NULL, + ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__, + args.next_hop, args.next_hop6)); +#ifdef INET6 + if (args.next_hop6 != NULL) + len = sizeof(struct sockaddr_in6); +#endif +#ifdef INET + if (args.next_hop != NULL) + len = sizeof(struct sockaddr_in); +#endif + + /* Incoming packets should not be tagged so we do not + * m_tag_find. Outgoing packets may be tagged, so we + * reuse the tag if present. + */ + fwd_tag = (dir == DIR_IN) ? NULL : + m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag != NULL) { + m_tag_unlink(*m0, fwd_tag); + } else { + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, + M_NOWAIT); + if (fwd_tag == NULL) { + ret = EACCES; + break; /* i.e. drop */ + } + } +#ifdef INET6 + if (args.next_hop6 != NULL) { + bcopy(args.next_hop6, (fwd_tag+1), len); + if (in6_localip(&args.next_hop6->sin6_addr)) + (*m0)->m_flags |= M_FASTFWD_OURS; + } +#endif +#ifdef INET + if (args.next_hop != NULL) { + bcopy(args.next_hop, (fwd_tag+1), len); + if (in_localip(args.next_hop->sin_addr)) + (*m0)->m_flags |= M_FASTFWD_OURS; + } +#endif + m_tag_prepend(*m0, fwd_tag); + } +#endif /* IPFIREWALL_FORWARD */ + break; + + case IP_FW_DENY: + ret = EACCES; + break; /* i.e. drop */ + + case IP_FW_DUMMYNET: + ret = EACCES; + if (ip_dn_io_ptr == NULL) + break; /* i.e. drop */ + if (mtod(*m0, struct ip *)->ip_v == 4) + ret = ip_dn_io_ptr(m0, dir, &args); + else if (mtod(*m0, struct ip *)->ip_v == 6) + ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); + else + break; /* drop it */ + /* + * XXX should read the return value. + * dummynet normally eats the packet and sets *m0=NULL + * unless the packet can be sent immediately. In this + * case args is updated and we should re-run the + * check without clearing args. + */ + if (*m0 != NULL) + goto again; + break; + + case IP_FW_TEE: + case IP_FW_DIVERT: + if (ip_divert_ptr == NULL) { + ret = EACCES; + break; /* i.e. drop */ + } + ret = ipfw_divert(m0, dir, &args.rule, + (ipfw == IP_FW_TEE) ? 1 : 0); + /* continue processing for the original packet (tee). */ + if (*m0) + goto again; + break; + + case IP_FW_NGTEE: + case IP_FW_NETGRAPH: + if (ng_ipfw_input_p == NULL) { + ret = EACCES; + break; /* i.e. drop */ + } + ret = ng_ipfw_input_p(m0, dir, &args, + (ipfw == IP_FW_NGTEE) ? 1 : 0); + if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ + goto again; /* continue with packet */ + break; + + case IP_FW_NAT: + /* honor one-pass in case of successful nat */ + if (V_fw_one_pass) + break; /* ret is already 0 */ + goto again; + + case IP_FW_REASS: + goto again; /* continue with packet */ + + default: + KASSERT(0, ("%s: unknown retval", __func__)); + } + + if (ret != 0) { + if (*m0) + FREE_PKT(*m0); + *m0 = NULL; + } + if (*m0 && mtod(*m0, struct ip *)->ip_v == 4) + SET_HOST_IPLEN(mtod(*m0, struct ip *)); + return ret; +} + +/* + * ipfw processing for ethernet packets (in and out). + * Inteface is NULL from ether_demux, and ifp from + * ether_output_frame. + */ +static int +ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, + struct inpcb *inp) +{ + struct ether_header *eh; + struct ether_header save_eh; + struct mbuf *m; + int i, ret; + struct ip_fw_args args; + struct m_tag *mtag; + + /* fetch start point from rule, if any */ + mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); + if (mtag == NULL) { + args.rule.slot = 0; + } else { + /* dummynet packet, already partially processed */ + struct ipfw_rule_ref *r; + + /* XXX can we free it after use ? */ + mtag->m_tag_id = PACKET_TAG_NONE; + r = (struct ipfw_rule_ref *)(mtag + 1); + if (r->info & IPFW_ONEPASS) + return (0); + args.rule = *r; + } + + /* I need some amt of data to be contiguous */ + m = *m0; + i = min(m->m_pkthdr.len, max_protohdr); + if (m->m_len < i) { + m = m_pullup(m, i); + if (m == NULL) { + *m0 = m; + return (0); + } + } + eh = mtod(m, struct ether_header *); + save_eh = *eh; /* save copy for restore below */ + m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ + + args.m = m; /* the packet we are looking at */ + args.oif = dst; /* destination, if any */ + args.next_hop = NULL; /* we do not support forward yet */ + args.next_hop6 = NULL; /* we do not support forward yet */ + args.eh = &save_eh; /* MAC header for bridged/MAC packets */ + args.inp = NULL; /* used by ipfw uid/gid/jail rules */ + i = ipfw_chk(&args); + m = args.m; + if (m != NULL) { + /* + * Restore Ethernet header, as needed, in case the + * mbuf chain was replaced by ipfw. + */ + M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); + if (m == NULL) { + *m0 = NULL; + return (0); + } + if (eh != mtod(m, struct ether_header *)) + bcopy(&save_eh, mtod(m, struct ether_header *), + ETHER_HDR_LEN); + } + *m0 = m; + + ret = 0; + /* Check result of ipfw_chk() */ + switch (i) { + case IP_FW_PASS: + break; + + case IP_FW_DENY: + ret = EACCES; + break; /* i.e. drop */ + + case IP_FW_DUMMYNET: + ret = EACCES; + int dir; + + if (ip_dn_io_ptr == NULL) + break; /* i.e. drop */ + + *m0 = NULL; + dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN); + ip_dn_io_ptr(&m, dir, &args); + return 0; + + default: + KASSERT(0, ("%s: unknown retval", __func__)); + } + + if (ret != 0) { + if (*m0) + FREE_PKT(*m0); + *m0 = NULL; + } + + return ret; +} + +/* do the divert, return 1 on error 0 on success */ +static int +ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, + int tee) +{ + /* + * ipfw_chk() has already tagged the packet with the divert tag. + * If tee is set, copy packet and return original. + * If not tee, consume packet and send it to divert socket. + */ + struct mbuf *clone; + struct ip *ip = mtod(*m0, struct ip *); + struct m_tag *tag; + + /* Cloning needed for tee? */ + if (tee == 0) { + clone = *m0; /* use the original mbuf */ + *m0 = NULL; + } else { + clone = m_dup(*m0, M_DONTWAIT); + /* If we cannot duplicate the mbuf, we sacrifice the divert + * chain and continue with the tee-ed packet. + */ + if (clone == NULL) + return 1; + } + + /* + * Divert listeners can normally handle non-fragmented packets, + * but we can only reass in the non-tee case. + * This means that listeners on a tee rule may get fragments, + * and have to live with that. + * Note that we now have the 'reass' ipfw option so if we care + * we can do it before a 'tee'. + */ + if (!tee) switch (ip->ip_v) { + case IPVERSION: + if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { + int hlen; + struct mbuf *reass; + + SET_HOST_IPLEN(ip); /* ip_reass wants host order */ + reass = ip_reass(clone); /* Reassemble packet. */ + if (reass == NULL) + return 0; /* not an error */ + /* if reass = NULL then it was consumed by ip_reass */ + /* + * IP header checksum fixup after reassembly and leave header + * in network byte order. + */ + ip = mtod(reass, struct ip *); + hlen = ip->ip_hl << 2; + SET_NET_IPLEN(ip); + ip->ip_sum = 0; + if (hlen == sizeof(struct ip)) + ip->ip_sum = in_cksum_hdr(ip); + else + ip->ip_sum = in_cksum(reass, hlen); + clone = reass; + } + break; +#ifdef INET6 + case IPV6_VERSION >> 4: + { + struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *); + + if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { + int nxt, off; + + off = sizeof(struct ip6_hdr); + nxt = frag6_input(&clone, &off, 0); + if (nxt == IPPROTO_DONE) + return (0); + } + break; + } +#endif + } + + /* attach a tag to the packet with the reinject info */ + tag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT); + if (tag == NULL) { + FREE_PKT(clone); + return 1; + } + *((struct ipfw_rule_ref *)(tag+1)) = *rule; + m_tag_prepend(clone, tag); + + /* Do the dirty job... */ + ip_divert_ptr(clone, incoming); + return 0; +} + +/* + * attach or detach hooks for a given protocol family + */ +static int +ipfw_hook(int onoff, int pf) +{ + struct pfil_head *pfh; + void *hook_func; + + pfh = pfil_head_get(PFIL_TYPE_AF, pf); + if (pfh == NULL) + return ENOENT; + + hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet; + + (void) (onoff ? pfil_add_hook : pfil_remove_hook) + (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); + + return 0; +} + +int +ipfw_attach_hooks(int arg) +{ + int error = 0; + + if (arg == 0) /* detach */ + ipfw_hook(0, AF_INET); + else if (V_fw_enable && ipfw_hook(1, AF_INET) != 0) { + error = ENOENT; /* see ip_fw_pfil.c::ipfw_hook() */ + printf("ipfw_hook() error\n"); + } +#ifdef INET6 + if (arg == 0) /* detach */ + ipfw_hook(0, AF_INET6); + else if (V_fw6_enable && ipfw_hook(1, AF_INET6) != 0) { + error = ENOENT; + printf("ipfw6_hook() error\n"); + } +#endif + if (arg == 0) /* detach */ + ipfw_hook(0, AF_LINK); + else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) { + error = ENOENT; + printf("ipfw_link_hook() error\n"); + } + return error; +} + +int +ipfw_chg_hook(SYSCTL_HANDLER_ARGS) +{ + int *enable; + int newval; + int error; + int af; + + if (arg1 == &VNET_NAME(fw_enable)) { + enable = &V_fw_enable; + af = AF_INET; + } +#ifdef INET6 + else if (arg1 == &VNET_NAME(fw6_enable)) { + enable = &V_fw6_enable; + af = AF_INET6; + } +#endif + else if (arg1 == &VNET_NAME(fwlink_enable)) { + enable = &V_fwlink_enable; + af = AF_LINK; + } + else + return (EINVAL); + + newval = *enable; + + /* Handle sysctl change */ + error = sysctl_handle_int(oidp, &newval, 0, req); + + if (error) + return (error); + + /* Formalize new value */ + newval = (newval) ? 1 : 0; + + if (*enable == newval) + return (0); + + error = ipfw_hook(newval, af); + if (error) + return (error); + *enable = newval; + + return (0); +} +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h new file mode 100644 index 0000000000000..fb13a7228d9eb --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -0,0 +1,309 @@ +/*- + * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _IPFW2_PRIVATE_H +#define _IPFW2_PRIVATE_H + +/* + * Internal constants and data structures used by ipfw components + * and not meant to be exported outside the kernel. + */ + +#ifdef _KERNEL + +/* + * For platforms that do not have SYSCTL support, we wrap the + * SYSCTL_* into a function (one per file) to collect the values + * into an array at module initialization. The wrapping macros, + * SYSBEGIN() and SYSEND, are empty in the default case. + */ +#ifndef SYSBEGIN +#define SYSBEGIN(x) +#endif +#ifndef SYSEND +#define SYSEND +#endif + +/* Return values from ipfw_chk() */ +enum { + IP_FW_PASS = 0, + IP_FW_DENY, + IP_FW_DIVERT, + IP_FW_TEE, + IP_FW_DUMMYNET, + IP_FW_NETGRAPH, + IP_FW_NGTEE, + IP_FW_NAT, + IP_FW_REASS, +}; + +/* + * Structure for collecting parameters to dummynet for ip6_output forwarding + */ +struct _ip6dn_args { + struct ip6_pktopts *opt_or; + struct route_in6 ro_or; + int flags_or; + struct ip6_moptions *im6o_or; + struct ifnet *origifp_or; + struct ifnet *ifp_or; + struct sockaddr_in6 dst_or; + u_long mtu_or; + struct route_in6 ro_pmtu_or; +}; + + +/* + * Arguments for calling ipfw_chk() and dummynet_io(). We put them + * all into a structure because this way it is easier and more + * efficient to pass variables around and extend the interface. + */ +struct ip_fw_args { + struct mbuf *m; /* the mbuf chain */ + struct ifnet *oif; /* output interface */ + struct sockaddr_in *next_hop; /* forward address */ + struct sockaddr_in6 *next_hop6; /* ipv6 forward address */ + + /* + * On return, it points to the matching rule. + * On entry, rule.slot > 0 means the info is valid and + * contains the starting rule for an ipfw search. + * If chain_id == chain->id && slot >0 then jump to that slot. + * Otherwise, we locate the first rule >= rulenum:rule_id + */ + struct ipfw_rule_ref rule; /* match/restart info */ + + struct ether_header *eh; /* for bridged packets */ + + struct ipfw_flow_id f_id; /* grabbed from IP header */ + //uint32_t cookie; /* a cookie depending on rule action */ + struct inpcb *inp; + + struct _ip6dn_args dummypar; /* dummynet->ip6_output */ + struct sockaddr_in hopstore; /* store here if cannot use a pointer */ +}; + +MALLOC_DECLARE(M_IPFW); + +/* + * Hooks sometime need to know the direction of the packet + * (divert, dummynet, netgraph, ...) + * We use a generic definition here, with bit0-1 indicating the + * direction, bit 2 indicating layer2 or 3, bit 3-4 indicating the + * specific protocol + * indicating the protocol (if necessary) + */ +enum { + DIR_MASK = 0x3, + DIR_OUT = 0, + DIR_IN = 1, + DIR_FWD = 2, + DIR_DROP = 3, + PROTO_LAYER2 = 0x4, /* set for layer 2 */ + /* PROTO_DEFAULT = 0, */ + PROTO_IPV4 = 0x08, + PROTO_IPV6 = 0x10, + PROTO_IFB = 0x0c, /* layer2 + ifbridge */ + /* PROTO_OLDBDG = 0x14, unused, old bridge */ +}; + +/* wrapper for freeing a packet, in case we need to do more work */ +#ifndef FREE_PKT +#if defined(__linux__) || defined(_WIN32) +#define FREE_PKT(m) netisr_dispatch(-1, m) +#else +#define FREE_PKT(m) m_freem(m) +#endif +#endif /* !FREE_PKT */ + +/* + * Function definitions. + */ + +/* attach (arg = 1) or detach (arg = 0) hooks */ +int ipfw_attach_hooks(int); +#ifdef NOTYET +void ipfw_nat_destroy(void); +#endif + +/* In ip_fw_log.c */ +struct ip; +void ipfw_log_bpf(int); +void ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, + struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg, + struct ip *ip); +VNET_DECLARE(u_int64_t, norule_counter); +#define V_norule_counter VNET(norule_counter) +VNET_DECLARE(int, verbose_limit); +#define V_verbose_limit VNET(verbose_limit) + +/* In ip_fw_dynamic.c */ + +enum { /* result for matching dynamic rules */ + MATCH_REVERSE = 0, + MATCH_FORWARD, + MATCH_NONE, + MATCH_UNKNOWN, +}; + +/* + * The lock for dynamic rules is only used once outside the file, + * and only to release the result of lookup_dyn_rule(). + * Eventually we may implement it with a callback on the function. + */ +void ipfw_dyn_unlock(void); + +struct tcphdr; +struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *, + u_int32_t, u_int32_t, int); +int ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, + struct ip_fw_args *args, uint32_t tablearg); +ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, + int *match_direction, struct tcphdr *tcp); +void ipfw_remove_dyn_children(struct ip_fw *rule); +void ipfw_get_dynamic(char **bp, const char *ep); + +void ipfw_dyn_attach(void); /* uma_zcreate .... */ +void ipfw_dyn_detach(void); /* uma_zdestroy ... */ +void ipfw_dyn_init(void); /* per-vnet initialization */ +void ipfw_dyn_uninit(int); /* per-vnet deinitialization */ +int ipfw_dyn_len(void); + +/* common variables */ +VNET_DECLARE(int, fw_one_pass); +#define V_fw_one_pass VNET(fw_one_pass) + +VNET_DECLARE(int, fw_verbose); +#define V_fw_verbose VNET(fw_verbose) + +VNET_DECLARE(struct ip_fw_chain, layer3_chain); +#define V_layer3_chain VNET(layer3_chain) + +VNET_DECLARE(u_int32_t, set_disable); +#define V_set_disable VNET(set_disable) + +VNET_DECLARE(int, autoinc_step); +#define V_autoinc_step VNET(autoinc_step) + +VNET_DECLARE(unsigned int, fw_tables_max); +#define V_fw_tables_max VNET(fw_tables_max) + +struct ip_fw_chain { + struct ip_fw *rules; /* list of rules */ + struct ip_fw *reap; /* list of rules to reap */ + struct ip_fw *default_rule; + int n_rules; /* number of static rules */ + int static_len; /* total len of static rules */ + struct ip_fw **map; /* array of rule ptrs to ease lookup */ + LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ + struct radix_node_head **tables; /* IPv4 tables */ + struct radix_node_head **xtables; /* extended tables */ + uint8_t *tabletype; /* Array of table types */ +#if defined( __linux__ ) || defined( _WIN32 ) + spinlock_t rwmtx; + spinlock_t uh_lock; +#else + struct rwlock rwmtx; + struct rwlock uh_lock; /* lock for upper half */ +#endif + uint32_t id; /* ruleset id */ + uint32_t gencnt; /* generation count */ +}; + +struct sockopt; /* used by tcp_var.h */ + +/* + * The lock is heavily used by ip_fw2.c (the main file) and ip_fw_nat.c + * so the variable and the macros must be here. + */ + +#define IPFW_LOCK_INIT(_chain) do { \ + rw_init(&(_chain)->rwmtx, "IPFW static rules"); \ + rw_init(&(_chain)->uh_lock, "IPFW UH lock"); \ + } while (0) + +#define IPFW_LOCK_DESTROY(_chain) do { \ + rw_destroy(&(_chain)->rwmtx); \ + rw_destroy(&(_chain)->uh_lock); \ + } while (0) + +#define IPFW_WLOCK_ASSERT(_chain) rw_assert(&(_chain)->rwmtx, RA_WLOCKED) + +#define IPFW_RLOCK(p) rw_rlock(&(p)->rwmtx) +#define IPFW_RUNLOCK(p) rw_runlock(&(p)->rwmtx) +#define IPFW_WLOCK(p) rw_wlock(&(p)->rwmtx) +#define IPFW_WUNLOCK(p) rw_wunlock(&(p)->rwmtx) + +#define IPFW_UH_RLOCK(p) rw_rlock(&(p)->uh_lock) +#define IPFW_UH_RUNLOCK(p) rw_runlock(&(p)->uh_lock) +#define IPFW_UH_WLOCK(p) rw_wlock(&(p)->uh_lock) +#define IPFW_UH_WUNLOCK(p) rw_wunlock(&(p)->uh_lock) + +/* In ip_fw_sockopt.c */ +int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id); +int ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule); +int ipfw_ctl(struct sockopt *sopt); +int ipfw_chk(struct ip_fw_args *args); +void ipfw_reap_rules(struct ip_fw *head); + +/* In ip_fw_table.c */ +struct radix_node; +int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, + uint32_t *val); +int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint32_t *val, int type); +int ipfw_init_tables(struct ip_fw_chain *ch); +void ipfw_destroy_tables(struct ip_fw_chain *ch); +int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl); +int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value); +int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint8_t plen, uint8_t mlen, uint8_t type); +int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt); +int ipfw_dump_table_entry(struct radix_node *rn, void *arg); +int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl); +int ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt); +int ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl); +int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables); + +/* In ip_fw_nat.c -- XXX to be moved to ip_var.h */ + +extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); + +typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *); +typedef int ipfw_nat_cfg_t(struct sockopt *); + +extern ipfw_nat_t *ipfw_nat_ptr; +#define IPFW_NAT_LOADED (ipfw_nat_ptr != NULL) + +extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; +extern ipfw_nat_cfg_t *ipfw_nat_del_ptr; +extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; +extern ipfw_nat_cfg_t *ipfw_nat_get_log_ptr; + +#endif /* _KERNEL */ +#endif /* _IPFW2_PRIVATE_H */ diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c new file mode 100644 index 0000000000000..a412eb03fb365 --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -0,0 +1,1449 @@ +/*- + * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa + * + * Supported by: Valeria Paoli + * + * 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 +__FBSDID("$FreeBSD$"); + +/* + * Sockopt support for ipfw. The routines here implement + * the upper half of the ipfw code. + */ + +#include "opt_ipfw.h" +#include "opt_inet.h" +#ifndef INET +#error IPFIREWALL requires INET. +#endif /* INET */ +#include "opt_inet6.h" + +#include +#include +#include +#include /* struct m_tag used by nested headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* hooks */ +#include + +#include + +#ifdef MAC +#include +#endif + +MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); + +/* + * static variables followed by global ones (none in this file) + */ + +/* + * Find the smallest rule >= key, id. + * We could use bsearch but it is so simple that we code it directly + */ +int +ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id) +{ + int i, lo, hi; + struct ip_fw *r; + + for (lo = 0, hi = chain->n_rules - 1; lo < hi;) { + i = (lo + hi) / 2; + r = chain->map[i]; + if (r->rulenum < key) + lo = i + 1; /* continue from the next one */ + else if (r->rulenum > key) + hi = i; /* this might be good */ + else if (r->id < id) + lo = i + 1; /* continue from the next one */ + else /* r->id >= id */ + hi = i; /* this might be good */ + }; + return hi; +} + +/* + * allocate a new map, returns the chain locked. extra is the number + * of entries to add or delete. + */ +static struct ip_fw ** +get_map(struct ip_fw_chain *chain, int extra, int locked) +{ + + for (;;) { + struct ip_fw **map; + int i; + + i = chain->n_rules + extra; + map = malloc(i * sizeof(struct ip_fw *), M_IPFW, + locked ? M_NOWAIT : M_WAITOK); + if (map == NULL) { + printf("%s: cannot allocate map\n", __FUNCTION__); + return NULL; + } + if (!locked) + IPFW_UH_WLOCK(chain); + if (i >= chain->n_rules + extra) /* good */ + return map; + /* otherwise we lost the race, free and retry */ + if (!locked) + IPFW_UH_WUNLOCK(chain); + free(map, M_IPFW); + } +} + +/* + * swap the maps. It is supposed to be called with IPFW_UH_WLOCK + */ +static struct ip_fw ** +swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len) +{ + struct ip_fw **old_map; + + IPFW_WLOCK(chain); + chain->id++; + chain->n_rules = new_len; + old_map = chain->map; + chain->map = new_map; + IPFW_WUNLOCK(chain); + return old_map; +} + +/* + * Add a new rule to the list. Copy the rule into a malloc'ed area, then + * possibly create a rule number and add the rule to the list. + * Update the rule_number in the input struct so the caller knows it as well. + * XXX DO NOT USE FOR THE DEFAULT RULE. + * Must be called without IPFW_UH held + */ +int +ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) +{ + struct ip_fw *rule; + int i, l, insert_before; + struct ip_fw **map; /* the new array of pointers */ + + if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) + return (EINVAL); + + l = RULESIZE(input_rule); + rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO); + /* get_map returns with IPFW_UH_WLOCK if successful */ + map = get_map(chain, 1, 0 /* not locked */); + if (map == NULL) { + free(rule, M_IPFW); + return ENOSPC; + } + + bcopy(input_rule, rule, l); + /* clear fields not settable from userland */ + rule->x_next = NULL; + rule->next_rule = NULL; + rule->pcnt = 0; + rule->bcnt = 0; + rule->timestamp = 0; + + if (V_autoinc_step < 1) + V_autoinc_step = 1; + else if (V_autoinc_step > 1000) + V_autoinc_step = 1000; + /* find the insertion point, we will insert before */ + insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE; + i = ipfw_find_rule(chain, insert_before, 0); + /* duplicate first part */ + if (i > 0) + bcopy(chain->map, map, i * sizeof(struct ip_fw *)); + map[i] = rule; + /* duplicate remaining part, we always have the default rule */ + bcopy(chain->map + i, map + i + 1, + sizeof(struct ip_fw *) *(chain->n_rules - i)); + if (rule->rulenum == 0) { + /* write back the number */ + rule->rulenum = i > 0 ? map[i-1]->rulenum : 0; + if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) + rule->rulenum += V_autoinc_step; + input_rule->rulenum = rule->rulenum; + } + + rule->id = chain->id + 1; + map = swap_map(chain, map, chain->n_rules + 1); + chain->static_len += l; + IPFW_UH_WUNLOCK(chain); + if (map) + free(map, M_IPFW); + return (0); +} + +/* + * Reclaim storage associated with a list of rules. This is + * typically the list created using remove_rule. + * A NULL pointer on input is handled correctly. + */ +void +ipfw_reap_rules(struct ip_fw *head) +{ + struct ip_fw *rule; + + while ((rule = head) != NULL) { + head = head->x_next; + free(rule, M_IPFW); + } +} + +/* + * Used by del_entry() to check if a rule should be kept. + * Returns 1 if the rule must be kept, 0 otherwise. + * + * Called with cmd = {0,1,5}. + * cmd == 0 matches on rule numbers, excludes rules in RESVD_SET if n == 0 ; + * cmd == 1 matches on set numbers only, rule numbers are ignored; + * cmd == 5 matches on rule and set numbers. + * + * n == 0 is a wildcard for rule numbers, there is no wildcard for sets. + * + * Rules to keep are + * (default || reserved || !match_set || !match_number) + * where + * default ::= (rule->rulenum == IPFW_DEFAULT_RULE) + * // the default rule is always protected + * + * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET) + * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush") + * + * match_set ::= (cmd == 0 || rule->set == set) + * // set number is ignored for cmd == 0 + * + * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum) + * // number is ignored for cmd == 1 or n == 0 + * + */ +static int +keep_rule(struct ip_fw *rule, uint8_t cmd, uint8_t set, uint32_t n) +{ + return + (rule->rulenum == IPFW_DEFAULT_RULE) || + (cmd == 0 && n == 0 && rule->set == RESVD_SET) || + !(cmd == 0 || rule->set == set) || + !(cmd == 1 || n == 0 || n == rule->rulenum); +} + +/** + * Remove all rules with given number, or do set manipulation. + * Assumes chain != NULL && *chain != NULL. + * + * The argument is an uint32_t. The low 16 bit are the rule or set number; + * the next 8 bits are the new set; the top 8 bits indicate the command: + * + * 0 delete rules numbered "rulenum" + * 1 delete rules in set "rulenum" + * 2 move rules "rulenum" to set "new_set" + * 3 move rules from set "rulenum" to set "new_set" + * 4 swap sets "rulenum" and "new_set" + * 5 delete rules "rulenum" and set "new_set" + */ +static int +del_entry(struct ip_fw_chain *chain, uint32_t arg) +{ + struct ip_fw *rule; + uint32_t num; /* rule number or old_set */ + uint8_t cmd, new_set; + int start, end, i, ofs, n; + struct ip_fw **map = NULL; + int error = 0; + + num = arg & 0xffff; + cmd = (arg >> 24) & 0xff; + new_set = (arg >> 16) & 0xff; + + if (cmd > 5 || new_set > RESVD_SET) + return EINVAL; + if (cmd == 0 || cmd == 2 || cmd == 5) { + if (num >= IPFW_DEFAULT_RULE) + return EINVAL; + } else { + if (num > RESVD_SET) /* old_set */ + return EINVAL; + } + + IPFW_UH_WLOCK(chain); /* arbitrate writers */ + chain->reap = NULL; /* prepare for deletions */ + + switch (cmd) { + case 0: /* delete rules "num" (num == 0 matches all) */ + case 1: /* delete all rules in set N */ + case 5: /* delete rules with number N and set "new_set". */ + + /* + * Locate first rule to delete (start), the rule after + * the last one to delete (end), and count how many + * rules to delete (n). Always use keep_rule() to + * determine which rules to keep. + */ + n = 0; + if (cmd == 1) { + /* look for a specific set including RESVD_SET. + * Must scan the entire range, ignore num. + */ + new_set = num; + for (start = -1, end = i = 0; i < chain->n_rules; i++) { + if (keep_rule(chain->map[i], cmd, new_set, 0)) + continue; + if (start < 0) + start = i; + end = i; + n++; + } + end++; /* first non-matching */ + } else { + /* Optimized search on rule numbers */ + start = ipfw_find_rule(chain, num, 0); + for (end = start; end < chain->n_rules; end++) { + rule = chain->map[end]; + if (num > 0 && rule->rulenum != num) + break; + if (!keep_rule(rule, cmd, new_set, num)) + n++; + } + } + + if (n == 0) { + /* A flush request (arg == 0 or cmd == 1) on empty + * ruleset returns with no error. On the contrary, + * if there is no match on a specific request, + * we return EINVAL. + */ + if (arg != 0 && cmd != 1) + error = EINVAL; + break; + } + + /* We have something to delete. Allocate the new map */ + map = get_map(chain, -n, 1 /* locked */); + if (map == NULL) { + error = EINVAL; + break; + } + + /* 1. bcopy the initial part of the map */ + if (start > 0) + bcopy(chain->map, map, start * sizeof(struct ip_fw *)); + /* 2. copy active rules between start and end */ + for (i = ofs = start; i < end; i++) { + rule = chain->map[i]; + if (keep_rule(rule, cmd, new_set, num)) + map[ofs++] = rule; + } + /* 3. copy the final part of the map */ + bcopy(chain->map + end, map + ofs, + (chain->n_rules - end) * sizeof(struct ip_fw *)); + /* 4. swap the maps (under BH_LOCK) */ + map = swap_map(chain, map, chain->n_rules - n); + /* 5. now remove the rules deleted from the old map */ + for (i = start; i < end; i++) { + int l; + rule = map[i]; + if (keep_rule(rule, cmd, new_set, num)) + continue; + l = RULESIZE(rule); + chain->static_len -= l; + ipfw_remove_dyn_children(rule); + rule->x_next = chain->reap; + chain->reap = rule; + } + break; + + /* + * In the next 3 cases the loop stops at (n_rules - 1) + * because the default rule is never eligible.. + */ + + case 2: /* move rules with given RULE number to new set */ + for (i = 0; i < chain->n_rules - 1; i++) { + rule = chain->map[i]; + if (rule->rulenum == num) + rule->set = new_set; + } + break; + + case 3: /* move rules with given SET number to new set */ + for (i = 0; i < chain->n_rules - 1; i++) { + rule = chain->map[i]; + if (rule->set == num) + rule->set = new_set; + } + break; + + case 4: /* swap two sets */ + for (i = 0; i < chain->n_rules - 1; i++) { + rule = chain->map[i]; + if (rule->set == num) + rule->set = new_set; + else if (rule->set == new_set) + rule->set = num; + } + break; + } + + rule = chain->reap; + chain->reap = NULL; + IPFW_UH_WUNLOCK(chain); + ipfw_reap_rules(rule); + if (map) + free(map, M_IPFW); + return error; +} + +/* + * Clear counters for a specific rule. + * Normally run under IPFW_UH_RLOCK, but these are idempotent ops + * so we only care that rules do not disappear. + */ +static void +clear_counters(struct ip_fw *rule, int log_only) +{ + ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule); + + if (log_only == 0) { + rule->bcnt = rule->pcnt = 0; + rule->timestamp = 0; + } + if (l->o.opcode == O_LOG) + l->log_left = l->max_log; +} + +/** + * Reset some or all counters on firewall rules. + * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, + * the next 8 bits are the set number, the top 8 bits are the command: + * 0 work with rules from all set's; + * 1 work with rules only from specified set. + * Specified rule number is zero if we want to clear all entries. + * log_only is 1 if we only want to reset logs, zero otherwise. + */ +static int +zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) +{ + struct ip_fw *rule; + char *msg; + int i; + + uint16_t rulenum = arg & 0xffff; + uint8_t set = (arg >> 16) & 0xff; + uint8_t cmd = (arg >> 24) & 0xff; + + if (cmd > 1) + return (EINVAL); + if (cmd == 1 && set > RESVD_SET) + return (EINVAL); + + IPFW_UH_RLOCK(chain); + if (rulenum == 0) { + V_norule_counter = 0; + for (i = 0; i < chain->n_rules; i++) { + rule = chain->map[i]; + /* Skip rules not in our set. */ + if (cmd == 1 && rule->set != set) + continue; + clear_counters(rule, log_only); + } + msg = log_only ? "All logging counts reset" : + "Accounting cleared"; + } else { + int cleared = 0; + for (i = 0; i < chain->n_rules; i++) { + rule = chain->map[i]; + if (rule->rulenum == rulenum) { + if (cmd == 0 || rule->set == set) + clear_counters(rule, log_only); + cleared = 1; + } + if (rule->rulenum > rulenum) + break; + } + if (!cleared) { /* we did not find any matching rules */ + IPFW_UH_RUNLOCK(chain); + return (EINVAL); + } + msg = log_only ? "logging count reset" : "cleared"; + } + IPFW_UH_RUNLOCK(chain); + + if (V_fw_verbose) { + int lev = LOG_SECURITY | LOG_NOTICE; + + if (rulenum) + log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); + else + log(lev, "ipfw: %s.\n", msg); + } + return (0); +} + +/* + * Check validity of the structure before insert. + * Rules are simple, so this mostly need to check rule sizes. + */ +static int +check_ipfw_struct(struct ip_fw *rule, int size) +{ + int l, cmdlen = 0; + int have_action=0; + ipfw_insn *cmd; + + if (size < sizeof(*rule)) { + printf("ipfw: rule too short\n"); + return (EINVAL); + } + /* first, check for valid size */ + l = RULESIZE(rule); + if (l != size) { + printf("ipfw: size mismatch (have %d want %d)\n", size, l); + return (EINVAL); + } + if (rule->act_ofs >= rule->cmd_len) { + printf("ipfw: bogus action offset (%u > %u)\n", + rule->act_ofs, rule->cmd_len - 1); + return (EINVAL); + } + /* + * Now go for the individual checks. Very simple ones, basically only + * instruction sizes. + */ + for (l = rule->cmd_len, cmd = rule->cmd ; + l > 0 ; l -= cmdlen, cmd += cmdlen) { + cmdlen = F_LEN(cmd); + if (cmdlen > l) { + printf("ipfw: opcode %d size truncated\n", + cmd->opcode); + return EINVAL; + } + switch (cmd->opcode) { + case O_PROBE_STATE: + case O_KEEP_STATE: + case O_PROTO: + case O_IP_SRC_ME: + case O_IP_DST_ME: + case O_LAYER2: + case O_IN: + case O_FRAG: + case O_DIVERTED: + case O_IPOPT: + case O_IPTOS: + case O_IPPRECEDENCE: + case O_IPVER: + case O_SOCKARG: + case O_TCPFLAGS: + case O_TCPOPTS: + case O_ESTAB: + case O_VERREVPATH: + case O_VERSRCREACH: + case O_ANTISPOOF: + case O_IPSEC: +#ifdef INET6 + case O_IP6_SRC_ME: + case O_IP6_DST_ME: + case O_EXT_HDR: + case O_IP6: +#endif + case O_IP4: + case O_TAG: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + goto bad_size; + break; + + case O_FIB: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + goto bad_size; + if (cmd->arg1 >= rt_numfibs) { + printf("ipfw: invalid fib number %d\n", + cmd->arg1); + return EINVAL; + } + break; + + case O_SETFIB: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + goto bad_size; + if ((cmd->arg1 != IP_FW_TABLEARG) && + (cmd->arg1 >= rt_numfibs)) { + printf("ipfw: invalid fib number %d\n", + cmd->arg1); + return EINVAL; + } + goto check_action; + + case O_UID: + case O_GID: + case O_JAIL: + case O_IP_SRC: + case O_IP_DST: + case O_TCPSEQ: + case O_TCPACK: + case O_PROB: + case O_ICMPTYPE: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) + goto bad_size; + break; + + case O_LIMIT: + if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) + goto bad_size; + break; + + case O_LOG: + if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) + goto bad_size; + + ((ipfw_insn_log *)cmd)->log_left = + ((ipfw_insn_log *)cmd)->max_log; + + break; + + case O_IP_SRC_MASK: + case O_IP_DST_MASK: + /* only odd command lengths */ + if ( !(cmdlen & 1) || cmdlen > 31) + goto bad_size; + break; + + case O_IP_SRC_SET: + case O_IP_DST_SET: + if (cmd->arg1 == 0 || cmd->arg1 > 256) { + printf("ipfw: invalid set size %d\n", + cmd->arg1); + return EINVAL; + } + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + + (cmd->arg1+31)/32 ) + goto bad_size; + break; + + case O_IP_SRC_LOOKUP: + case O_IP_DST_LOOKUP: + if (cmd->arg1 >= IPFW_TABLES_MAX) { + printf("ipfw: invalid table number %d\n", + cmd->arg1); + return (EINVAL); + } + if (cmdlen != F_INSN_SIZE(ipfw_insn) && + cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && + cmdlen != F_INSN_SIZE(ipfw_insn_u32)) + goto bad_size; + break; + case O_MACADDR2: + if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) + goto bad_size; + break; + + case O_NOP: + case O_IPID: + case O_IPTTL: + case O_IPLEN: + case O_TCPDATALEN: + case O_TCPWIN: + case O_TAGGED: + if (cmdlen < 1 || cmdlen > 31) + goto bad_size; + break; + + case O_MAC_TYPE: + case O_IP_SRCPORT: + case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ + if (cmdlen < 2 || cmdlen > 31) + goto bad_size; + break; + + case O_RECV: + case O_XMIT: + case O_VIA: + if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) + goto bad_size; + break; + + case O_ALTQ: + if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) + goto bad_size; + break; + + case O_PIPE: + case O_QUEUE: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + goto bad_size; + goto check_action; + + case O_FORWARD_IP: +#ifdef IPFIREWALL_FORWARD + if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) + goto bad_size; + goto check_action; +#else + return EINVAL; +#endif + +#ifdef INET6 + case O_FORWARD_IP6: +#ifdef IPFIREWALL_FORWARD + if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) + goto bad_size; + goto check_action; +#else + return (EINVAL); +#endif +#endif /* INET6 */ + + case O_DIVERT: + case O_TEE: + if (ip_divert_ptr == NULL) + return EINVAL; + else + goto check_size; + case O_NETGRAPH: + case O_NGTEE: + if (ng_ipfw_input_p == NULL) + return EINVAL; + else + goto check_size; + case O_NAT: + if (!IPFW_NAT_LOADED) + return EINVAL; + if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) + goto bad_size; + goto check_action; + case O_FORWARD_MAC: /* XXX not implemented yet */ + case O_CHECK_STATE: + case O_COUNT: + case O_ACCEPT: + case O_DENY: + case O_REJECT: +#ifdef INET6 + case O_UNREACH6: +#endif + case O_SKIPTO: + case O_REASS: + case O_CALLRETURN: +check_size: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + goto bad_size; +check_action: + if (have_action) { + printf("ipfw: opcode %d, multiple actions" + " not allowed\n", + cmd->opcode); + return EINVAL; + } + have_action = 1; + if (l != cmdlen) { + printf("ipfw: opcode %d, action must be" + " last opcode\n", + cmd->opcode); + return EINVAL; + } + break; +#ifdef INET6 + case O_IP6_SRC: + case O_IP6_DST: + if (cmdlen != F_INSN_SIZE(struct in6_addr) + + F_INSN_SIZE(ipfw_insn)) + goto bad_size; + break; + + case O_FLOW6ID: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + + ((ipfw_insn_u32 *)cmd)->o.arg1) + goto bad_size; + break; + + case O_IP6_SRC_MASK: + case O_IP6_DST_MASK: + if ( !(cmdlen & 1) || cmdlen > 127) + goto bad_size; + break; + case O_ICMP6TYPE: + if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) + goto bad_size; + break; +#endif + + default: + switch (cmd->opcode) { +#ifndef INET6 + case O_IP6_SRC_ME: + case O_IP6_DST_ME: + case O_EXT_HDR: + case O_IP6: + case O_UNREACH6: + case O_IP6_SRC: + case O_IP6_DST: + case O_FLOW6ID: + case O_IP6_SRC_MASK: + case O_IP6_DST_MASK: + case O_ICMP6TYPE: + printf("ipfw: no IPv6 support in kernel\n"); + return EPROTONOSUPPORT; +#endif + default: + printf("ipfw: opcode %d, unknown opcode\n", + cmd->opcode); + return EINVAL; + } + } + } + if (have_action == 0) { + printf("ipfw: missing action\n"); + return EINVAL; + } + return 0; + +bad_size: + printf("ipfw: opcode %d size %d wrong\n", + cmd->opcode, cmdlen); + return EINVAL; +} + + +/* + * Translation of requests for compatibility with FreeBSD 7.2/8. + * a static variable tells us if we have an old client from userland, + * and if necessary we translate requests and responses between the + * two formats. + */ +static int is7 = 0; + +struct ip_fw7 { + struct ip_fw7 *next; /* linked list of rules */ + struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ + /* 'next_rule' is used to pass up 'set_disable' status */ + + uint16_t act_ofs; /* offset of action in 32-bit units */ + uint16_t cmd_len; /* # of 32-bit words in cmd */ + uint16_t rulenum; /* rule number */ + uint8_t set; /* rule set (0..31) */ + // #define RESVD_SET 31 /* set for default and persistent rules */ + uint8_t _pad; /* padding */ + // uint32_t id; /* rule id, only in v.8 */ + /* These fields are present in all rules. */ + uint64_t pcnt; /* Packet counter */ + uint64_t bcnt; /* Byte counter */ + uint32_t timestamp; /* tv_sec of last match */ + + ipfw_insn cmd[1]; /* storage for commands */ +}; + + int convert_rule_to_7(struct ip_fw *rule); +int convert_rule_to_8(struct ip_fw *rule); + +#ifndef RULESIZE7 +#define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ + ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) +#endif + + +/* + * Copy the static and dynamic rules to the supplied buffer + * and return the amount of space actually used. + * Must be run under IPFW_UH_RLOCK + */ +static size_t +ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) +{ + char *bp = buf; + char *ep = bp + space; + struct ip_fw *rule, *dst; + int l, i; + time_t boot_seconds; + + boot_seconds = boottime.tv_sec; + for (i = 0; i < chain->n_rules; i++) { + rule = chain->map[i]; + + if (is7) { + /* Convert rule to FreeBSd 7.2 format */ + l = RULESIZE7(rule); + if (bp + l + sizeof(uint32_t) <= ep) { + int error; + bcopy(rule, bp, l + sizeof(uint32_t)); + error = convert_rule_to_7((struct ip_fw *) bp); + if (error) + return 0; /*XXX correct? */ + /* + * XXX HACK. Store the disable mask in the "next" + * pointer in a wild attempt to keep the ABI the same. + * Why do we do this on EVERY rule? + */ + bcopy(&V_set_disable, + &(((struct ip_fw7 *)bp)->next_rule), + sizeof(V_set_disable)); + if (((struct ip_fw7 *)bp)->timestamp) + ((struct ip_fw7 *)bp)->timestamp += boot_seconds; + bp += l; + } + continue; /* go to next rule */ + } + + /* normal mode, don't touch rules */ + l = RULESIZE(rule); + if (bp + l > ep) { /* should not happen */ + printf("overflow dumping static rules\n"); + break; + } + dst = (struct ip_fw *)bp; + bcopy(rule, dst, l); + /* + * XXX HACK. Store the disable mask in the "next" + * pointer in a wild attempt to keep the ABI the same. + * Why do we do this on EVERY rule? + */ + bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); + if (dst->timestamp) + dst->timestamp += boot_seconds; + bp += l; + } + ipfw_get_dynamic(&bp, ep); /* protected by the dynamic lock */ + return (bp - (char *)buf); +} + + +#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader)) +/** + * {set|get}sockopt parser. + */ +int +ipfw_ctl(struct sockopt *sopt) +{ +#define RULE_MAXSIZE (256*sizeof(u_int32_t)) + int error; + size_t size, len, valsize; + struct ip_fw *buf, *rule; + struct ip_fw_chain *chain; + u_int32_t rulenum[2]; + uint32_t opt; + char xbuf[128]; + ip_fw3_opheader *op3 = NULL; + + error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); + if (error) + return (error); + + /* + * Disallow modifications in really-really secure mode, but still allow + * the logging counters to be reset. + */ + if (sopt->sopt_name == IP_FW_ADD || + (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) { + error = securelevel_ge(sopt->sopt_td->td_ucred, 3); + if (error) + return (error); + } + + chain = &V_layer3_chain; + error = 0; + + /* Save original valsize before it is altered via sooptcopyin() */ + valsize = sopt->sopt_valsize; + if ((opt = sopt->sopt_name) == IP_FW3) { + /* + * Copy not less than sizeof(ip_fw3_opheader). + * We hope any IP_FW3 command will fit into 128-byte buffer. + */ + if ((error = sooptcopyin(sopt, xbuf, sizeof(xbuf), + sizeof(ip_fw3_opheader))) != 0) + return (error); + op3 = (ip_fw3_opheader *)xbuf; + opt = op3->opcode; + } + + switch (opt) { + case IP_FW_GET: + /* + * pass up a copy of the current rules. Static rules + * come first (the last of which has number IPFW_DEFAULT_RULE), + * followed by a possibly empty list of dynamic rule. + * The last dynamic rule has NULL in the "next" field. + * + * Note that the calculated size is used to bound the + * amount of data returned to the user. The rule set may + * change between calculating the size and returning the + * data in which case we'll just return what fits. + */ + for (;;) { + int len = 0, want; + + size = chain->static_len; + size += ipfw_dyn_len(); + if (size >= sopt->sopt_valsize) + break; + buf = malloc(size, M_TEMP, M_WAITOK); + IPFW_UH_RLOCK(chain); + /* check again how much space we need */ + want = chain->static_len + ipfw_dyn_len(); + if (size >= want) + len = ipfw_getrules(chain, buf, size); + IPFW_UH_RUNLOCK(chain); + if (size >= want) + error = sooptcopyout(sopt, buf, len); + free(buf, M_TEMP); + if (size >= want) + break; + } + break; + + case IP_FW_FLUSH: + /* locking is done within del_entry() */ + error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ + break; + + case IP_FW_ADD: + rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); + error = sooptcopyin(sopt, rule, RULE_MAXSIZE, + sizeof(struct ip_fw7) ); + + /* + * If the size of commands equals RULESIZE7 then we assume + * a FreeBSD7.2 binary is talking to us (set is7=1). + * is7 is persistent so the next 'ipfw list' command + * will use this format. + * NOTE: If wrong version is guessed (this can happen if + * the first ipfw command is 'ipfw [pipe] list') + * the ipfw binary may crash or loop infinitly... + */ + if (sopt->sopt_valsize == RULESIZE7(rule)) { + is7 = 1; + error = convert_rule_to_8(rule); + if (error) + return error; + if (error == 0) + error = check_ipfw_struct(rule, RULESIZE(rule)); + } else { + is7 = 0; + if (error == 0) + error = check_ipfw_struct(rule, sopt->sopt_valsize); + } + if (error == 0) { + /* locking is done within ipfw_add_rule() */ + error = ipfw_add_rule(chain, rule); + size = RULESIZE(rule); + if (!error && sopt->sopt_dir == SOPT_GET) { + if (is7) { + error = convert_rule_to_7(rule); + size = RULESIZE7(rule); + if (error) + return error; + } + error = sooptcopyout(sopt, rule, size); + } + } + free(rule, M_TEMP); + break; + + case IP_FW_DEL: + /* + * IP_FW_DEL is used for deleting single rules or sets, + * and (ab)used to atomically manipulate sets. Argument size + * is used to distinguish between the two: + * sizeof(u_int32_t) + * delete single rule or set of rules, + * or reassign rules (or sets) to a different set. + * 2*sizeof(u_int32_t) + * atomic disable/enable sets. + * first u_int32_t contains sets to be disabled, + * second u_int32_t contains sets to be enabled. + */ + error = sooptcopyin(sopt, rulenum, + 2*sizeof(u_int32_t), sizeof(u_int32_t)); + if (error) + break; + size = sopt->sopt_valsize; + if (size == sizeof(u_int32_t) && rulenum[0] != 0) { + /* delete or reassign, locking done in del_entry() */ + error = del_entry(chain, rulenum[0]); + } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ + IPFW_UH_WLOCK(chain); + V_set_disable = + (V_set_disable | rulenum[0]) & ~rulenum[1] & + ~(1<sopt_val != 0) { + error = sooptcopyin(sopt, rulenum, + sizeof(u_int32_t), sizeof(u_int32_t)); + if (error) + break; + } + error = zero_entry(chain, rulenum[0], + sopt->sopt_name == IP_FW_RESETLOG); + break; + + /*--- TABLE manipulations are protected by the IPFW_LOCK ---*/ + case IP_FW_TABLE_ADD: + { + ipfw_table_entry ent; + + error = sooptcopyin(sopt, &ent, + sizeof(ent), sizeof(ent)); + if (error) + break; + error = ipfw_add_table_entry(chain, ent.tbl, + &ent.addr, sizeof(ent.addr), ent.masklen, + IPFW_TABLE_CIDR, ent.value); + } + break; + + case IP_FW_TABLE_DEL: + { + ipfw_table_entry ent; + + error = sooptcopyin(sopt, &ent, + sizeof(ent), sizeof(ent)); + if (error) + break; + error = ipfw_del_table_entry(chain, ent.tbl, + &ent.addr, sizeof(ent.addr), ent.masklen, IPFW_TABLE_CIDR); + } + break; + + case IP_FW_TABLE_XADD: /* IP_FW3 */ + case IP_FW_TABLE_XDEL: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + len = xent->len - offsetof(ipfw_table_xentry, k); + + error = (opt == IP_FW_TABLE_XADD) ? + ipfw_add_table_entry(chain, xent->tbl, &xent->k, + len, xent->masklen, xent->type, xent->value) : + ipfw_del_table_entry(chain, xent->tbl, &xent->k, + len, xent->masklen, xent->type); + } + break; + + case IP_FW_TABLE_FLUSH: + { + u_int16_t tbl; + + error = sooptcopyin(sopt, &tbl, + sizeof(tbl), sizeof(tbl)); + if (error) + break; + error = ipfw_flush_table(chain, tbl); + } + break; + + case IP_FW_TABLE_GETSIZE: + { + u_int32_t tbl, cnt; + + if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), + sizeof(tbl)))) + break; + IPFW_RLOCK(chain); + error = ipfw_count_table(chain, tbl, &cnt); + IPFW_RUNLOCK(chain); + if (error) + break; + error = sooptcopyout(sopt, &cnt, sizeof(cnt)); + } + break; + + case IP_FW_TABLE_LIST: + { + ipfw_table *tbl; + + if (sopt->sopt_valsize < sizeof(*tbl)) { + error = EINVAL; + break; + } + size = sopt->sopt_valsize; + tbl = malloc(size, M_TEMP, M_WAITOK); + error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); + if (error) { + free(tbl, M_TEMP); + break; + } + tbl->size = (size - sizeof(*tbl)) / + sizeof(ipfw_table_entry); + IPFW_RLOCK(chain); + error = ipfw_dump_table(chain, tbl); + IPFW_RUNLOCK(chain); + if (error) { + free(tbl, M_TEMP); + break; + } + error = sooptcopyout(sopt, tbl, size); + free(tbl, M_TEMP); + } + break; + + case IP_FW_TABLE_XGETSIZE: /* IP_FW3 */ + { + uint32_t *tbl; + + if (IP_FW3_OPLENGTH(sopt) < sizeof(uint32_t)) { + error = EINVAL; + break; + } + + tbl = (uint32_t *)(op3 + 1); + + IPFW_RLOCK(chain); + error = ipfw_count_xtable(chain, *tbl, tbl); + IPFW_RUNLOCK(chain); + if (error) + break; + error = sooptcopyout(sopt, op3, sopt->sopt_valsize); + } + break; + + case IP_FW_TABLE_XLIST: /* IP_FW3 */ + { + ipfw_xtable *tbl; + + if ((size = valsize) < sizeof(ipfw_xtable)) { + error = EINVAL; + break; + } + + tbl = malloc(size, M_TEMP, M_ZERO | M_WAITOK); + memcpy(tbl, op3, sizeof(ipfw_xtable)); + + /* Get maximum number of entries we can store */ + tbl->size = (size - sizeof(ipfw_xtable)) / + sizeof(ipfw_table_xentry); + IPFW_RLOCK(chain); + error = ipfw_dump_xtable(chain, tbl); + IPFW_RUNLOCK(chain); + if (error) { + free(tbl, M_TEMP); + break; + } + + /* Revert size field back to bytes */ + tbl->size = tbl->size * sizeof(ipfw_table_xentry) + + sizeof(ipfw_table); + /* + * Since we call sooptcopyin() with small buffer, sopt_valsize is + * decreased to reflect supplied buffer size. Set it back to original value + */ + sopt->sopt_valsize = valsize; + error = sooptcopyout(sopt, tbl, size); + free(tbl, M_TEMP); + } + break; + + /*--- NAT operations are protected by the IPFW_LOCK ---*/ + case IP_FW_NAT_CFG: + if (IPFW_NAT_LOADED) + error = ipfw_nat_cfg_ptr(sopt); + else { + printf("IP_FW_NAT_CFG: %s\n", + "ipfw_nat not present, please load it"); + error = EINVAL; + } + break; + + case IP_FW_NAT_DEL: + if (IPFW_NAT_LOADED) + error = ipfw_nat_del_ptr(sopt); + else { + printf("IP_FW_NAT_DEL: %s\n", + "ipfw_nat not present, please load it"); + error = EINVAL; + } + break; + + case IP_FW_NAT_GET_CONFIG: + if (IPFW_NAT_LOADED) + error = ipfw_nat_get_cfg_ptr(sopt); + else { + printf("IP_FW_NAT_GET_CFG: %s\n", + "ipfw_nat not present, please load it"); + error = EINVAL; + } + break; + + case IP_FW_NAT_GET_LOG: + if (IPFW_NAT_LOADED) + error = ipfw_nat_get_log_ptr(sopt); + else { + printf("IP_FW_NAT_GET_LOG: %s\n", + "ipfw_nat not present, please load it"); + error = EINVAL; + } + break; + + default: + printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); + error = EINVAL; + } + + return (error); +#undef RULE_MAXSIZE +} + + +#define RULE_MAXSIZE (256*sizeof(u_int32_t)) + +/* Functions to convert rules 7.2 <==> 8.0 */ +int +convert_rule_to_7(struct ip_fw *rule) +{ + /* Used to modify original rule */ + struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; + /* copy of original rule, version 8 */ + struct ip_fw *tmp; + + /* Used to copy commands */ + ipfw_insn *ccmd, *dst; + int ll = 0, ccmdlen = 0; + + tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); + if (tmp == NULL) { + return 1; //XXX error + } + bcopy(rule, tmp, RULE_MAXSIZE); + + /* Copy fields */ + rule7->_pad = tmp->_pad; + rule7->set = tmp->set; + rule7->rulenum = tmp->rulenum; + rule7->cmd_len = tmp->cmd_len; + rule7->act_ofs = tmp->act_ofs; + rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; + rule7->next = (struct ip_fw7 *)tmp->x_next; + rule7->cmd_len = tmp->cmd_len; + rule7->pcnt = tmp->pcnt; + rule7->bcnt = tmp->bcnt; + rule7->timestamp = tmp->timestamp; + + /* Copy commands */ + for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; + ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { + ccmdlen = F_LEN(ccmd); + + bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); + + if (dst->opcode > O_NAT) + /* O_REASS doesn't exists in 7.2 version, so + * decrement opcode if it is after O_REASS + */ + dst->opcode--; + + if (ccmdlen > ll) { + printf("ipfw: opcode %d size truncated\n", + ccmd->opcode); + return EINVAL; + } + } + free(tmp, M_TEMP); + + return 0; +} + +int +convert_rule_to_8(struct ip_fw *rule) +{ + /* Used to modify original rule */ + struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; + + /* Used to copy commands */ + ipfw_insn *ccmd, *dst; + int ll = 0, ccmdlen = 0; + + /* Copy of original rule */ + struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); + if (tmp == NULL) { + return 1; //XXX error + } + + bcopy(rule7, tmp, RULE_MAXSIZE); + + for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; + ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { + ccmdlen = F_LEN(ccmd); + + bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); + + if (dst->opcode > O_NAT) + /* O_REASS doesn't exists in 7.2 version, so + * increment opcode if it is after O_REASS + */ + dst->opcode++; + + if (ccmdlen > ll) { + printf("ipfw: opcode %d size truncated\n", + ccmd->opcode); + return EINVAL; + } + } + + rule->_pad = tmp->_pad; + rule->set = tmp->set; + rule->rulenum = tmp->rulenum; + rule->cmd_len = tmp->cmd_len; + rule->act_ofs = tmp->act_ofs; + rule->next_rule = (struct ip_fw *)tmp->next_rule; + rule->x_next = (struct ip_fw *)tmp->next; + rule->cmd_len = tmp->cmd_len; + rule->id = 0; /* XXX see if is ok = 0 */ + rule->pcnt = tmp->pcnt; + rule->bcnt = tmp->bcnt; + rule->timestamp = tmp->timestamp; + + free (tmp, M_TEMP); + return 0; +} + +/* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c new file mode 100644 index 0000000000000..a22fff9240f29 --- /dev/null +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -0,0 +1,762 @@ +/*- + * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. + * + * 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 +__FBSDID("$FreeBSD$"); + +/* + * Lookup table support for ipfw + * + * Lookup tables are implemented (at the moment) using the radix + * tree used for routing tables. Tables store key-value entries, where + * keys are network prefixes (addr/masklen), and values are integers. + * As a degenerate case we can interpret keys as 32-bit integers + * (with a /32 mask). + * + * The table is protected by the IPFW lock even for manipulation coming + * from userland, because operations are typically fast. + */ + +#include "opt_ipfw.h" +#include "opt_inet.h" +#ifndef INET +#error IPFIREWALL requires INET. +#endif /* INET */ +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* ip_fw.h requires IFNAMSIZ */ +#include +#include +#include + +#include +#include /* struct ipfw_rule_ref */ +#include + +#include + +#ifdef MAC +#include +#endif + +static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); + +struct table_entry { + struct radix_node rn[2]; + struct sockaddr_in addr, mask; + u_int32_t value; +}; + +struct xaddr_iface { + uint8_t if_len; /* length of this struct */ + uint8_t pad[7]; /* Align name */ + char ifname[IF_NAMESIZE]; /* Interface name */ +}; + +struct table_xentry { + struct radix_node rn[2]; + union { +#ifdef INET6 + struct sockaddr_in6 addr6; +#endif + struct xaddr_iface iface; + } a; + union { +#ifdef INET6 + struct sockaddr_in6 mask6; +#endif + struct xaddr_iface ifmask; + } m; + u_int32_t value; +}; + +/* + * The radix code expects addr and mask to be array of bytes, + * with the first byte being the length of the array. rn_inithead + * is called with the offset in bits of the lookup key within the + * array. If we use a sockaddr_in as the underlying type, + * sin_len is conveniently located at offset 0, sin_addr is at + * offset 4 and normally aligned. + * But for portability, let's avoid assumption and make the code explicit + */ +#define KEY_LEN(v) *((uint8_t *)&(v)) +#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) +/* + * Do not require radix to compare more than actual IPv4/IPv6 address + */ +#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) +#define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr)) +#define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname)) + +#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) +#define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr)) +#define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) + + +static inline void +ipv6_writemask(struct in6_addr *addr6, uint8_t mask) +{ + uint32_t *cp; + + for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) + *cp++ = 0xFFFFFFFF; + *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); +} + +int +ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) +{ + struct radix_node_head *rnh, **rnh_ptr; + struct table_entry *ent; + struct table_xentry *xent; + struct radix_node *rn; + in_addr_t addr; + int offset; + void *ent_ptr; + struct sockaddr *addr_ptr, *mask_ptr; + char c; + + if (tbl >= V_fw_tables_max) + return (EINVAL); + + switch (type) { + case IPFW_TABLE_CIDR: + if (plen == sizeof(in_addr_t)) { +#ifdef INET + /* IPv4 case */ + if (mlen > 32) + return (EINVAL); + ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); + ent->value = value; + /* Set 'total' structure length */ + KEY_LEN(ent->addr) = KEY_LEN_INET; + KEY_LEN(ent->mask) = KEY_LEN_INET; + /* Set offset of IPv4 address in bits */ + offset = OFF_LEN_INET; + ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); + addr = *((in_addr_t *)paddr); + ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; + /* Set pointers */ + rnh_ptr = &ch->tables[tbl]; + ent_ptr = ent; + addr_ptr = (struct sockaddr *)&ent->addr; + mask_ptr = (struct sockaddr *)&ent->mask; +#endif +#ifdef INET6 + } else if (plen == sizeof(struct in6_addr)) { + /* IPv6 case */ + if (mlen > 128) + return (EINVAL); + xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); + xent->value = value; + /* Set 'total' structure length */ + KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; + KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; + /* Set offset of IPv6 address in bits */ + offset = OFF_LEN_INET6; + ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); + memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); + APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); + /* Set pointers */ + rnh_ptr = &ch->xtables[tbl]; + ent_ptr = xent; + addr_ptr = (struct sockaddr *)&xent->a.addr6; + mask_ptr = (struct sockaddr *)&xent->m.mask6; +#endif + } else { + /* Unknown CIDR type */ + return (EINVAL); + } + break; + + case IPFW_TABLE_INTERFACE: + /* Check if string is terminated */ + c = ((char *)paddr)[IF_NAMESIZE - 1]; + ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; + if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) + return (EINVAL); + + /* Include last \0 into comparison */ + mlen++; + + xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); + xent->value = value; + /* Set 'total' structure length */ + KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; + KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; + /* Set offset of interface name in bits */ + offset = OFF_LEN_IFACE; + memcpy(xent->a.iface.ifname, paddr, mlen); + /* Assume direct match */ + /* TODO: Add interface pattern matching */ +#if 0 + memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); + mask_ptr = (struct sockaddr *)&xent->m.ifmask; +#endif + /* Set pointers */ + rnh_ptr = &ch->xtables[tbl]; + ent_ptr = xent; + addr_ptr = (struct sockaddr *)&xent->a.iface; + mask_ptr = NULL; + break; + + default: + return (EINVAL); + } + + IPFW_WLOCK(ch); + + /* Check if tabletype is valid */ + if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { + IPFW_WUNLOCK(ch); + free(ent_ptr, M_IPFW_TBL); + return (EINVAL); + } + + /* Check if radix tree exists */ + if ((rnh = *rnh_ptr) == NULL) { + IPFW_WUNLOCK(ch); + /* Create radix for a new table */ + if (!rn_inithead((void **)&rnh, offset)) { + free(ent_ptr, M_IPFW_TBL); + return (ENOMEM); + } + + IPFW_WLOCK(ch); + if (*rnh_ptr != NULL) { + /* Tree is already attached by other thread */ + rn_detachhead((void **)&rnh); + rnh = *rnh_ptr; + /* Check table type another time */ + if (ch->tabletype[tbl] != type) { + IPFW_WUNLOCK(ch); + free(ent_ptr, M_IPFW_TBL); + return (EINVAL); + } + } else { + *rnh_ptr = rnh; + /* + * Set table type. It can be set already + * (if we have IPv6-only table) but setting + * it another time does not hurt + */ + ch->tabletype[tbl] = type; + } + } + + rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); + IPFW_WUNLOCK(ch); + + if (rn == NULL) { + free(ent_ptr, M_IPFW_TBL); + return (EEXIST); + } + return (0); +} + +int +ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint8_t plen, uint8_t mlen, uint8_t type) +{ + struct radix_node_head *rnh, **rnh_ptr; + struct table_entry *ent; + in_addr_t addr; + struct sockaddr_in sa, mask; + struct sockaddr *sa_ptr, *mask_ptr; + char c; + + if (tbl >= V_fw_tables_max) + return (EINVAL); + + switch (type) { + case IPFW_TABLE_CIDR: + if (plen == sizeof(in_addr_t)) { + /* Set 'total' structure length */ + KEY_LEN(sa) = KEY_LEN_INET; + KEY_LEN(mask) = KEY_LEN_INET; + mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); + addr = *((in_addr_t *)paddr); + sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; + rnh_ptr = &ch->tables[tbl]; + sa_ptr = (struct sockaddr *)&sa; + mask_ptr = (struct sockaddr *)&mask; +#ifdef INET6 + } else if (plen == sizeof(struct in6_addr)) { + /* IPv6 case */ + if (mlen > 128) + return (EINVAL); + struct sockaddr_in6 sa6, mask6; + memset(&sa6, 0, sizeof(struct sockaddr_in6)); + memset(&mask6, 0, sizeof(struct sockaddr_in6)); + /* Set 'total' structure length */ + KEY_LEN(sa6) = KEY_LEN_INET6; + KEY_LEN(mask6) = KEY_LEN_INET6; + ipv6_writemask(&mask6.sin6_addr, mlen); + memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); + APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); + rnh_ptr = &ch->xtables[tbl]; + sa_ptr = (struct sockaddr *)&sa6; + mask_ptr = (struct sockaddr *)&mask6; +#endif + } else { + /* Unknown CIDR type */ + return (EINVAL); + } + break; + + case IPFW_TABLE_INTERFACE: + /* Check if string is terminated */ + c = ((char *)paddr)[IF_NAMESIZE - 1]; + ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; + if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) + return (EINVAL); + + struct xaddr_iface ifname, ifmask; + memset(&ifname, 0, sizeof(ifname)); + + /* Include last \0 into comparison */ + mlen++; + + /* Set 'total' structure length */ + KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; + KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ +#if 0 + memset(ifmask.ifname, 0xFF, IF_NAMESIZE); + mask_ptr = (struct sockaddr *)&ifmask; +#endif + mask_ptr = NULL; + memcpy(ifname.ifname, paddr, mlen); + /* Set pointers */ + rnh_ptr = &ch->xtables[tbl]; + sa_ptr = (struct sockaddr *)&ifname; + + break; + + default: + return (EINVAL); + } + + IPFW_WLOCK(ch); + if ((rnh = *rnh_ptr) == NULL) { + IPFW_WUNLOCK(ch); + return (ESRCH); + } + + if (ch->tabletype[tbl] != type) { + IPFW_WUNLOCK(ch); + return (EINVAL); + } + + ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); + IPFW_WUNLOCK(ch); + + if (ent == NULL) + return (ESRCH); + + free(ent, M_IPFW_TBL); + return (0); +} + +static int +flush_table_entry(struct radix_node *rn, void *arg) +{ + struct radix_node_head * const rnh = arg; + struct table_entry *ent; + + ent = (struct table_entry *) + rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); + if (ent != NULL) + free(ent, M_IPFW_TBL); + return (0); +} + +int +ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) +{ + struct radix_node_head *rnh, *xrnh; + + if (tbl >= V_fw_tables_max) + return (EINVAL); + + /* + * We free both (IPv4 and extended) radix trees and + * clear table type here to permit table to be reused + * for different type without module reload + */ + + IPFW_WLOCK(ch); + /* Set IPv4 table pointer to zero */ + if ((rnh = ch->tables[tbl]) != NULL) + ch->tables[tbl] = NULL; + /* Set extended table pointer to zero */ + if ((xrnh = ch->xtables[tbl]) != NULL) + ch->xtables[tbl] = NULL; + /* Zero table type */ + ch->tabletype[tbl] = 0; + IPFW_WUNLOCK(ch); + + if (rnh != NULL) { + rnh->rnh_walktree(rnh, flush_table_entry, rnh); + rn_detachhead((void **)&rnh); + } + + if (xrnh != NULL) { + xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); + rn_detachhead((void **)&xrnh); + } + + return (0); +} + +void +ipfw_destroy_tables(struct ip_fw_chain *ch) +{ + uint16_t tbl; + + /* Flush all tables */ + for (tbl = 0; tbl < V_fw_tables_max; tbl++) + ipfw_flush_table(ch, tbl); + + /* Free pointers itself */ + free(ch->tables, M_IPFW); + free(ch->xtables, M_IPFW); + free(ch->tabletype, M_IPFW); +} + +int +ipfw_init_tables(struct ip_fw_chain *ch) +{ + /* Allocate pointers */ + ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); + ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); + ch->tabletype = malloc(V_fw_tables_max * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); + return (0); +} + +int +ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) +{ + struct radix_node_head **tables, **xtables, *rnh; + struct radix_node_head **tables_old, **xtables_old; + uint8_t *tabletype, *tabletype_old; + unsigned int ntables_old, tbl; + + /* Check new value for validity */ + if (ntables > IPFW_TABLES_MAX) + ntables = IPFW_TABLES_MAX; + + /* Allocate new pointers */ + tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); + xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); + tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); + + IPFW_WLOCK(ch); + + tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; + + /* Copy old table pointers */ + memcpy(tables, ch->tables, sizeof(void *) * tbl); + memcpy(xtables, ch->xtables, sizeof(void *) * tbl); + memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl); + + /* Change pointers and number of tables */ + tables_old = ch->tables; + xtables_old = ch->xtables; + tabletype_old = ch->tabletype; + ch->tables = tables; + ch->xtables = xtables; + ch->tabletype = tabletype; + + ntables_old = V_fw_tables_max; + V_fw_tables_max = ntables; + + IPFW_WUNLOCK(ch); + + /* Check if we need to destroy radix trees */ + if (ntables < ntables_old) { + for (tbl = ntables; tbl < ntables_old; tbl++) { + if ((rnh = tables_old[tbl]) != NULL) { + rnh->rnh_walktree(rnh, flush_table_entry, rnh); + rn_detachhead((void **)&rnh); + } + + if ((rnh = xtables_old[tbl]) != NULL) { + rnh->rnh_walktree(rnh, flush_table_entry, rnh); + rn_detachhead((void **)&rnh); + } + } + } + + /* Free old pointers */ + free(tables_old, M_IPFW); + free(xtables_old, M_IPFW); + free(tabletype_old, M_IPFW); + + return (0); +} + +int +ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, + uint32_t *val) +{ + struct radix_node_head *rnh; + struct table_entry *ent; + struct sockaddr_in sa; + + if (tbl >= V_fw_tables_max) + return (0); + if ((rnh = ch->tables[tbl]) == NULL) + return (0); + KEY_LEN(sa) = KEY_LEN_INET; + sa.sin_addr.s_addr = addr; + ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); + if (ent != NULL) { + *val = ent->value; + return (1); + } + return (0); +} + +int +ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint32_t *val, int type) +{ + struct radix_node_head *rnh; + struct table_xentry *xent; + struct sockaddr_in6 sa6; + struct xaddr_iface iface; + + if (tbl >= V_fw_tables_max) + return (0); + if ((rnh = ch->xtables[tbl]) == NULL) + return (0); + + switch (type) { + case IPFW_TABLE_CIDR: + KEY_LEN(sa6) = KEY_LEN_INET6; + memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); + xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + break; + + case IPFW_TABLE_INTERFACE: + KEY_LEN(iface) = KEY_LEN_IFACE + + strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ + xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + break; + + default: + return (0); + } + + if (xent != NULL) { + *val = xent->value; + return (1); + } + return (0); +} + +static int +count_table_entry(struct radix_node *rn, void *arg) +{ + u_int32_t * const cnt = arg; + + (*cnt)++; + return (0); +} + +int +ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) +{ + struct radix_node_head *rnh; + + if (tbl >= V_fw_tables_max) + return (EINVAL); + *cnt = 0; + if ((rnh = ch->tables[tbl]) == NULL) + return (0); + rnh->rnh_walktree(rnh, count_table_entry, cnt); + return (0); +} + +static int +dump_table_entry(struct radix_node *rn, void *arg) +{ + struct table_entry * const n = (struct table_entry *)rn; + ipfw_table * const tbl = arg; + ipfw_table_entry *ent; + + if (tbl->cnt == tbl->size) + return (1); + ent = &tbl->ent[tbl->cnt]; + ent->tbl = tbl->tbl; + if (in_nullhost(n->mask.sin_addr)) + ent->masklen = 0; + else + ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); + ent->addr = n->addr.sin_addr.s_addr; + ent->value = n->value; + tbl->cnt++; + return (0); +} + +int +ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) +{ + struct radix_node_head *rnh; + + if (tbl->tbl >= V_fw_tables_max) + return (EINVAL); + tbl->cnt = 0; + if ((rnh = ch->tables[tbl->tbl]) == NULL) + return (0); + rnh->rnh_walktree(rnh, dump_table_entry, tbl); + return (0); +} + +static int +count_table_xentry(struct radix_node *rn, void *arg) +{ + uint32_t * const cnt = arg; + + (*cnt) += sizeof(ipfw_table_xentry); + return (0); +} + +int +ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) +{ + struct radix_node_head *rnh; + + if (tbl >= V_fw_tables_max) + return (EINVAL); + *cnt = 0; + if ((rnh = ch->tables[tbl]) != NULL) + rnh->rnh_walktree(rnh, count_table_xentry, cnt); + if ((rnh = ch->xtables[tbl]) != NULL) + rnh->rnh_walktree(rnh, count_table_xentry, cnt); + /* Return zero if table is empty */ + if (*cnt > 0) + (*cnt) += sizeof(ipfw_xtable); + return (0); +} + + +static int +dump_table_xentry_base(struct radix_node *rn, void *arg) +{ + struct table_entry * const n = (struct table_entry *)rn; + ipfw_xtable * const tbl = arg; + ipfw_table_xentry *xent; + + /* Out of memory, returning */ + if (tbl->cnt == tbl->size) + return (1); + xent = &tbl->xent[tbl->cnt]; + xent->len = sizeof(ipfw_table_xentry); + xent->tbl = tbl->tbl; + if (in_nullhost(n->mask.sin_addr)) + xent->masklen = 0; + else + xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); + /* Save IPv4 address as deprecated IPv6 compatible */ + xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; + xent->value = n->value; + tbl->cnt++; + return (0); +} + +static int +dump_table_xentry_extended(struct radix_node *rn, void *arg) +{ + struct table_xentry * const n = (struct table_xentry *)rn; + ipfw_xtable * const tbl = arg; + ipfw_table_xentry *xent; +#ifdef INET6 + int i; + uint32_t *v; +#endif + /* Out of memory, returning */ + if (tbl->cnt == tbl->size) + return (1); + xent = &tbl->xent[tbl->cnt]; + xent->len = sizeof(ipfw_table_xentry); + xent->tbl = tbl->tbl; + + switch (tbl->type) { +#ifdef INET6 + case IPFW_TABLE_CIDR: + /* Count IPv6 mask */ + v = (uint32_t *)&n->m.mask6.sin6_addr; + for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++) + xent->masklen += bitcount32(*v); + memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr)); + break; +#endif + case IPFW_TABLE_INTERFACE: + /* Assume exact mask */ + xent->masklen = 8 * IF_NAMESIZE; + memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); + break; + + default: + /* unknown, skip entry */ + return (0); + } + + xent->value = n->value; + tbl->cnt++; + return (0); +} + +int +ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl) +{ + struct radix_node_head *rnh; + + if (tbl->tbl >= V_fw_tables_max) + return (EINVAL); + tbl->cnt = 0; + tbl->type = ch->tabletype[tbl->tbl]; + if ((rnh = ch->tables[tbl->tbl]) != NULL) + rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl); + if ((rnh = ch->xtables[tbl->tbl]) != NULL) + rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl); + return (0); +} + +/* end of file */ diff --git a/sys/netpfil/ipfw/test/Makefile b/sys/netpfil/ipfw/test/Makefile new file mode 100644 index 0000000000000..c556a4bf3d517 --- /dev/null +++ b/sys/netpfil/ipfw/test/Makefile @@ -0,0 +1,51 @@ +# +# $FreeBSD$ +# +# Makefile for building userland tests +# this is written in a form compatible with gmake + +SCHED_SRCS = test_dn_sched.c +SCHED_SRCS += dn_sched_fifo.c +SCHED_SRCS += dn_sched_prio.c +SCHED_SRCS += dn_sched_qfq.c +SCHED_SRCS += dn_sched_rr.c +SCHED_SRCS += dn_sched_wf2q.c +SCHED_SRCS += dn_heap.c +SCHED_SRCS += main.c + +SCHED_OBJS=$(SCHED_SRCS:.c=.o) + +HEAP_SRCS = dn_heap.c test_dn_heap.c +HEAP_OBJS=$(HEAP_SRCS:.c=.o) + +VPATH= .:.. + +CFLAGS = -I.. -I. -Wall -Werror -O3 -DIPFW +TARGETS= test_sched # no test_heap by default + +all: $(TARGETS) + +test_heap : $(HEAP_OBJS) + $(CC) -o $@ $(HEAP_OBJS) + +test_sched : $(SCHED_OBJS) + $(CC) -o $@ $(SCHED_OBJS) + +$(SCHED_OBJS): dn_test.h +main.o: mylist.h + +clean: + - rm *.o $(TARGETS) *.core + +ALLSRCS = $(SCHED_SRCS) dn_test.h mylist.h \ + dn_sched.h dn_heap.h ip_dn_private.h Makefile +TMPBASE = /tmp/testXYZ +TMPDIR = $(TMPBASE)/test + +tgz: + -rm -rf $(TMPDIR) + mkdir -p $(TMPDIR) + -cp -p $(ALLSRCS) $(TMPDIR) + -(cd ..; cp -p $(ALLSRCS) $(TMPDIR)) + ls -la $(TMPDIR) + (cd $(TMPBASE); tar cvzf /tmp/test.tgz test) diff --git a/sys/netpfil/ipfw/test/dn_test.h b/sys/netpfil/ipfw/test/dn_test.h new file mode 100644 index 0000000000000..4e079bc4d68b8 --- /dev/null +++ b/sys/netpfil/ipfw/test/dn_test.h @@ -0,0 +1,175 @@ +/* + * $FreeBSD$ + * + * userspace compatibility code for dummynet schedulers + */ + +#ifndef _DN_TEST_H +#define _DN_TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include /* bzero, ffs, ... */ +#include /* strcmp */ +#include +#include +#include + +extern int debug; +#define ND(fmt, args...) do {} while (0) +#define D1(fmt, args...) do {} while (0) +#define D(fmt, args...) fprintf(stderr, "%-8s " fmt "\n", \ + __FUNCTION__, ## args) +#define DX(lev, fmt, args...) do { \ + if (debug > lev) D(fmt, ## args); } while (0) + + +#ifndef offsetof +#define offsetof(t,m) (int)((&((t *)0L)->m)) +#endif + +#include + +/* prevent include of other system headers */ +#define _NETINET_IP_VAR_H_ /* ip_fw_args */ +#define _IPFW2_H +#define _SYS_MBUF_H_ + +enum { + DN_QUEUE, +}; + +enum { + DN_SCHED_FIFO, + DN_SCHED_WF2QP, +}; + +struct dn_id { + int type, subtype, len, id; +}; + +struct dn_fs { + int par[4]; /* flowset parameters */ + + /* simulation entries. + * 'index' is not strictly necessary + * y is used for the inverse mapping , + */ + int index; + int y; /* inverse mapping */ + int base_y; /* inverse mapping */ + int next_y; /* inverse mapping */ + int n_flows; + int first_flow; + int next_flow; /* first_flow + n_flows */ + /* + * when generating, let 'cur' go from 0 to n_flows-1, + * then point to flow first_flow + cur + */ + int cur; +}; + +struct dn_sch { +}; + +struct dn_flow { + struct dn_id oid; + int length; + int len_bytes; + int drops; + uint64_t tot_bytes; + uint32_t flow_id; + struct list_head h; /* used by the generator */ +}; + +struct dn_link { +}; + +struct ip_fw_args { +}; + +struct mbuf { + struct { + int len; + } m_pkthdr; + struct mbuf *m_nextpkt; + int flow_id; /* for testing, index of a flow */ + //int flowset_id; /* for testing, index of a flowset */ + void *cfg; /* config args */ +}; + +#define MALLOC_DECLARE(x) +#define KASSERT(x, y) do { if (!(x)) printf y ; exit(0); } while (0) +struct ipfw_flow_id { +}; + +typedef void * module_t; + +struct _md_t { + const char *name; + int (*f)(module_t, int, void *); + void *p; +}; + +typedef struct _md_t moduledata_t; + +#define DECLARE_MODULE(name, b, c, d) \ + moduledata_t *_g_##name = & b +#define MODULE_DEPEND(a, b, c, d, e) + +#ifdef IPFW +#include +#include +#include +#else +struct dn_queue { + struct dn_fsk *fs; /* parent flowset. */ + struct dn_sch_inst *_si; /* parent sched instance. */ +}; +struct dn_schk { +}; +struct dn_fsk { + struct dn_fs fs; + struct dn_schk *sched; +}; +struct dn_sch_inst { + struct dn_schk *sched; +}; +struct dn_alg { + int type; + const char *name; + void *enqueue, *dequeue; + int q_datalen, si_datalen, schk_datalen; + int (*config)(struct dn_schk *); + int (*new_sched)(struct dn_sch_inst *); + int (*new_fsk)(struct dn_fsk *); + int (*new_queue)(struct dn_queue *q); +}; + +#endif + +#ifndef __FreeBSD__ +int fls(int); +#endif + +static inline void +mq_append(struct mq *q, struct mbuf *m) +{ + if (q->head == NULL) + q->head = m; + else + q->tail->m_nextpkt = m; + q->tail = m; + m->m_nextpkt = NULL; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DN_TEST_H */ diff --git a/sys/netpfil/ipfw/test/main.c b/sys/netpfil/ipfw/test/main.c new file mode 100644 index 0000000000000..be9fdf53612c3 --- /dev/null +++ b/sys/netpfil/ipfw/test/main.c @@ -0,0 +1,636 @@ +/* + * $FreeBSD$ + * + * Testing program for schedulers + * + * The framework include a simple controller which, at each + * iteration, decides whether we can enqueue and/or dequeue. + * Then the mainloop runs the required number of tests, + * keeping track of statistics. + */ + +#include "dn_test.h" + +struct q_list { + struct list_head h; +}; + +struct cfg_s { + int ac; + char * const *av; + + const char *name; + int loops; + struct timeval time; + + /* running counters */ + uint32_t _enqueue; + uint32_t drop; + uint32_t pending; + uint32_t dequeue; + + /* generator parameters */ + int th_min, th_max; + int maxburst; + int lmin, lmax; /* packet len */ + int flows; /* number of flows */ + int flowsets; /* number of flowsets */ + int wsum; /* sum of weights of all flows */ + int max_y; /* max random number in the generation */ + int cur_y, cur_fs; /* used in generation, between 0 and max_y - 1 */ + const char *fs_config; /* flowset config */ + int can_dequeue; + int burst; /* count of packets sent in a burst */ + struct mbuf *tosend; /* packet to send -- also flag to enqueue */ + + struct mbuf *freelist; + + struct mbuf *head, *tail; /* a simple tailq */ + + /* scheduler hooks */ + int (*enq)(struct dn_sch_inst *, struct dn_queue *, + struct mbuf *); + struct mbuf * (*deq)(struct dn_sch_inst *); + /* size of the three fields including sched-specific areas */ + int schk_len; + int q_len; /* size of a queue including sched-fields */ + int si_len; /* size of a sch_inst including sched-fields */ + char *q; /* array of flow queues */ + /* use a char* because size is variable */ + struct dn_fsk *fs; /* array of flowsets */ + struct dn_sch_inst *si; + struct dn_schk *sched; + + /* generator state */ + int state; /* 0 = going up, 1: going down */ + + /* + * We keep lists for each backlog level, and always serve + * the one with shortest backlog. llmask contains a bitmap + * of lists, and ll are the heads of the lists. The last + * entry (BACKLOG) contains all entries considered 'full' + * XXX to optimize things, entry i could contain queues with + * 2^{i-1}+1 .. 2^i entries. + */ +#define BACKLOG 30 + uint32_t llmask; + struct list_head ll[BACKLOG + 10]; +}; + +/* FI2Q and Q2FI converts from flow_id to dn_queue and back. + * We cannot easily use pointer arithmetic because it is variable size. + */ +#define FI2Q(c, i) ((struct dn_queue *)((c)->q + (c)->q_len * (i))) +#define Q2FI(c, q) (((char *)(q) - (c)->q)/(c)->q_len) + +int debug = 0; + +struct dn_parms dn_cfg; + +static void controller(struct cfg_s *c); + +/* release a packet: put the mbuf in the freelist, and the queue in + * the bucket. + */ +int +drop(struct cfg_s *c, struct mbuf *m) +{ + struct dn_queue *q; + int i; + + c->drop++; + q = FI2Q(c, m->flow_id); + i = q->ni.length; // XXX or ffs... + + ND("q %p id %d current length %d", q, m->flow_id, i); + if (i < BACKLOG) { + struct list_head *h = &q->ni.h; + c->llmask &= ~(1<<(i+1)); + c->llmask |= (1<<(i)); + list_del(h); + list_add_tail(h, &c->ll[i]); + } + m->m_nextpkt = c->freelist; + c->freelist = m; + return 0; +} + +/* dequeue returns NON-NULL when a packet is dropped */ +static int +enqueue(struct cfg_s *c, void *_m) +{ + struct mbuf *m = _m; + if (c->enq) + return c->enq(c->si, FI2Q(c, m->flow_id), m); + if (c->head == NULL) + c->head = m; + else + c->tail->m_nextpkt = m; + c->tail = m; + return 0; /* default - success */ +} + +/* dequeue returns NON-NULL when a packet is available */ +static void * +dequeue(struct cfg_s *c) +{ + struct mbuf *m; + if (c->deq) + return c->deq(c->si); + if ((m = c->head)) { + m = c->head; + c->head = m->m_nextpkt; + m->m_nextpkt = NULL; + } + return m; +} + +static int +mainloop(struct cfg_s *c) +{ + int i; + struct mbuf *m; + + for (i=0; i < c->loops; i++) { + /* implement histeresis */ + controller(c); + DX(3, "loop %d enq %d send %p rx %d", + i, c->_enqueue, c->tosend, c->can_dequeue); + if ( (m = c->tosend) ) { + c->_enqueue++; + if (enqueue(c, m)) { + drop(c, m); + ND("loop %d enqueue fail", i ); + } else { + ND("enqueue ok"); + c->pending++; + } + } + if (c->can_dequeue) { + c->dequeue++; + if ((m = dequeue(c))) { + c->pending--; + drop(c, m); + c->drop--; /* compensate */ + } + } + } + DX(1, "mainloop ends %d", i); + return 0; +} + +int +dump(struct cfg_s *c) +{ + int i; + struct dn_queue *q; + + for (i=0; i < c->flows; i++) { + q = FI2Q(c, i); + DX(1, "queue %4d tot %10lld", i, q->ni.tot_bytes); + } + DX(1, "done %d loops\n", c->loops); + return 0; +} + +/* interpret a number in human form */ +static long +getnum(const char *s, char **next, const char *key) +{ + char *end = NULL; + long l; + + if (next) /* default */ + *next = NULL; + if (s && *s) { + DX(3, "token is <%s> %s", s, key ? key : "-"); + l = strtol(s, &end, 0); + } else { + DX(3, "empty string"); + l = -1; + } + if (l < 0) { + DX(2, "invalid %s for %s", s ? s : "NULL", (key ? key : "") ); + return 0; // invalid + } + if (!end || !*end) + return l; + if (*end == 'n') + l = -l; /* multiply by n */ + else if (*end == 'K') + l = l*1000; + else if (*end == 'M') + l = l*1000000; + else if (*end == 'k') + l = l*1024; + else if (*end == 'm') + l = l*1024*1024; + else if (*end == 'w') + ; + else {/* not recognized */ + D("suffix %s for %s, next %p", end, key, next); + end--; + } + end++; + DX(3, "suffix now %s for %s, next %p", end, key, next); + if (next && *end) { + DX(3, "setting next to %s for %s", end, key); + *next = end; + } + return l; +} + +/* + * flowsets are a comma-separated list of + * weight:maxlen:flows + * indicating how many flows are hooked to that fs. + * Both weight and range can be min-max-steps. + * In a first pass we just count the number of flowsets and flows, + * in a second pass we complete the setup. + */ +static void +parse_flowsets(struct cfg_s *c, const char *fs, int pass) +{ + char *s, *cur, *next; + int n_flows = 0, n_fs = 0, wsum = 0; + int i, j; + struct dn_fs *prev = NULL; + + DX(3, "--- pass %d flows %d flowsets %d", pass, c->flows, c->flowsets); + if (pass == 0) + c->fs_config = fs; + s = c->fs_config ? strdup(c->fs_config) : NULL; + if (s == NULL) { + if (pass == 0) + D("no fsconfig"); + return; + } + for (next = s; (cur = strsep(&next, ","));) { + char *p = NULL; + int w, w_h, w_steps, wi; + int len, len_h, l_steps, li; + int flows; + + w = getnum(strsep(&cur, ":"), &p, "weight"); + if (w <= 0) + w = 1; + w_h = p ? getnum(p+1, &p, "weight_max") : w; + w_steps = p ? getnum(p+1, &p, "w_steps") : (w_h == w ?1:2); + len = getnum(strsep(&cur, ":"), &p, "len"); + if (len <= 0) + len = 1000; + len_h = p ? getnum(p+1, &p, "len_max") : len; + l_steps = p ? getnum(p+1, &p, "l_steps") : (len_h == len ? 1 : 2); + flows = getnum(strsep(&cur, ":"), NULL, "flows"); + if (flows == 0) + flows = 1; + DX(4, "weight %d..%d (%d) len %d..%d (%d) flows %d", + w, w_h, w_steps, len, len_h, l_steps, flows); + if (w == 0 || w_h < w || len == 0 || len_h < len || + flows == 0) { + DX(4,"wrong parameters %s", fs); + return; + } + n_flows += flows * w_steps * l_steps; + for (i = 0; i < w_steps; i++) { + wi = w + ((w_h - w)* i)/(w_steps == 1 ? 1 : (w_steps-1)); + for (j = 0; j < l_steps; j++, n_fs++) { + struct dn_fs *fs = &c->fs[n_fs].fs; // tentative + int x; + + li = len + ((len_h - len)* j)/(l_steps == 1 ? 1 : (l_steps-1)); + x = (wi*2048)/li; + DX(3, "----- fs %4d weight %4d lmax %4d X %4d flows %d", + n_fs, wi, li, x, flows); + if (pass == 0) + continue; + if (c->fs == NULL || c->flowsets <= n_fs) { + D("error in number of flowsets"); + return; + } + wsum += wi * flows; + fs->par[0] = wi; + fs->par[1] = li; + fs->index = n_fs; + fs->n_flows = flows; + fs->cur = fs->first_flow = prev==NULL ? 0 : prev->next_flow; + fs->next_flow = fs->first_flow + fs->n_flows; + fs->y = x * flows; + fs->base_y = (prev == NULL) ? 0 : prev->next_y; + fs->next_y = fs->base_y + fs->y; + prev = fs; + } + } + } + c->max_y = prev ? prev->base_y + prev->y : 0; + c->flows = n_flows; + c->flowsets = n_fs; + c->wsum = wsum; + if (pass == 0) + return; + + /* now link all flows to their parent flowsets */ + DX(1,"%d flows on %d flowsets max_y %d", c->flows, c->flowsets, c->max_y); + for (i=0; i < c->flowsets; i++) { + struct dn_fs *fs = &c->fs[i].fs; + DX(1, "fs %3d w %5d l %4d flow %5d .. %5d y %6d .. %6d", + i, fs->par[0], fs->par[1], + fs->first_flow, fs->next_flow, + fs->base_y, fs->next_y); + for (j = fs->first_flow; j < fs->next_flow; j++) { + struct dn_queue *q = FI2Q(c, j); + q->fs = &c->fs[i]; + } + } +} + +static int +init(struct cfg_s *c) +{ + int i; + int ac = c->ac; + char * const *av = c->av; + + c->si_len = sizeof(struct dn_sch_inst); + c->q_len = sizeof(struct dn_queue); + moduledata_t *mod = NULL; + struct dn_alg *p = NULL; + + c->th_min = 0; + c->th_max = -20;/* 20 packets per flow */ + c->lmin = c->lmax = 1280; /* packet len */ + c->flows = 1; + c->flowsets = 1; + c->name = "null"; + ac--; av++; + while (ac > 1) { + if (!strcmp(*av, "-n")) { + c->loops = getnum(av[1], NULL, av[0]); + } else if (!strcmp(*av, "-d")) { + debug = atoi(av[1]); + } else if (!strcmp(*av, "-alg")) { + extern moduledata_t *_g_dn_fifo; + extern moduledata_t *_g_dn_wf2qp; + extern moduledata_t *_g_dn_rr; + extern moduledata_t *_g_dn_qfq; +#ifdef WITH_KPS + extern moduledata_t *_g_dn_kps; +#endif + if (!strcmp(av[1], "rr")) + mod = _g_dn_rr; + else if (!strcmp(av[1], "wf2qp")) + mod = _g_dn_wf2qp; + else if (!strcmp(av[1], "fifo")) + mod = _g_dn_fifo; + else if (!strcmp(av[1], "qfq")) + mod = _g_dn_qfq; +#ifdef WITH_KPS + else if (!strcmp(av[1], "kps")) + mod = _g_dn_kps; +#endif + else + mod = NULL; + c->name = mod ? mod->name : "NULL"; + DX(3, "using scheduler %s", c->name); + } else if (!strcmp(*av, "-len")) { + c->lmin = getnum(av[1], NULL, av[0]); + c->lmax = c->lmin; + DX(3, "setting max to %d", c->th_max); + } else if (!strcmp(*av, "-burst")) { + c->maxburst = getnum(av[1], NULL, av[0]); + DX(3, "setting max to %d", c->th_max); + } else if (!strcmp(*av, "-qmax")) { + c->th_max = getnum(av[1], NULL, av[0]); + DX(3, "setting max to %d", c->th_max); + } else if (!strcmp(*av, "-qmin")) { + c->th_min = getnum(av[1], NULL, av[0]); + DX(3, "setting min to %d", c->th_min); + } else if (!strcmp(*av, "-flows")) { + c->flows = getnum(av[1], NULL, av[0]); + DX(3, "setting flows to %d", c->flows); + } else if (!strcmp(*av, "-flowsets")) { + parse_flowsets(c, av[1], 0); + DX(3, "setting flowsets to %d", c->flowsets); + } else { + D("option %s not recognised, ignore", *av); + } + ac -= 2; av += 2; + } + if (c->maxburst <= 0) + c->maxburst = 1; + if (c->loops <= 0) + c->loops = 1; + if (c->flows <= 0) + c->flows = 1; + if (c->flowsets <= 0) + c->flowsets = 1; + if (c->lmin <= 0) + c->lmin = 1; + if (c->lmax <= 0) + c->lmax = 1; + /* multiply by N */ + if (c->th_min < 0) + c->th_min = c->flows * -c->th_min; + if (c->th_max < 0) + c->th_max = c->flows * -c->th_max; + if (c->th_max <= c->th_min) + c->th_max = c->th_min + 1; + if (mod) { + p = mod->p; + DX(3, "using module %s f %p p %p", mod->name, mod->f, mod->p); + DX(3, "modname %s ty %d", p->name, p->type); + c->enq = p->enqueue; + c->deq = p->dequeue; + c->si_len += p->si_datalen; + c->q_len += p->q_datalen; + c->schk_len += p->schk_datalen; + } + /* allocate queues, flowsets and one scheduler */ + c->q = calloc(c->flows, c->q_len); + c->fs = calloc(c->flowsets, sizeof(struct dn_fsk)); + c->si = calloc(1, c->si_len); + c->sched = calloc(c->flows, c->schk_len); + if (c->q == NULL || c->fs == NULL) { + D("error allocating memory for flows"); + exit(1); + } + c->si->sched = c->sched; + if (p) { + if (p->config) + p->config(c->sched); + if (p->new_sched) + p->new_sched(c->si); + } + /* parse_flowsets links queues to their flowsets */ + parse_flowsets(c, av[1], 1); + /* complete the work calling new_fsk */ + for (i = 0; i < c->flowsets; i++) { + if (c->fs[i].fs.par[1] == 0) + c->fs[i].fs.par[1] = 1000; /* default pkt len */ + c->fs[i].sched = c->sched; + if (p && p->new_fsk) + p->new_fsk(&c->fs[i]); + } + + /* initialize the lists for the generator, and put + * all flows in the list for backlog = 0 + */ + for (i=0; i <= BACKLOG+5; i++) + INIT_LIST_HEAD(&c->ll[i]); + + for (i = 0; i < c->flows; i++) { + struct dn_queue *q = FI2Q(c, i); + if (q->fs == NULL) + q->fs = &c->fs[0]; /* XXX */ + q->_si = c->si; + if (p && p->new_queue) + p->new_queue(q); + INIT_LIST_HEAD(&q->ni.h); + list_add_tail(&q->ni.h, &c->ll[0]); + } + c->llmask = 1; + return 0; +} + + +int +main(int ac, char *av[]) +{ + struct cfg_s c; + struct timeval end; + double ll; + int i; + char msg[40]; + + bzero(&c, sizeof(c)); + c.ac = ac; + c.av = av; + init(&c); + gettimeofday(&c.time, NULL); + mainloop(&c); + gettimeofday(&end, NULL); + end.tv_sec -= c.time.tv_sec; + end.tv_usec -= c.time.tv_usec; + if (end.tv_usec < 0) { + end.tv_usec += 1000000; + end.tv_sec--; + } + c.time = end; + ll = end.tv_sec*1000000 + end.tv_usec; + ll *= 1000; /* convert to nanoseconds */ + ll /= c._enqueue; + sprintf(msg, "1::%d", c.flows); + D("%-8s n %d %d time %d.%06d %8.3f qlen %d %d flows %s drops %d", + c.name, c._enqueue, c.loops, + (int)c.time.tv_sec, (int)c.time.tv_usec, ll, + c.th_min, c.th_max, + c.fs_config ? c.fs_config : msg, c.drop); + dump(&c); + DX(1, "done ac %d av %p", ac, av); + for (i=0; i < ac; i++) + DX(1, "arg %d %s", i, av[i]); + return 0; +} + +/* + * The controller decides whether in this iteration we should send + * (the packet is in c->tosend) and/or receive (flag c->can_dequeue) + */ +static void +controller(struct cfg_s *c) +{ + struct mbuf *m; + struct dn_fs *fs; + int flow_id; + + /* histeresis between max and min */ + if (c->state == 0 && c->pending >= c->th_max) + c->state = 1; + else if (c->state == 1 && c->pending <= c->th_min) + c->state = 0; + ND(1, "state %d pending %2d", c->state, c->pending); + c->can_dequeue = c->state; + c->tosend = NULL; + if (c->state) + return; + + if (1) { + int i; + struct dn_queue *q; + struct list_head *h; + + i = ffs(c->llmask) - 1; + if (i < 0) { + DX(2, "no candidate"); + c->can_dequeue = 1; + return; + } + h = &c->ll[i]; + ND(1, "backlog %d p %p prev %p next %p", i, h, h->prev, h->next); + q = list_first_entry(h, struct dn_queue, ni.h); + list_del(&q->ni.h); + flow_id = Q2FI(c, q); + DX(2, "extracted flow %p %d backlog %d", q, flow_id, i); + if (list_empty(h)) { + ND(2, "backlog %d empty", i); + c->llmask &= ~(1<ni.h, h+1); + ND(1, " after %d p %p prev %p next %p", i+1, h+1, h[1].prev, h[1].next); + if (i < BACKLOG) { + ND(2, "backlog %d full", i+1); + c->llmask |= 1<<(1+i); + } + fs = &q->fs->fs; + c->cur_fs = q->fs - c->fs; + fs->cur = flow_id; + } else { + /* XXX this does not work ? */ + /* now decide whom to send the packet, and the length */ + /* lookup in the flow table */ + if (c->cur_y >= c->max_y) { /* handle wraparound */ + c->cur_y = 0; + c->cur_fs = 0; + } + fs = &c->fs[c->cur_fs].fs; + flow_id = fs->cur++; + if (fs->cur >= fs->next_flow) + fs->cur = fs->first_flow; + c->cur_y++; + if (c->cur_y >= fs->next_y) + c->cur_fs++; + } + + /* construct a packet */ + if (c->freelist) { + m = c->tosend = c->freelist; + c->freelist = c->freelist->m_nextpkt; + } else { + m = c->tosend = calloc(1, sizeof(struct mbuf)); + } + if (m == NULL) + return; + + m->cfg = c; + m->m_nextpkt = NULL; + m->m_pkthdr.len = fs->par[1]; // XXX maxlen + m->flow_id = flow_id; + + ND(2,"y %6d flow %5d fs %3d weight %4d len %4d", + c->cur_y, m->flow_id, c->cur_fs, + fs->par[0], m->m_pkthdr.len); + +} + +/* +Packet allocation: +to achieve a distribution that matches weights, for each X=w/lmax class +we should generate a number of packets proportional to Y = X times the number +of flows in the class. +So we construct an array with the cumulative distribution of Y's, +and use it to identify the flow via inverse mapping (if the Y's are +not too many we can use an array for the lookup). In practice, +each flow will have X entries [virtually] pointing to it. + +*/ diff --git a/sys/netpfil/ipfw/test/mylist.h b/sys/netpfil/ipfw/test/mylist.h new file mode 100644 index 0000000000000..6247f32ea4e44 --- /dev/null +++ b/sys/netpfil/ipfw/test/mylist.h @@ -0,0 +1,49 @@ +/* + * $FreeBSD$ + * + * linux-like bidirectional lists + */ + +#ifndef _MYLIST_H +#define _MYLIST_H +struct list_head { + struct list_head *prev, *next; +}; + +#define INIT_LIST_HEAD(l) do { (l)->prev = (l)->next = (l); } while (0) +#define list_empty(l) ( (l)->next == l ) +static inline void +__list_add(struct list_head *o, struct list_head *prev, + struct list_head *next) +{ + next->prev = o; + o->next = next; + o->prev = prev; + prev->next = o; +} + +static inline void +list_add_tail(struct list_head *o, struct list_head *head) +{ + __list_add(o, head->prev, head); +} + +#define list_first_entry(pL, ty, member) \ + (ty *)((char *)((pL)->next) - offsetof(ty, member)) + +static inline void +__list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void +list_del(struct list_head *entry) +{ + ND("called on %p", entry); + __list_del(entry->prev, entry->next); + entry->next = entry->prev = NULL; +} + +#endif /* _MYLIST_H */ diff --git a/sys/netpfil/ipfw/test/test_dn_heap.c b/sys/netpfil/ipfw/test/test_dn_heap.c new file mode 100644 index 0000000000000..d460cf2ff36b7 --- /dev/null +++ b/sys/netpfil/ipfw/test/test_dn_heap.c @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa + * 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. + */ + +/* + * Userland code for testing binary heaps and hash tables + * + * $FreeBSD$ + */ + +#include +#include + +#include +#include +#include + +#include "dn_heap.h" +#define log(x, arg...) fprintf(stderr, ## arg) +#define panic(x...) fprintf(stderr, ## x), exit(1) + +#include + +struct x { + struct x *ht_link; + char buf[0]; +}; + +uint32_t hf(uintptr_t key, int flags, void *arg) +{ + return (flags & DNHT_KEY_IS_OBJ) ? + ((struct x *)key)->buf[0] : *(char *)key; +} + +int matchf(void *obj, uintptr_t key, int flags, void *arg) +{ + char *s = (flags & DNHT_KEY_IS_OBJ) ? + ((struct x *)key)->buf : (char *)key; + return (strcmp(((struct x *)obj)->buf, s) == 0); +} + +void *newfn(uintptr_t key, int flags, void *arg) +{ + char *s = (char *)key; + struct x *p = malloc(sizeof(*p) + 1 + strlen(s)); + if (p) + strcpy(p->buf, s); + return p; +} + +char *strings[] = { + "undici", "unico", "doppio", "devoto", + "uno", "due", "tre", "quattro", "cinque", "sei", + "uno", "due", "tre", "quattro", "cinque", "sei", + NULL, +}; + +int doprint(void *_x, void *arg) +{ + struct x *x = _x; + printf("found element <%s>\n", x->buf); + return (int)arg; +} + +static void +test_hash() +{ + char **p; + struct dn_ht *h; + uintptr_t x = 0; + uintptr_t x1 = 0; + + /* first, find and allocate */ + h = dn_ht_init(NULL, 10, 0, hf, matchf, newfn); + + for (p = strings; *p; p++) { + dn_ht_find(h, (uintptr_t)*p, DNHT_INSERT, NULL); + } + dn_ht_scan(h, doprint, 0); + printf("/* second -- find without allocate */\n"); + h = dn_ht_init(NULL, 10, 0, hf, matchf, NULL); + for (p = strings; *p; p++) { + void **y = newfn((uintptr_t)*p, 0, NULL); + if (x == 0) + x = (uintptr_t)y; + else { + if (x1 == 0) + x1 = (uintptr_t)*p; + } + dn_ht_find(h, (uintptr_t)y, DNHT_INSERT | DNHT_KEY_IS_OBJ, NULL); + } + dn_ht_scan(h, doprint, 0); + printf("remove %p gives %p\n", (void *)x, + dn_ht_find(h, x, DNHT_KEY_IS_OBJ | DNHT_REMOVE, NULL)); + printf("remove %p gives %p\n", (void *)x, + dn_ht_find(h, x, DNHT_KEY_IS_OBJ | DNHT_REMOVE, NULL)); + printf("remove %p gives %p\n", (void *)x, + dn_ht_find(h, x1, DNHT_REMOVE, NULL)); + printf("remove %p gives %p\n", (void *)x, + dn_ht_find(h, x1, DNHT_REMOVE, NULL)); + dn_ht_scan(h, doprint, 0); +} + +int +main(int argc, char *argv[]) +{ + struct dn_heap h; + int i, n, n2, n3; + + test_hash(); + return 0; + + /* n = elements, n2 = cycles */ + n = (argc > 1) ? atoi(argv[1]) : 0; + if (n <= 0 || n > 1000000) + n = 100; + n2 = (argc > 2) ? atoi(argv[2]) : 0; + if (n2 <= 0) + n = 1000000; + n3 = (argc > 3) ? atoi(argv[3]) : 0; + bzero(&h, sizeof(h)); + heap_init(&h, n, -1); + while (n2-- > 0) { + uint64_t prevk = 0; + for (i=0; i < n; i++) + heap_insert(&h, n3 ? n-i: random(), (void *)(100+i)); + + for (i=0; h.elements > 0; i++) { + uint64_t k = h.p[0].key; + if (k < prevk) + panic("wrong sequence\n"); + prevk = k; + if (0) + printf("%d key %llu, val %p\n", + i, h.p[0].key, h.p[0].object); + heap_extract(&h, NULL); + } + } + return 0; +} diff --git a/sys/netpfil/ipfw/test/test_dn_sched.c b/sys/netpfil/ipfw/test/test_dn_sched.c new file mode 100644 index 0000000000000..ee46c95ed8689 --- /dev/null +++ b/sys/netpfil/ipfw/test/test_dn_sched.c @@ -0,0 +1,89 @@ +/* + * $FreeBSD$ + * + * library functions for userland testing of dummynet schedulers + */ + +#include "dn_test.h" + +void +m_freem(struct mbuf *m) +{ + printf("free %p\n", m); +} + +int +dn_sched_modevent(module_t mod, int cmd, void *arg) +{ + return 0; +} + +void +dn_free_pkts(struct mbuf *m) +{ + struct mbuf *x; + while ( (x = m) ) { + m = m->m_nextpkt; + m_freem(x); + } +} + +int +dn_delete_queue(void *_q, void *do_free) +{ + struct dn_queue *q = _q; + if (q->mq.head) + dn_free_pkts(q->mq.head); + free(q); + return 0; +} + +/* + * This is a simplified function for testing purposes, which does + * not implement statistics or random loss. + * Enqueue a packet in q, subject to space and queue management policy + * (whose parameters are in q->fs). + * Update stats for the queue and the scheduler. + * Return 0 on success, 1 on drop. The packet is consumed anyways. + */ +int +dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) +{ + if (drop) + goto drop; + if (q->ni.length >= 200) + goto drop; + mq_append(&q->mq, m); + q->ni.length++; + q->ni.tot_bytes += m->m_pkthdr.len; + return 0; + +drop: + q->ni.drops++; + return 1; +} + +int +ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg) +{ + if (*v < lo) { + *v = dflt; + } else if (*v > hi) { + *v = hi; + } + return *v; +} + +#ifndef __FreeBSD__ +int +fls(int mask) +{ + int bit; + + if (mask == 0) + return (0); + for (bit = 1; mask != 1; bit++) + mask = (unsigned int)mask >> 1; + return (bit); +} +#endif diff --git a/sys/netpfil/pf/if_pflog.c b/sys/netpfil/pf/if_pflog.c new file mode 100644 index 0000000000000..20feea2330e14 --- /dev/null +++ b/sys/netpfil/pf/if_pflog.c @@ -0,0 +1,290 @@ +/* $OpenBSD: if_pflog.c,v 1.26 2007/10/18 21:58:18 mpf Exp $ */ +/* + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr) and + * Niels Provos (provos@physnet.uni-hamburg.de). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis + * and Niels Provos. + * Copyright (c) 2001, Angelos D. Keromytis, Niels Provos. + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_bpf.h" +#include "opt_pf.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(INET) || defined(INET6) +#include +#endif +#ifdef INET +#include +#include +#endif + +#ifdef INET6 +#include +#include +#endif /* INET6 */ + +#ifdef INET +#include +#endif /* INET */ + +#define PFLOGMTU (32768 + MHLEN + MLEN) + +#ifdef PFLOGDEBUG +#define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0) +#else +#define DPRINTF(x) +#endif + +static int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct route *); +static void pflogattach(int); +static int pflogioctl(struct ifnet *, u_long, caddr_t); +static void pflogstart(struct ifnet *); +static int pflog_clone_create(struct if_clone *, int, caddr_t); +static void pflog_clone_destroy(struct ifnet *); + +IFC_SIMPLE_DECLARE(pflog, 1); + +struct ifnet *pflogifs[PFLOGIFS_MAX]; /* for fast access */ + +static void +pflogattach(int npflog) +{ + int i; + for (i = 0; i < PFLOGIFS_MAX; i++) + pflogifs[i] = NULL; + if_clone_attach(&pflog_cloner); +} + +static int +pflog_clone_create(struct if_clone *ifc, int unit, caddr_t param) +{ + struct ifnet *ifp; + + if (unit >= PFLOGIFS_MAX) + return (EINVAL); + + ifp = if_alloc(IFT_PFLOG); + if (ifp == NULL) { + return (ENOSPC); + } + if_initname(ifp, ifc->ifc_name, unit); + ifp->if_mtu = PFLOGMTU; + ifp->if_ioctl = pflogioctl; + ifp->if_output = pflogoutput; + ifp->if_start = pflogstart; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_hdrlen = PFLOG_HDRLEN; + if_attach(ifp); + + bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN); + + pflogifs[unit] = ifp; + + return (0); +} + +static void +pflog_clone_destroy(struct ifnet *ifp) +{ + int i; + + for (i = 0; i < PFLOGIFS_MAX; i++) + if (pflogifs[i] == ifp) + pflogifs[i] = NULL; + + bpfdetach(ifp); + if_detach(ifp); + if_free(ifp); +} + +/* + * Start output on the pflog interface. + */ +static void +pflogstart(struct ifnet *ifp) +{ + struct mbuf *m; + + for (;;) { + IF_LOCK(&ifp->if_snd); + _IF_DROP(&ifp->if_snd); + _IF_DEQUEUE(&ifp->if_snd, m); + IF_UNLOCK(&ifp->if_snd); + + if (m == NULL) + return; + else + m_freem(m); + } +} + +static int +pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct route *rt) +{ + m_freem(m); + return (0); +} + +/* ARGSUSED */ +static int +pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + switch (cmd) { + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) + ifp->if_drv_flags |= IFF_DRV_RUNNING; + else + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + break; + default: + return (ENOTTY); + } + + return (0); +} + +static int +pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir, + u_int8_t reason, struct pf_rule *rm, struct pf_rule *am, + struct pf_ruleset *ruleset, struct pf_pdesc *pd, int lookupsafe) +{ + struct ifnet *ifn; + struct pfloghdr hdr; + + if (kif == NULL || m == NULL || rm == NULL || pd == NULL) + return ( 1); + + if ((ifn = pflogifs[rm->logif]) == NULL || !ifn->if_bpf) + return (0); + + bzero(&hdr, sizeof(hdr)); + hdr.length = PFLOG_REAL_HDRLEN; + hdr.af = af; + hdr.action = rm->action; + hdr.reason = reason; + memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname)); + + if (am == NULL) { + hdr.rulenr = htonl(rm->nr); + hdr.subrulenr = 1; + } else { + hdr.rulenr = htonl(am->nr); + hdr.subrulenr = htonl(rm->nr); + if (ruleset != NULL && ruleset->anchor != NULL) + strlcpy(hdr.ruleset, ruleset->anchor->name, + sizeof(hdr.ruleset)); + } + /* + * XXXGL: we avoid pf_socket_lookup() when we are holding + * state lock, since this leads to unsafe LOR. + * These conditions are very very rare, however. + */ + if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done && lookupsafe) + pd->lookup.done = pf_socket_lookup(dir, pd, m); + if (pd->lookup.done > 0) + hdr.uid = pd->lookup.uid; + else + hdr.uid = UID_MAX; + hdr.pid = NO_PID; + hdr.rule_uid = rm->cuid; + hdr.rule_pid = rm->cpid; + hdr.dir = dir; + +#ifdef INET + if (af == AF_INET && dir == PF_OUT) { + struct ip *ip; + + ip = mtod(m, struct ip *); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + } +#endif /* INET */ + + ifn->if_opackets++; + ifn->if_obytes += m->m_pkthdr.len; + BPF_MTAP2(ifn, &hdr, PFLOG_HDRLEN, m); + + return (0); +} + +static int +pflog_modevent(module_t mod, int type, void *data) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + pflogattach(1); + PF_RULES_WLOCK(); + pflog_packet_ptr = pflog_packet; + PF_RULES_WUNLOCK(); + break; + case MOD_UNLOAD: + PF_RULES_WLOCK(); + pflog_packet_ptr = NULL; + PF_RULES_WUNLOCK(); + if_clone_detach(&pflog_cloner); + break; + default: + error = EINVAL; + break; + } + + return error; +} + +static moduledata_t pflog_mod = { "pflog", pflog_modevent, 0 }; + +#define PFLOG_MODVER 1 + +DECLARE_MODULE(pflog, pflog_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); +MODULE_VERSION(pflog, PFLOG_MODVER); +MODULE_DEPEND(pflog, pf, PF_MODVER, PF_MODVER, PF_MODVER); diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c new file mode 100644 index 0000000000000..28af641ad6fab --- /dev/null +++ b/sys/netpfil/pf/if_pfsync.c @@ -0,0 +1,2397 @@ +/* $OpenBSD: if_pfsync.c,v 1.110 2009/02/24 05:39:19 dlg Exp $ */ + +/* + * Copyright (c) 2002 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES 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 MIND, 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) 2009 David Gwynne + * + * 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. + */ + +/* + * Revisions picked from OpenBSD after revision 1.110 import: + * 1.118, 1.124, 1.148, 1.149, 1.151, 1.171 - fixes to bulk updates + * 1.120, 1.175 - use monotonic time_uptime + * 1.122 - reduce number of updates for non-TCP sessions + * 1.128 - cleanups + * 1.146 - bzero() mbuf before sparsely filling it with data + * 1.170 - SIOCSIFMTU checks + * 1.126, 1.142 - deferred packets processing + * 1.173 - correct expire time processing + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_pf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFSYNC_MINPKT ( \ + sizeof(struct ip) + \ + sizeof(struct pfsync_header) + \ + sizeof(struct pfsync_subheader) + \ + sizeof(struct pfsync_eof)) + +struct pfsync_pkt { + struct ip *ip; + struct in_addr src; + u_int8_t flags; +}; + +static int pfsync_upd_tcp(struct pf_state *, struct pfsync_state_peer *, + struct pfsync_state_peer *); +static int pfsync_in_clr(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_ins(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_iack(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_upd(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_upd_c(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_ureq(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_del(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_del_c(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_bus(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_tdb(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_eof(struct pfsync_pkt *, struct mbuf *, int, int); +static int pfsync_in_error(struct pfsync_pkt *, struct mbuf *, int, int); + +static int (*pfsync_acts[])(struct pfsync_pkt *, struct mbuf *, int, int) = { + pfsync_in_clr, /* PFSYNC_ACT_CLR */ + pfsync_in_ins, /* PFSYNC_ACT_INS */ + pfsync_in_iack, /* PFSYNC_ACT_INS_ACK */ + pfsync_in_upd, /* PFSYNC_ACT_UPD */ + pfsync_in_upd_c, /* PFSYNC_ACT_UPD_C */ + pfsync_in_ureq, /* PFSYNC_ACT_UPD_REQ */ + pfsync_in_del, /* PFSYNC_ACT_DEL */ + pfsync_in_del_c, /* PFSYNC_ACT_DEL_C */ + pfsync_in_error, /* PFSYNC_ACT_INS_F */ + pfsync_in_error, /* PFSYNC_ACT_DEL_F */ + pfsync_in_bus, /* PFSYNC_ACT_BUS */ + pfsync_in_tdb, /* PFSYNC_ACT_TDB */ + pfsync_in_eof /* PFSYNC_ACT_EOF */ +}; + +struct pfsync_q { + int (*write)(struct pf_state *, struct mbuf *, int); + size_t len; + u_int8_t action; +}; + +/* we have one of these for every PFSYNC_S_ */ +static int pfsync_out_state(struct pf_state *, struct mbuf *, int); +static int pfsync_out_iack(struct pf_state *, struct mbuf *, int); +static int pfsync_out_upd_c(struct pf_state *, struct mbuf *, int); +static int pfsync_out_del(struct pf_state *, struct mbuf *, int); + +static struct pfsync_q pfsync_qs[] = { + { pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_INS }, + { pfsync_out_iack, sizeof(struct pfsync_ins_ack), PFSYNC_ACT_INS_ACK }, + { pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_UPD }, + { pfsync_out_upd_c, sizeof(struct pfsync_upd_c), PFSYNC_ACT_UPD_C }, + { pfsync_out_del, sizeof(struct pfsync_del_c), PFSYNC_ACT_DEL_C } +}; + +static void pfsync_q_ins(struct pf_state *, int); +static void pfsync_q_del(struct pf_state *); + +static void pfsync_update_state(struct pf_state *); + +struct pfsync_upd_req_item { + TAILQ_ENTRY(pfsync_upd_req_item) ur_entry; + struct pfsync_upd_req ur_msg; +}; + +struct pfsync_deferral { + struct pfsync_softc *pd_sc; + TAILQ_ENTRY(pfsync_deferral) pd_entry; + u_int pd_refs; + struct callout pd_tmo; + + struct pf_state *pd_st; + struct mbuf *pd_m; +}; + +struct pfsync_softc { + /* Configuration */ + struct ifnet *sc_ifp; + struct ifnet *sc_sync_if; + struct ip_moptions sc_imo; + struct in_addr sc_sync_peer; + uint32_t sc_flags; +#define PFSYNCF_OK 0x00000001 +#define PFSYNCF_DEFER 0x00000002 +#define PFSYNCF_PUSH 0x00000004 + uint8_t sc_maxupdates; + struct ip sc_template; + struct callout sc_tmo; + struct mtx sc_mtx; + + /* Queued data */ + size_t sc_len; + TAILQ_HEAD(, pf_state) sc_qs[PFSYNC_S_COUNT]; + TAILQ_HEAD(, pfsync_upd_req_item) sc_upd_req_list; + TAILQ_HEAD(, pfsync_deferral) sc_deferrals; + u_int sc_deferred; + void *sc_plus; + size_t sc_pluslen; + + /* Bulk update info */ + struct mtx sc_bulk_mtx; + uint32_t sc_ureq_sent; + int sc_bulk_tries; + uint32_t sc_ureq_received; + int sc_bulk_hashid; + uint64_t sc_bulk_stateid; + uint32_t sc_bulk_creatorid; + struct callout sc_bulk_tmo; + struct callout sc_bulkfail_tmo; +}; + +#define PFSYNC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define PFSYNC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define PFSYNC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) + +#define PFSYNC_BLOCK(sc) mtx_lock(&(sc)->sc_bulk_mtx) +#define PFSYNC_BUNLOCK(sc) mtx_unlock(&(sc)->sc_bulk_mtx) +#define PFSYNC_BLOCK_ASSERT(sc) mtx_assert(&(sc)->sc_bulk_mtx, MA_OWNED) + +static MALLOC_DEFINE(M_PFSYNC, "pfsync", "pfsync(4) data"); +static VNET_DEFINE(struct pfsync_softc *, pfsyncif) = NULL; +#define V_pfsyncif VNET(pfsyncif) +static VNET_DEFINE(void *, pfsync_swi_cookie) = NULL; +#define V_pfsync_swi_cookie VNET(pfsync_swi_cookie) +static VNET_DEFINE(struct pfsyncstats, pfsyncstats); +#define V_pfsyncstats VNET(pfsyncstats) +static VNET_DEFINE(int, pfsync_carp_adj) = CARP_MAXSKEW; +#define V_pfsync_carp_adj VNET(pfsync_carp_adj) + +static void pfsync_timeout(void *); +static void pfsync_push(struct pfsync_softc *); +static void pfsyncintr(void *); +static int pfsync_multicast_setup(struct pfsync_softc *, struct ifnet *, + void *); +static void pfsync_multicast_cleanup(struct pfsync_softc *); +static int pfsync_init(void); +static void pfsync_uninit(void); + +SYSCTL_NODE(_net, OID_AUTO, pfsync, CTLFLAG_RW, 0, "PFSYNC"); +SYSCTL_VNET_STRUCT(_net_pfsync, OID_AUTO, stats, CTLFLAG_RW, + &VNET_NAME(pfsyncstats), pfsyncstats, + "PFSYNC statistics (struct pfsyncstats, net/if_pfsync.h)"); +SYSCTL_INT(_net_pfsync, OID_AUTO, carp_demotion_factor, CTLFLAG_RW, + &VNET_NAME(pfsync_carp_adj), 0, "pfsync's CARP demotion factor adjustment"); + +static int pfsync_clone_create(struct if_clone *, int, caddr_t); +static void pfsync_clone_destroy(struct ifnet *); +static int pfsync_alloc_scrub_memory(struct pfsync_state_peer *, + struct pf_state_peer *); +static int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct route *); +static int pfsyncioctl(struct ifnet *, u_long, caddr_t); + +static int pfsync_defer(struct pf_state *, struct mbuf *); +static void pfsync_undefer(struct pfsync_deferral *, int); +static void pfsync_undefer_state(struct pf_state *, int); +static void pfsync_defer_tmo(void *); + +static void pfsync_request_update(u_int32_t, u_int64_t); +static void pfsync_update_state_req(struct pf_state *); + +static void pfsync_drop(struct pfsync_softc *); +static void pfsync_sendout(int); +static void pfsync_send_plus(void *, size_t); + +static void pfsync_bulk_start(void); +static void pfsync_bulk_status(u_int8_t); +static void pfsync_bulk_update(void *); +static void pfsync_bulk_fail(void *); + +#ifdef IPSEC +static void pfsync_update_net_tdb(struct pfsync_tdb *); +#endif + +#define PFSYNC_MAX_BULKTRIES 12 + +VNET_DEFINE(struct ifc_simple_data, pfsync_cloner_data); +VNET_DEFINE(struct if_clone, pfsync_cloner); +#define V_pfsync_cloner_data VNET(pfsync_cloner_data) +#define V_pfsync_cloner VNET(pfsync_cloner) +IFC_SIMPLE_DECLARE(pfsync, 1); + +static int +pfsync_clone_create(struct if_clone *ifc, int unit, caddr_t param) +{ + struct pfsync_softc *sc; + struct ifnet *ifp; + int q; + + if (unit != 0) + return (EINVAL); + + sc = malloc(sizeof(struct pfsync_softc), M_PFSYNC, M_WAITOK | M_ZERO); + sc->sc_flags |= PFSYNCF_OK; + + for (q = 0; q < PFSYNC_S_COUNT; q++) + TAILQ_INIT(&sc->sc_qs[q]); + + TAILQ_INIT(&sc->sc_upd_req_list); + TAILQ_INIT(&sc->sc_deferrals); + + sc->sc_len = PFSYNC_MINPKT; + sc->sc_maxupdates = 128; + + ifp = sc->sc_ifp = if_alloc(IFT_PFSYNC); + if (ifp == NULL) { + free(sc, M_PFSYNC); + return (ENOSPC); + } + if_initname(ifp, ifc->ifc_name, unit); + ifp->if_softc = sc; + ifp->if_ioctl = pfsyncioctl; + ifp->if_output = pfsyncoutput; + ifp->if_type = IFT_PFSYNC; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_hdrlen = sizeof(struct pfsync_header); + ifp->if_mtu = ETHERMTU; + mtx_init(&sc->sc_mtx, "pfsync", NULL, MTX_DEF); + mtx_init(&sc->sc_bulk_mtx, "pfsync bulk", NULL, MTX_DEF); + callout_init(&sc->sc_tmo, CALLOUT_MPSAFE); + callout_init_mtx(&sc->sc_bulk_tmo, &sc->sc_bulk_mtx, 0); + callout_init_mtx(&sc->sc_bulkfail_tmo, &sc->sc_bulk_mtx, 0); + + if_attach(ifp); + + bpfattach(ifp, DLT_PFSYNC, PFSYNC_HDRLEN); + + V_pfsyncif = sc; + + return (0); +} + +static void +pfsync_clone_destroy(struct ifnet *ifp) +{ + struct pfsync_softc *sc = ifp->if_softc; + + /* + * At this stage, everything should have already been + * cleared by pfsync_uninit(), and we have only to + * drain callouts. + */ + while (sc->sc_deferred > 0) { + struct pfsync_deferral *pd = TAILQ_FIRST(&sc->sc_deferrals); + + TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); + sc->sc_deferred--; + if (callout_stop(&pd->pd_tmo)) { + pf_release_state(pd->pd_st); + m_freem(pd->pd_m); + free(pd, M_PFSYNC); + } else { + pd->pd_refs++; + callout_drain(&pd->pd_tmo); + free(pd, M_PFSYNC); + } + } + + callout_drain(&sc->sc_tmo); + callout_drain(&sc->sc_bulkfail_tmo); + callout_drain(&sc->sc_bulk_tmo); + + if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) + (*carp_demote_adj_p)(-V_pfsync_carp_adj, "pfsync destroy"); + bpfdetach(ifp); + if_detach(ifp); + + pfsync_drop(sc); + + if_free(ifp); + if (sc->sc_imo.imo_membership) + pfsync_multicast_cleanup(sc); + mtx_destroy(&sc->sc_mtx); + mtx_destroy(&sc->sc_bulk_mtx); + free(sc, M_PFSYNC); + + V_pfsyncif = NULL; +} + +static int +pfsync_alloc_scrub_memory(struct pfsync_state_peer *s, + struct pf_state_peer *d) +{ + if (s->scrub.scrub_flag && d->scrub == NULL) { + d->scrub = uma_zalloc(V_pf_state_scrub_z, M_NOWAIT | M_ZERO); + if (d->scrub == NULL) + return (ENOMEM); + } + + return (0); +} + + +static int +pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pf_state *st = NULL; + struct pf_state_key *skw = NULL, *sks = NULL; + struct pf_rule *r = NULL; + struct pfi_kif *kif; + int error; + + PF_RULES_RASSERT(); + + if (sp->creatorid == 0 && V_pf_status.debug >= PF_DEBUG_MISC) { + printf("%s: invalid creator id: %08x\n", __func__, + ntohl(sp->creatorid)); + return (EINVAL); + } + + if ((kif = pfi_kif_find(sp->ifname)) == NULL) { + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("%s: unknown interface: %s\n", __func__, + sp->ifname); + if (flags & PFSYNC_SI_IOCTL) + return (EINVAL); + return (0); /* skip this state */ + } + + /* + * If the ruleset checksums match or the state is coming from the ioctl, + * it's safe to associate the state with the rule of that number. + */ + if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) && + (flags & (PFSYNC_SI_IOCTL | PFSYNC_SI_CKSUM)) && ntohl(sp->rule) < + pf_main_ruleset.rules[PF_RULESET_FILTER].active.rcount) + r = pf_main_ruleset.rules[ + PF_RULESET_FILTER].active.ptr_array[ntohl(sp->rule)]; + else + r = &V_pf_default_rule; + + if ((r->max_states && r->states_cur >= r->max_states)) + goto cleanup; + + /* + * XXXGL: consider M_WAITOK in ioctl path after. + */ + if ((st = uma_zalloc(V_pf_state_z, M_NOWAIT | M_ZERO)) == NULL) + goto cleanup; + + if ((skw = uma_zalloc(V_pf_state_key_z, M_NOWAIT)) == NULL) + goto cleanup; + + if (PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0], + &sp->key[PF_SK_STACK].addr[0], sp->af) || + PF_ANEQ(&sp->key[PF_SK_WIRE].addr[1], + &sp->key[PF_SK_STACK].addr[1], sp->af) || + sp->key[PF_SK_WIRE].port[0] != sp->key[PF_SK_STACK].port[0] || + sp->key[PF_SK_WIRE].port[1] != sp->key[PF_SK_STACK].port[1]) { + sks = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (sks == NULL) + goto cleanup; + } else + sks = skw; + + /* allocate memory for scrub info */ + if (pfsync_alloc_scrub_memory(&sp->src, &st->src) || + pfsync_alloc_scrub_memory(&sp->dst, &st->dst)) + goto cleanup; + + /* copy to state key(s) */ + skw->addr[0] = sp->key[PF_SK_WIRE].addr[0]; + skw->addr[1] = sp->key[PF_SK_WIRE].addr[1]; + skw->port[0] = sp->key[PF_SK_WIRE].port[0]; + skw->port[1] = sp->key[PF_SK_WIRE].port[1]; + skw->proto = sp->proto; + skw->af = sp->af; + if (sks != skw) { + sks->addr[0] = sp->key[PF_SK_STACK].addr[0]; + sks->addr[1] = sp->key[PF_SK_STACK].addr[1]; + sks->port[0] = sp->key[PF_SK_STACK].port[0]; + sks->port[1] = sp->key[PF_SK_STACK].port[1]; + sks->proto = sp->proto; + sks->af = sp->af; + } + + /* copy to state */ + bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr)); + st->creation = time_uptime - ntohl(sp->creation); + st->expire = time_uptime; + if (sp->expire) { + uint32_t timeout; + + timeout = r->timeout[sp->timeout]; + if (!timeout) + timeout = V_pf_default_rule.timeout[sp->timeout]; + + /* sp->expire may have been adaptively scaled by export. */ + st->expire -= timeout - ntohl(sp->expire); + } + + st->direction = sp->direction; + st->log = sp->log; + st->timeout = sp->timeout; + st->state_flags = sp->state_flags; + + st->id = sp->id; + st->creatorid = sp->creatorid; + pf_state_peer_ntoh(&sp->src, &st->src); + pf_state_peer_ntoh(&sp->dst, &st->dst); + + st->rule.ptr = r; + st->nat_rule.ptr = NULL; + st->anchor.ptr = NULL; + st->rt_kif = NULL; + + st->pfsync_time = time_uptime; + st->sync_state = PFSYNC_S_NONE; + + /* XXX when we have nat_rule/anchors, use STATE_INC_COUNTERS */ + r->states_cur++; + r->states_tot++; + + if (!(flags & PFSYNC_SI_IOCTL)) + st->state_flags |= PFSTATE_NOSYNC; + + if ((error = pf_state_insert(kif, skw, sks, st)) != 0) { + /* XXX when we have nat_rule/anchors, use STATE_DEC_COUNTERS */ + r->states_cur--; + goto cleanup_state; + } + + if (!(flags & PFSYNC_SI_IOCTL)) { + st->state_flags &= ~PFSTATE_NOSYNC; + if (st->state_flags & PFSTATE_ACK) { + pfsync_q_ins(st, PFSYNC_S_IACK); + pfsync_push(sc); + } + } + st->state_flags &= ~PFSTATE_ACK; + PF_STATE_UNLOCK(st); + + return (0); + +cleanup: + error = ENOMEM; + if (skw == sks) + sks = NULL; + if (skw != NULL) + uma_zfree(V_pf_state_key_z, skw); + if (sks != NULL) + uma_zfree(V_pf_state_key_z, sks); + +cleanup_state: /* pf_state_insert() frees the state keys. */ + if (st) { + if (st->dst.scrub) + uma_zfree(V_pf_state_scrub_z, st->dst.scrub); + if (st->src.scrub) + uma_zfree(V_pf_state_scrub_z, st->src.scrub); + uma_zfree(V_pf_state_z, st); + } + return (error); +} + +static void +pfsync_input(struct mbuf *m, __unused int off) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_pkt pkt; + struct ip *ip = mtod(m, struct ip *); + struct pfsync_header *ph; + struct pfsync_subheader subh; + + int offset; + int rv; + uint16_t count; + + V_pfsyncstats.pfsyncs_ipackets++; + + /* Verify that we have a sync interface configured. */ + if (!sc || !sc->sc_sync_if || !V_pf_status.running || + (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + /* verify that the packet came in on the right interface */ + if (sc->sc_sync_if != m->m_pkthdr.rcvif) { + V_pfsyncstats.pfsyncs_badif++; + goto done; + } + + sc->sc_ifp->if_ipackets++; + sc->sc_ifp->if_ibytes += m->m_pkthdr.len; + /* verify that the IP TTL is 255. */ + if (ip->ip_ttl != PFSYNC_DFLTTL) { + V_pfsyncstats.pfsyncs_badttl++; + goto done; + } + + offset = ip->ip_hl << 2; + if (m->m_pkthdr.len < offset + sizeof(*ph)) { + V_pfsyncstats.pfsyncs_hdrops++; + goto done; + } + + if (offset + sizeof(*ph) > m->m_len) { + if (m_pullup(m, offset + sizeof(*ph)) == NULL) { + V_pfsyncstats.pfsyncs_hdrops++; + return; + } + ip = mtod(m, struct ip *); + } + ph = (struct pfsync_header *)((char *)ip + offset); + + /* verify the version */ + if (ph->version != PFSYNC_VERSION) { + V_pfsyncstats.pfsyncs_badver++; + goto done; + } + + /* Cheaper to grab this now than having to mess with mbufs later */ + pkt.ip = ip; + pkt.src = ip->ip_src; + pkt.flags = 0; + + /* + * Trusting pf_chksum during packet processing, as well as seeking + * in interface name tree, require holding PF_RULES_RLOCK(). + */ + PF_RULES_RLOCK(); + if (!bcmp(&ph->pfcksum, &V_pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH)) + pkt.flags |= PFSYNC_SI_CKSUM; + + offset += sizeof(*ph); + for (;;) { + m_copydata(m, offset, sizeof(subh), (caddr_t)&subh); + offset += sizeof(subh); + + if (subh.action >= PFSYNC_ACT_MAX) { + V_pfsyncstats.pfsyncs_badact++; + PF_RULES_RUNLOCK(); + goto done; + } + + count = ntohs(subh.count); + V_pfsyncstats.pfsyncs_iacts[subh.action] += count; + rv = (*pfsync_acts[subh.action])(&pkt, m, offset, count); + if (rv == -1) { + PF_RULES_RUNLOCK(); + return; + } + + offset += rv; + } + PF_RULES_RUNLOCK(); + +done: + m_freem(m); +} + +static int +pfsync_in_clr(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct pfsync_clr *clr; + struct mbuf *mp; + int len = sizeof(*clr) * count; + int i, offp; + u_int32_t creatorid; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + clr = (struct pfsync_clr *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + creatorid = clr[i].creatorid; + + if (clr[i].ifname[0] != '\0' && + pfi_kif_find(clr[i].ifname) == NULL) + continue; + + for (int i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_state *s; +relock: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (s->creatorid == creatorid) { + s->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(s, PF_ENTER_LOCKED); + goto relock; + } + } + PF_HASHROW_UNLOCK(ih); + } + } + + return (len); +} + +static int +pfsync_in_ins(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct mbuf *mp; + struct pfsync_state *sa, *sp; + int len = sizeof(*sp) * count; + int i, offp; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + sa = (struct pfsync_state *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + sp = &sa[i]; + + /* Check for invalid values. */ + if (sp->timeout >= PFTM_MAX || + sp->src.state > PF_TCPS_PROXY_DST || + sp->dst.state > PF_TCPS_PROXY_DST || + sp->direction > PF_OUT || + (sp->af != AF_INET && sp->af != AF_INET6)) { + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("%s: invalid value\n", __func__); + V_pfsyncstats.pfsyncs_badval++; + continue; + } + + if (pfsync_state_import(sp, pkt->flags) == ENOMEM) + /* Drop out, but process the rest of the actions. */ + break; + } + + return (len); +} + +static int +pfsync_in_iack(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct pfsync_ins_ack *ia, *iaa; + struct pf_state *st; + + struct mbuf *mp; + int len = count * sizeof(*ia); + int offp, i; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + iaa = (struct pfsync_ins_ack *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + ia = &iaa[i]; + + st = pf_find_state_byid(ia->id, ia->creatorid); + if (st == NULL) + continue; + + if (st->state_flags & PFSTATE_ACK) { + PFSYNC_LOCK(V_pfsyncif); + pfsync_undefer_state(st, 0); + PFSYNC_UNLOCK(V_pfsyncif); + } + PF_STATE_UNLOCK(st); + } + /* + * XXX this is not yet implemented, but we know the size of the + * message so we can skip it. + */ + + return (count * sizeof(struct pfsync_ins_ack)); +} + +static int +pfsync_upd_tcp(struct pf_state *st, struct pfsync_state_peer *src, + struct pfsync_state_peer *dst) +{ + int sfail = 0; + + PF_STATE_LOCK_ASSERT(st); + + /* + * The state should never go backwards except + * for syn-proxy states. Neither should the + * sequence window slide backwards. + */ + if (st->src.state > src->state && + (st->src.state < PF_TCPS_PROXY_SRC || + src->state >= PF_TCPS_PROXY_SRC)) + sfail = 1; + else if (SEQ_GT(st->src.seqlo, ntohl(src->seqlo))) + sfail = 3; + else if (st->dst.state > dst->state) { + /* There might still be useful + * information about the src state here, + * so import that part of the update, + * then "fail" so we send the updated + * state back to the peer who is missing + * our what we know. */ + pf_state_peer_ntoh(src, &st->src); + /* XXX do anything with timeouts? */ + sfail = 7; + } else if (st->dst.state >= TCPS_SYN_SENT && + SEQ_GT(st->dst.seqlo, ntohl(dst->seqlo))) + sfail = 4; + + return (sfail); +} + +static int +pfsync_in_upd(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_state *sa, *sp; + struct pf_state_key *sk; + struct pf_state *st; + int sfail; + + struct mbuf *mp; + int len = count * sizeof(*sp); + int offp, i; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + sa = (struct pfsync_state *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + sp = &sa[i]; + + /* check for invalid values */ + if (sp->timeout >= PFTM_MAX || + sp->src.state > PF_TCPS_PROXY_DST || + sp->dst.state > PF_TCPS_PROXY_DST) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pfsync_input: PFSYNC_ACT_UPD: " + "invalid value\n"); + } + V_pfsyncstats.pfsyncs_badval++; + continue; + } + + st = pf_find_state_byid(sp->id, sp->creatorid); + if (st == NULL) { + /* insert the update */ + if (pfsync_state_import(sp, 0)) + V_pfsyncstats.pfsyncs_badstate++; + continue; + } + + if (st->state_flags & PFSTATE_ACK) { + PFSYNC_LOCK(sc); + pfsync_undefer_state(st, 1); + PFSYNC_UNLOCK(sc); + } + + sk = st->key[PF_SK_WIRE]; /* XXX right one? */ + sfail = 0; + if (sk->proto == IPPROTO_TCP) + sfail = pfsync_upd_tcp(st, &sp->src, &sp->dst); + else { + /* + * Non-TCP protocol state machine always go + * forwards + */ + if (st->src.state > sp->src.state) + sfail = 5; + else if (st->dst.state > sp->dst.state) + sfail = 6; + } + + if (sfail) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pfsync: %s stale update (%d)" + " id: %016llx creatorid: %08x\n", + (sfail < 7 ? "ignoring" : "partial"), + sfail, (unsigned long long)be64toh(st->id), + ntohl(st->creatorid)); + } + V_pfsyncstats.pfsyncs_stale++; + + pfsync_update_state(st); + PF_STATE_UNLOCK(st); + PFSYNC_LOCK(sc); + pfsync_push(sc); + PFSYNC_UNLOCK(sc); + continue; + } + pfsync_alloc_scrub_memory(&sp->dst, &st->dst); + pf_state_peer_ntoh(&sp->src, &st->src); + pf_state_peer_ntoh(&sp->dst, &st->dst); + st->expire = time_uptime; + st->timeout = sp->timeout; + st->pfsync_time = time_uptime; + PF_STATE_UNLOCK(st); + } + + return (len); +} + +static int +pfsync_in_upd_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_upd_c *ua, *up; + struct pf_state_key *sk; + struct pf_state *st; + + int len = count * sizeof(*up); + int sfail; + + struct mbuf *mp; + int offp, i; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + ua = (struct pfsync_upd_c *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + up = &ua[i]; + + /* check for invalid values */ + if (up->timeout >= PFTM_MAX || + up->src.state > PF_TCPS_PROXY_DST || + up->dst.state > PF_TCPS_PROXY_DST) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pfsync_input: " + "PFSYNC_ACT_UPD_C: " + "invalid value\n"); + } + V_pfsyncstats.pfsyncs_badval++; + continue; + } + + st = pf_find_state_byid(up->id, up->creatorid); + if (st == NULL) { + /* We don't have this state. Ask for it. */ + PFSYNC_LOCK(sc); + pfsync_request_update(up->creatorid, up->id); + PFSYNC_UNLOCK(sc); + continue; + } + + if (st->state_flags & PFSTATE_ACK) { + PFSYNC_LOCK(sc); + pfsync_undefer_state(st, 1); + PFSYNC_UNLOCK(sc); + } + + sk = st->key[PF_SK_WIRE]; /* XXX right one? */ + sfail = 0; + if (sk->proto == IPPROTO_TCP) + sfail = pfsync_upd_tcp(st, &up->src, &up->dst); + else { + /* + * Non-TCP protocol state machine always go forwards + */ + if (st->src.state > up->src.state) + sfail = 5; + else if (st->dst.state > up->dst.state) + sfail = 6; + } + + if (sfail) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pfsync: ignoring stale update " + "(%d) id: %016llx " + "creatorid: %08x\n", sfail, + (unsigned long long)be64toh(st->id), + ntohl(st->creatorid)); + } + V_pfsyncstats.pfsyncs_stale++; + + pfsync_update_state(st); + PF_STATE_UNLOCK(st); + PFSYNC_LOCK(sc); + pfsync_push(sc); + PFSYNC_UNLOCK(sc); + continue; + } + pfsync_alloc_scrub_memory(&up->dst, &st->dst); + pf_state_peer_ntoh(&up->src, &st->src); + pf_state_peer_ntoh(&up->dst, &st->dst); + st->expire = time_uptime; + st->timeout = up->timeout; + st->pfsync_time = time_uptime; + PF_STATE_UNLOCK(st); + } + + return (len); +} + +static int +pfsync_in_ureq(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct pfsync_upd_req *ur, *ura; + struct mbuf *mp; + int len = count * sizeof(*ur); + int i, offp; + + struct pf_state *st; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + ura = (struct pfsync_upd_req *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + ur = &ura[i]; + + if (ur->id == 0 && ur->creatorid == 0) + pfsync_bulk_start(); + else { + st = pf_find_state_byid(ur->id, ur->creatorid); + if (st == NULL) { + V_pfsyncstats.pfsyncs_badstate++; + continue; + } + if (st->state_flags & PFSTATE_NOSYNC) { + PF_STATE_UNLOCK(st); + continue; + } + + pfsync_update_state_req(st); + PF_STATE_UNLOCK(st); + } + } + + return (len); +} + +static int +pfsync_in_del(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct mbuf *mp; + struct pfsync_state *sa, *sp; + struct pf_state *st; + int len = count * sizeof(*sp); + int offp, i; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + sa = (struct pfsync_state *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + sp = &sa[i]; + + st = pf_find_state_byid(sp->id, sp->creatorid); + if (st == NULL) { + V_pfsyncstats.pfsyncs_badstate++; + continue; + } + st->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(st, PF_ENTER_LOCKED); + } + + return (len); +} + +static int +pfsync_in_del_c(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct mbuf *mp; + struct pfsync_del_c *sa, *sp; + struct pf_state *st; + int len = count * sizeof(*sp); + int offp, i; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + sa = (struct pfsync_del_c *)(mp->m_data + offp); + + for (i = 0; i < count; i++) { + sp = &sa[i]; + + st = pf_find_state_byid(sp->id, sp->creatorid); + if (st == NULL) { + V_pfsyncstats.pfsyncs_badstate++; + continue; + } + + st->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(st, PF_ENTER_LOCKED); + } + + return (len); +} + +static int +pfsync_in_bus(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_bus *bus; + struct mbuf *mp; + int len = count * sizeof(*bus); + int offp; + + PFSYNC_BLOCK(sc); + + /* If we're not waiting for a bulk update, who cares. */ + if (sc->sc_ureq_sent == 0) { + PFSYNC_BUNLOCK(sc); + return (len); + } + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + PFSYNC_BUNLOCK(sc); + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + bus = (struct pfsync_bus *)(mp->m_data + offp); + + switch (bus->status) { + case PFSYNC_BUS_START: + callout_reset(&sc->sc_bulkfail_tmo, 4 * hz + + V_pf_limits[PF_LIMIT_STATES].limit / + ((sc->sc_ifp->if_mtu - PFSYNC_MINPKT) / + sizeof(struct pfsync_state)), + pfsync_bulk_fail, sc); + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received bulk update start\n"); + break; + + case PFSYNC_BUS_END: + if (time_uptime - ntohl(bus->endtime) >= + sc->sc_ureq_sent) { + /* that's it, we're happy */ + sc->sc_ureq_sent = 0; + sc->sc_bulk_tries = 0; + callout_stop(&sc->sc_bulkfail_tmo); + if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) + (*carp_demote_adj_p)(-V_pfsync_carp_adj, + "pfsync bulk done"); + sc->sc_flags |= PFSYNCF_OK; + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received valid " + "bulk update end\n"); + } else { + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received invalid " + "bulk update end: bad timestamp\n"); + } + break; + } + PFSYNC_BUNLOCK(sc); + + return (len); +} + +static int +pfsync_in_tdb(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + int len = count * sizeof(struct pfsync_tdb); + +#if defined(IPSEC) + struct pfsync_tdb *tp; + struct mbuf *mp; + int offp; + int i; + int s; + + mp = m_pulldown(m, offset, len, &offp); + if (mp == NULL) { + V_pfsyncstats.pfsyncs_badlen++; + return (-1); + } + tp = (struct pfsync_tdb *)(mp->m_data + offp); + + for (i = 0; i < count; i++) + pfsync_update_net_tdb(&tp[i]); +#endif + + return (len); +} + +#if defined(IPSEC) +/* Update an in-kernel tdb. Silently fail if no tdb is found. */ +static void +pfsync_update_net_tdb(struct pfsync_tdb *pt) +{ + struct tdb *tdb; + int s; + + /* check for invalid values */ + if (ntohl(pt->spi) <= SPI_RESERVED_MAX || + (pt->dst.sa.sa_family != AF_INET && + pt->dst.sa.sa_family != AF_INET6)) + goto bad; + + tdb = gettdb(pt->spi, &pt->dst, pt->sproto); + if (tdb) { + pt->rpl = ntohl(pt->rpl); + pt->cur_bytes = (unsigned long long)be64toh(pt->cur_bytes); + + /* Neither replay nor byte counter should ever decrease. */ + if (pt->rpl < tdb->tdb_rpl || + pt->cur_bytes < tdb->tdb_cur_bytes) { + goto bad; + } + + tdb->tdb_rpl = pt->rpl; + tdb->tdb_cur_bytes = pt->cur_bytes; + } + return; + +bad: + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_insert: PFSYNC_ACT_TDB_UPD: " + "invalid value\n"); + V_pfsyncstats.pfsyncs_badstate++; + return; +} +#endif + + +static int +pfsync_in_eof(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + /* check if we are at the right place in the packet */ + if (offset != m->m_pkthdr.len - sizeof(struct pfsync_eof)) + V_pfsyncstats.pfsyncs_badact++; + + /* we're done. free and let the caller return */ + m_freem(m); + return (-1); +} + +static int +pfsync_in_error(struct pfsync_pkt *pkt, struct mbuf *m, int offset, int count) +{ + V_pfsyncstats.pfsyncs_badact++; + + m_freem(m); + return (-1); +} + +static int +pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct route *rt) +{ + m_freem(m); + return (0); +} + +/* ARGSUSED */ +static int +pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct pfsync_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct pfsyncreq pfsyncr; + int error; + + switch (cmd) { + case SIOCSIFFLAGS: + PFSYNC_LOCK(sc); + if (ifp->if_flags & IFF_UP) + ifp->if_drv_flags |= IFF_DRV_RUNNING; + else + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + PFSYNC_UNLOCK(sc); + break; + case SIOCSIFMTU: + if (!sc->sc_sync_if || + ifr->ifr_mtu <= PFSYNC_MINPKT || + ifr->ifr_mtu > sc->sc_sync_if->if_mtu) + return (EINVAL); + if (ifr->ifr_mtu < ifp->if_mtu) { + PFSYNC_LOCK(sc); + if (sc->sc_len > PFSYNC_MINPKT) + pfsync_sendout(1); + PFSYNC_UNLOCK(sc); + } + ifp->if_mtu = ifr->ifr_mtu; + break; + case SIOCGETPFSYNC: + bzero(&pfsyncr, sizeof(pfsyncr)); + PFSYNC_LOCK(sc); + if (sc->sc_sync_if) { + strlcpy(pfsyncr.pfsyncr_syncdev, + sc->sc_sync_if->if_xname, IFNAMSIZ); + } + pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer; + pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates; + pfsyncr.pfsyncr_defer = (PFSYNCF_DEFER == + (sc->sc_flags & PFSYNCF_DEFER)); + PFSYNC_UNLOCK(sc); + return (copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr))); + + case SIOCSETPFSYNC: + { + struct ip_moptions *imo = &sc->sc_imo; + struct ifnet *sifp; + struct ip *ip; + void *mship = NULL; + + if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0) + return (error); + if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr)))) + return (error); + + if (pfsyncr.pfsyncr_maxupdates > 255) + return (EINVAL); + + if (pfsyncr.pfsyncr_syncdev[0] == 0) + sifp = NULL; + else if ((sifp = ifunit_ref(pfsyncr.pfsyncr_syncdev)) == NULL) + return (EINVAL); + + if (pfsyncr.pfsyncr_syncpeer.s_addr == 0 && sifp != NULL) + mship = malloc((sizeof(struct in_multi *) * + IP_MIN_MEMBERSHIPS), M_PFSYNC, M_WAITOK | M_ZERO); + + PFSYNC_LOCK(sc); + if (pfsyncr.pfsyncr_syncpeer.s_addr == 0) + sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP); + else + sc->sc_sync_peer.s_addr = + pfsyncr.pfsyncr_syncpeer.s_addr; + + sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates; + if (pfsyncr.pfsyncr_defer) { + sc->sc_flags |= PFSYNCF_DEFER; + pfsync_defer_ptr = pfsync_defer; + } else { + sc->sc_flags &= ~PFSYNCF_DEFER; + pfsync_defer_ptr = NULL; + } + + if (sifp == NULL) { + if (sc->sc_sync_if) + if_rele(sc->sc_sync_if); + sc->sc_sync_if = NULL; + if (imo->imo_membership) + pfsync_multicast_cleanup(sc); + PFSYNC_UNLOCK(sc); + break; + } + + if (sc->sc_len > PFSYNC_MINPKT && + (sifp->if_mtu < sc->sc_ifp->if_mtu || + (sc->sc_sync_if != NULL && + sifp->if_mtu < sc->sc_sync_if->if_mtu) || + sifp->if_mtu < MCLBYTES - sizeof(struct ip))) + pfsync_sendout(1); + + if (imo->imo_membership) + pfsync_multicast_cleanup(sc); + + if (sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) { + error = pfsync_multicast_setup(sc, sifp, mship); + if (error) { + if_rele(sifp); + free(mship, M_PFSYNC); + return (error); + } + } + if (sc->sc_sync_if) + if_rele(sc->sc_sync_if); + sc->sc_sync_if = sifp; + + ip = &sc->sc_template; + bzero(ip, sizeof(*ip)); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(sc->sc_template) >> 2; + ip->ip_tos = IPTOS_LOWDELAY; + /* len and id are set later. */ + ip->ip_off = IP_DF; + ip->ip_ttl = PFSYNC_DFLTTL; + ip->ip_p = IPPROTO_PFSYNC; + ip->ip_src.s_addr = INADDR_ANY; + ip->ip_dst.s_addr = sc->sc_sync_peer.s_addr; + + /* Request a full state table update. */ + if ((sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) + (*carp_demote_adj_p)(V_pfsync_carp_adj, + "pfsync bulk start"); + sc->sc_flags &= ~PFSYNCF_OK; + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: requesting bulk update\n"); + pfsync_request_update(0, 0); + PFSYNC_UNLOCK(sc); + PFSYNC_BLOCK(sc); + sc->sc_ureq_sent = time_uptime; + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulk_fail, + sc); + PFSYNC_BUNLOCK(sc); + + break; + } + default: + return (ENOTTY); + } + + return (0); +} + +static int +pfsync_out_state(struct pf_state *st, struct mbuf *m, int offset) +{ + struct pfsync_state *sp = (struct pfsync_state *)(m->m_data + offset); + + pfsync_state_export(sp, st); + + return (sizeof(*sp)); +} + +static int +pfsync_out_iack(struct pf_state *st, struct mbuf *m, int offset) +{ + struct pfsync_ins_ack *iack = + (struct pfsync_ins_ack *)(m->m_data + offset); + + iack->id = st->id; + iack->creatorid = st->creatorid; + + return (sizeof(*iack)); +} + +static int +pfsync_out_upd_c(struct pf_state *st, struct mbuf *m, int offset) +{ + struct pfsync_upd_c *up = (struct pfsync_upd_c *)(m->m_data + offset); + + bzero(up, sizeof(*up)); + up->id = st->id; + pf_state_peer_hton(&st->src, &up->src); + pf_state_peer_hton(&st->dst, &up->dst); + up->creatorid = st->creatorid; + up->timeout = st->timeout; + + return (sizeof(*up)); +} + +static int +pfsync_out_del(struct pf_state *st, struct mbuf *m, int offset) +{ + struct pfsync_del_c *dp = (struct pfsync_del_c *)(m->m_data + offset); + + dp->id = st->id; + dp->creatorid = st->creatorid; + + st->state_flags |= PFSTATE_NOSYNC; + + return (sizeof(*dp)); +} + +static void +pfsync_drop(struct pfsync_softc *sc) +{ + struct pf_state *st, *next; + struct pfsync_upd_req_item *ur; + int q; + + for (q = 0; q < PFSYNC_S_COUNT; q++) { + if (TAILQ_EMPTY(&sc->sc_qs[q])) + continue; + + TAILQ_FOREACH_SAFE(st, &sc->sc_qs[q], sync_list, next) { + KASSERT(st->sync_state == q, + ("%s: st->sync_state == q", + __func__)); + st->sync_state = PFSYNC_S_NONE; + pf_release_state(st); + } + TAILQ_INIT(&sc->sc_qs[q]); + } + + while ((ur = TAILQ_FIRST(&sc->sc_upd_req_list)) != NULL) { + TAILQ_REMOVE(&sc->sc_upd_req_list, ur, ur_entry); + free(ur, M_PFSYNC); + } + + sc->sc_plus = NULL; + sc->sc_len = PFSYNC_MINPKT; +} + +static void +pfsync_sendout(int schedswi) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct ip *ip; + struct pfsync_header *ph; + struct pfsync_subheader *subh; + struct pf_state *st, *next; + struct pfsync_upd_req_item *ur; + int offset; + int q, count = 0; + + KASSERT(sc != NULL, ("%s: null sc", __func__)); + KASSERT(sc->sc_len > PFSYNC_MINPKT, + ("%s: sc_len %zu", __func__, sc->sc_len)); + PFSYNC_LOCK_ASSERT(sc); + + if (ifp->if_bpf == NULL && sc->sc_sync_if == NULL) { + pfsync_drop(sc); + return; + } + + m = m_get2(M_NOWAIT, MT_DATA, M_PKTHDR, max_linkhdr + sc->sc_len); + if (m == NULL) { + sc->sc_ifp->if_oerrors++; + V_pfsyncstats.pfsyncs_onomem++; + return; + } + m->m_data += max_linkhdr; + m->m_len = m->m_pkthdr.len = sc->sc_len; + + /* build the ip header */ + ip = (struct ip *)m->m_data; + bcopy(&sc->sc_template, ip, sizeof(*ip)); + offset = sizeof(*ip); + + ip->ip_len = m->m_pkthdr.len; + ip->ip_id = htons(ip_randomid()); + + /* build the pfsync header */ + ph = (struct pfsync_header *)(m->m_data + offset); + bzero(ph, sizeof(*ph)); + offset += sizeof(*ph); + + ph->version = PFSYNC_VERSION; + ph->len = htons(sc->sc_len - sizeof(*ip)); + bcopy(V_pf_status.pf_chksum, ph->pfcksum, PF_MD5_DIGEST_LENGTH); + + /* walk the queues */ + for (q = 0; q < PFSYNC_S_COUNT; q++) { + if (TAILQ_EMPTY(&sc->sc_qs[q])) + continue; + + subh = (struct pfsync_subheader *)(m->m_data + offset); + offset += sizeof(*subh); + + count = 0; + TAILQ_FOREACH_SAFE(st, &sc->sc_qs[q], sync_list, next) { + KASSERT(st->sync_state == q, + ("%s: st->sync_state == q", + __func__)); + /* + * XXXGL: some of write methods do unlocked reads + * of state data :( + */ + offset += pfsync_qs[q].write(st, m, offset); + st->sync_state = PFSYNC_S_NONE; + pf_release_state(st); + count++; + } + TAILQ_INIT(&sc->sc_qs[q]); + + bzero(subh, sizeof(*subh)); + subh->action = pfsync_qs[q].action; + subh->count = htons(count); + V_pfsyncstats.pfsyncs_oacts[pfsync_qs[q].action] += count; + } + + if (!TAILQ_EMPTY(&sc->sc_upd_req_list)) { + subh = (struct pfsync_subheader *)(m->m_data + offset); + offset += sizeof(*subh); + + count = 0; + while ((ur = TAILQ_FIRST(&sc->sc_upd_req_list)) != NULL) { + TAILQ_REMOVE(&sc->sc_upd_req_list, ur, ur_entry); + + bcopy(&ur->ur_msg, m->m_data + offset, + sizeof(ur->ur_msg)); + offset += sizeof(ur->ur_msg); + free(ur, M_PFSYNC); + count++; + } + + bzero(subh, sizeof(*subh)); + subh->action = PFSYNC_ACT_UPD_REQ; + subh->count = htons(count); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_UPD_REQ] += count; + } + + /* has someone built a custom region for us to add? */ + if (sc->sc_plus != NULL) { + bcopy(sc->sc_plus, m->m_data + offset, sc->sc_pluslen); + offset += sc->sc_pluslen; + + sc->sc_plus = NULL; + } + + subh = (struct pfsync_subheader *)(m->m_data + offset); + offset += sizeof(*subh); + + bzero(subh, sizeof(*subh)); + subh->action = PFSYNC_ACT_EOF; + subh->count = htons(1); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_EOF]++; + + /* XXX write checksum in EOF here */ + + /* we're done, let's put it on the wire */ + if (ifp->if_bpf) { + m->m_data += sizeof(*ip); + m->m_len = m->m_pkthdr.len = sc->sc_len - sizeof(*ip); + BPF_MTAP(ifp, m); + m->m_data -= sizeof(*ip); + m->m_len = m->m_pkthdr.len = sc->sc_len; + } + + if (sc->sc_sync_if == NULL) { + sc->sc_len = PFSYNC_MINPKT; + m_freem(m); + return; + } + + sc->sc_ifp->if_opackets++; + sc->sc_ifp->if_obytes += m->m_pkthdr.len; + sc->sc_len = PFSYNC_MINPKT; + + if (!_IF_QFULL(&sc->sc_ifp->if_snd)) + _IF_ENQUEUE(&sc->sc_ifp->if_snd, m); + else { + m_freem(m); + sc->sc_ifp->if_snd.ifq_drops++; + } + if (schedswi) + swi_sched(V_pfsync_swi_cookie, 0); +} + +static void +pfsync_insert_state(struct pf_state *st) +{ + struct pfsync_softc *sc = V_pfsyncif; + + if (st->state_flags & PFSTATE_NOSYNC) + return; + + if ((st->rule.ptr->rule_flag & PFRULE_NOSYNC) || + st->key[PF_SK_WIRE]->proto == IPPROTO_PFSYNC) { + st->state_flags |= PFSTATE_NOSYNC; + return; + } + + KASSERT(st->sync_state == PFSYNC_S_NONE, + ("%s: st->sync_state == PFSYNC_S_NONE", __func__)); + + PFSYNC_LOCK(sc); + if (sc->sc_len == PFSYNC_MINPKT) + callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); + + pfsync_q_ins(st, PFSYNC_S_INS); + PFSYNC_UNLOCK(sc); + + st->sync_updates = 0; +} + +static int +pfsync_defer(struct pf_state *st, struct mbuf *m) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_deferral *pd; + + if (m->m_flags & (M_BCAST|M_MCAST)) + return (0); + + PFSYNC_LOCK(sc); + + if (sc == NULL || !(sc->sc_ifp->if_flags & IFF_DRV_RUNNING) || + !(sc->sc_flags & PFSYNCF_DEFER)) { + PFSYNC_UNLOCK(sc); + return (0); + } + + if (sc->sc_deferred >= 128) + pfsync_undefer(TAILQ_FIRST(&sc->sc_deferrals), 0); + + pd = malloc(sizeof(*pd), M_PFSYNC, M_NOWAIT); + if (pd == NULL) + return (0); + sc->sc_deferred++; + + m->m_flags |= M_SKIP_FIREWALL; + st->state_flags |= PFSTATE_ACK; + + pd->pd_sc = sc; + pd->pd_refs = 0; + pd->pd_st = st; + pf_ref_state(st); + pd->pd_m = m; + + TAILQ_INSERT_TAIL(&sc->sc_deferrals, pd, pd_entry); + callout_init_mtx(&pd->pd_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + callout_reset(&pd->pd_tmo, 10, pfsync_defer_tmo, pd); + + pfsync_push(sc); + + return (1); +} + +static void +pfsync_undefer(struct pfsync_deferral *pd, int drop) +{ + struct pfsync_softc *sc = pd->pd_sc; + struct mbuf *m = pd->pd_m; + struct pf_state *st = pd->pd_st; + + PFSYNC_LOCK_ASSERT(sc); + + TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); + sc->sc_deferred--; + pd->pd_st->state_flags &= ~PFSTATE_ACK; /* XXX: locking! */ + free(pd, M_PFSYNC); + pf_release_state(st); + + if (drop) + m_freem(m); + else { + _IF_ENQUEUE(&sc->sc_ifp->if_snd, m); + pfsync_push(sc); + } +} + +static void +pfsync_defer_tmo(void *arg) +{ + struct pfsync_deferral *pd = arg; + struct pfsync_softc *sc = pd->pd_sc; + struct mbuf *m = pd->pd_m; + struct pf_state *st = pd->pd_st; + + PFSYNC_LOCK_ASSERT(sc); + + CURVNET_SET(m->m_pkthdr.rcvif->if_vnet); + + TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry); + sc->sc_deferred--; + pd->pd_st->state_flags &= ~PFSTATE_ACK; /* XXX: locking! */ + if (pd->pd_refs == 0) + free(pd, M_PFSYNC); + PFSYNC_UNLOCK(sc); + + ip_output(m, NULL, NULL, 0, NULL, NULL); + + pf_release_state(st); + + CURVNET_RESTORE(); +} + +static void +pfsync_undefer_state(struct pf_state *st, int drop) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_deferral *pd; + + PFSYNC_LOCK_ASSERT(sc); + + TAILQ_FOREACH(pd, &sc->sc_deferrals, pd_entry) { + if (pd->pd_st == st) { + if (callout_stop(&pd->pd_tmo)) + pfsync_undefer(pd, drop); + return; + } + } + + panic("%s: unable to find deferred state", __func__); +} + +static void +pfsync_update_state(struct pf_state *st) +{ + struct pfsync_softc *sc = V_pfsyncif; + int sync = 0; + + PF_STATE_LOCK_ASSERT(st); + PFSYNC_LOCK(sc); + + if (st->state_flags & PFSTATE_ACK) + pfsync_undefer_state(st, 0); + if (st->state_flags & PFSTATE_NOSYNC) { + if (st->sync_state != PFSYNC_S_NONE) + pfsync_q_del(st); + PFSYNC_UNLOCK(sc); + return; + } + + if (sc->sc_len == PFSYNC_MINPKT) + callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); + + switch (st->sync_state) { + case PFSYNC_S_UPD_C: + case PFSYNC_S_UPD: + case PFSYNC_S_INS: + /* we're already handling it */ + + if (st->key[PF_SK_WIRE]->proto == IPPROTO_TCP) { + st->sync_updates++; + if (st->sync_updates >= sc->sc_maxupdates) + sync = 1; + } + break; + + case PFSYNC_S_IACK: + pfsync_q_del(st); + case PFSYNC_S_NONE: + pfsync_q_ins(st, PFSYNC_S_UPD_C); + st->sync_updates = 0; + break; + + default: + panic("%s: unexpected sync state %d", __func__, st->sync_state); + } + + if (sync || (time_uptime - st->pfsync_time) < 2) + pfsync_push(sc); + + PFSYNC_UNLOCK(sc); +} + +static void +pfsync_request_update(u_int32_t creatorid, u_int64_t id) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct pfsync_upd_req_item *item; + size_t nlen = sizeof(struct pfsync_upd_req); + + PFSYNC_LOCK_ASSERT(sc); + + /* + * This code does nothing to prevent multiple update requests for the + * same state being generated. + */ + item = malloc(sizeof(*item), M_PFSYNC, M_NOWAIT); + if (item == NULL) + return; /* XXX stats */ + + item->ur_msg.id = id; + item->ur_msg.creatorid = creatorid; + + if (TAILQ_EMPTY(&sc->sc_upd_req_list)) + nlen += sizeof(struct pfsync_subheader); + + if (sc->sc_len + nlen > sc->sc_ifp->if_mtu) { + pfsync_sendout(1); + + nlen = sizeof(struct pfsync_subheader) + + sizeof(struct pfsync_upd_req); + } + + TAILQ_INSERT_TAIL(&sc->sc_upd_req_list, item, ur_entry); + sc->sc_len += nlen; + + pfsync_push(sc); +} + +static void +pfsync_update_state_req(struct pf_state *st) +{ + struct pfsync_softc *sc = V_pfsyncif; + + PF_STATE_LOCK_ASSERT(st); + PFSYNC_LOCK(sc); + + if (st->state_flags & PFSTATE_NOSYNC) { + if (st->sync_state != PFSYNC_S_NONE) + pfsync_q_del(st); + PFSYNC_UNLOCK(sc); + return; + } + + switch (st->sync_state) { + case PFSYNC_S_UPD_C: + case PFSYNC_S_IACK: + pfsync_q_del(st); + case PFSYNC_S_NONE: + pfsync_q_ins(st, PFSYNC_S_UPD); + pfsync_push(sc); + break; + + case PFSYNC_S_INS: + case PFSYNC_S_UPD: + case PFSYNC_S_DEL: + /* we're already handling it */ + break; + + default: + panic("%s: unexpected sync state %d", __func__, st->sync_state); + } + + PFSYNC_UNLOCK(sc); +} + +static void +pfsync_delete_state(struct pf_state *st) +{ + struct pfsync_softc *sc = V_pfsyncif; + + PFSYNC_LOCK(sc); + if (st->state_flags & PFSTATE_ACK) + pfsync_undefer_state(st, 1); + if (st->state_flags & PFSTATE_NOSYNC) { + if (st->sync_state != PFSYNC_S_NONE) + pfsync_q_del(st); + PFSYNC_UNLOCK(sc); + return; + } + + if (sc->sc_len == PFSYNC_MINPKT) + callout_reset(&sc->sc_tmo, 1 * hz, pfsync_timeout, V_pfsyncif); + + switch (st->sync_state) { + case PFSYNC_S_INS: + /* We never got to tell the world so just forget about it. */ + pfsync_q_del(st); + break; + + case PFSYNC_S_UPD_C: + case PFSYNC_S_UPD: + case PFSYNC_S_IACK: + pfsync_q_del(st); + /* FALLTHROUGH to putting it on the del list */ + + case PFSYNC_S_NONE: + pfsync_q_ins(st, PFSYNC_S_DEL); + break; + + default: + panic("%s: unexpected sync state %d", __func__, st->sync_state); + } + PFSYNC_UNLOCK(sc); +} + +static void +pfsync_clear_states(u_int32_t creatorid, const char *ifname) +{ + struct pfsync_softc *sc = V_pfsyncif; + struct { + struct pfsync_subheader subh; + struct pfsync_clr clr; + } __packed r; + + bzero(&r, sizeof(r)); + + r.subh.action = PFSYNC_ACT_CLR; + r.subh.count = htons(1); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_CLR]++; + + strlcpy(r.clr.ifname, ifname, sizeof(r.clr.ifname)); + r.clr.creatorid = creatorid; + + PFSYNC_LOCK(sc); + pfsync_send_plus(&r, sizeof(r)); + PFSYNC_UNLOCK(sc); +} + +static void +pfsync_q_ins(struct pf_state *st, int q) +{ + struct pfsync_softc *sc = V_pfsyncif; + size_t nlen = pfsync_qs[q].len; + + PFSYNC_LOCK_ASSERT(sc); + + KASSERT(st->sync_state == PFSYNC_S_NONE, + ("%s: st->sync_state == PFSYNC_S_NONE", __func__)); + KASSERT(sc->sc_len >= PFSYNC_MINPKT, ("pfsync pkt len is too low %zu", + sc->sc_len)); + + if (TAILQ_EMPTY(&sc->sc_qs[q])) + nlen += sizeof(struct pfsync_subheader); + + if (sc->sc_len + nlen > sc->sc_ifp->if_mtu) { + pfsync_sendout(1); + + nlen = sizeof(struct pfsync_subheader) + pfsync_qs[q].len; + } + + sc->sc_len += nlen; + TAILQ_INSERT_TAIL(&sc->sc_qs[q], st, sync_list); + st->sync_state = q; + pf_ref_state(st); +} + +static void +pfsync_q_del(struct pf_state *st) +{ + struct pfsync_softc *sc = V_pfsyncif; + int q = st->sync_state; + + PFSYNC_LOCK_ASSERT(sc); + KASSERT(st->sync_state != PFSYNC_S_NONE, + ("%s: st->sync_state != PFSYNC_S_NONE", __func__)); + + sc->sc_len -= pfsync_qs[q].len; + TAILQ_REMOVE(&sc->sc_qs[q], st, sync_list); + st->sync_state = PFSYNC_S_NONE; + pf_release_state(st); + + if (TAILQ_EMPTY(&sc->sc_qs[q])) + sc->sc_len -= sizeof(struct pfsync_subheader); +} + +static void +pfsync_bulk_start(void) +{ + struct pfsync_softc *sc = V_pfsyncif; + + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received bulk update request\n"); + + PFSYNC_BLOCK(sc); + + sc->sc_ureq_received = time_uptime; + sc->sc_bulk_hashid = 0; + sc->sc_bulk_stateid = 0; + pfsync_bulk_status(PFSYNC_BUS_START); + callout_reset(&sc->sc_bulk_tmo, 1, pfsync_bulk_update, sc); + PFSYNC_BUNLOCK(sc); +} + +static void +pfsync_bulk_update(void *arg) +{ + struct pfsync_softc *sc = arg; + struct pf_state *s; + int i, sent = 0; + + PFSYNC_BLOCK_ASSERT(sc); + CURVNET_SET(sc->sc_ifp->if_vnet); + + /* + * Start with last state from previous invocation. + * It may had gone, in this case start from the + * hash slot. + */ + s = pf_find_state_byid(sc->sc_bulk_stateid, sc->sc_bulk_creatorid); + + if (s != NULL) + i = PF_IDHASH(s); + else + i = sc->sc_bulk_hashid; + + for (; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + + if (s != NULL) + PF_HASHROW_ASSERT(ih); + else { + PF_HASHROW_LOCK(ih); + s = LIST_FIRST(&ih->states); + } + + for (; s; s = LIST_NEXT(s, entry)) { + + if (sent > 1 && (sc->sc_ifp->if_mtu - sc->sc_len) < + sizeof(struct pfsync_state)) { + /* We've filled a packet. */ + sc->sc_bulk_hashid = i; + sc->sc_bulk_stateid = s->id; + sc->sc_bulk_creatorid = s->creatorid; + PF_HASHROW_UNLOCK(ih); + callout_reset(&sc->sc_bulk_tmo, 1, + pfsync_bulk_update, sc); + goto full; + } + + if (s->sync_state == PFSYNC_S_NONE && + s->timeout < PFTM_MAX && + s->pfsync_time <= sc->sc_ureq_received) { + PFSYNC_LOCK(sc); + pfsync_update_state_req(s); + PFSYNC_UNLOCK(sc); + sent++; + } + } + PF_HASHROW_UNLOCK(ih); + } + + /* We're done. */ + pfsync_bulk_status(PFSYNC_BUS_END); + +full: + CURVNET_RESTORE(); +} + +static void +pfsync_bulk_status(u_int8_t status) +{ + struct { + struct pfsync_subheader subh; + struct pfsync_bus bus; + } __packed r; + + struct pfsync_softc *sc = V_pfsyncif; + + bzero(&r, sizeof(r)); + + r.subh.action = PFSYNC_ACT_BUS; + r.subh.count = htons(1); + V_pfsyncstats.pfsyncs_oacts[PFSYNC_ACT_BUS]++; + + r.bus.creatorid = V_pf_status.hostid; + r.bus.endtime = htonl(time_uptime - sc->sc_ureq_received); + r.bus.status = status; + + PFSYNC_LOCK(sc); + pfsync_send_plus(&r, sizeof(r)); + PFSYNC_UNLOCK(sc); +} + +static void +pfsync_bulk_fail(void *arg) +{ + struct pfsync_softc *sc = arg; + + CURVNET_SET(sc->sc_ifp->if_vnet); + + PFSYNC_BLOCK_ASSERT(sc); + + if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) { + /* Try again */ + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, + pfsync_bulk_fail, V_pfsyncif); + PFSYNC_LOCK(sc); + pfsync_request_update(0, 0); + PFSYNC_UNLOCK(sc); + } else { + /* Pretend like the transfer was ok. */ + sc->sc_ureq_sent = 0; + sc->sc_bulk_tries = 0; + PFSYNC_LOCK(sc); + if (!(sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p) + (*carp_demote_adj_p)(-V_pfsync_carp_adj, + "pfsync bulk fail"); + sc->sc_flags |= PFSYNCF_OK; + PFSYNC_UNLOCK(sc); + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: failed to receive bulk update\n"); + } + + CURVNET_RESTORE(); +} + +static void +pfsync_send_plus(void *plus, size_t pluslen) +{ + struct pfsync_softc *sc = V_pfsyncif; + + PFSYNC_LOCK_ASSERT(sc); + + if (sc->sc_len + pluslen > sc->sc_ifp->if_mtu) + pfsync_sendout(1); + + sc->sc_plus = plus; + sc->sc_len += (sc->sc_pluslen = pluslen); + + pfsync_sendout(1); +} + +static void +pfsync_timeout(void *arg) +{ + struct pfsync_softc *sc = arg; + + CURVNET_SET(sc->sc_ifp->if_vnet); + PFSYNC_LOCK(sc); + pfsync_push(sc); + PFSYNC_UNLOCK(sc); + CURVNET_RESTORE(); +} + +static void +pfsync_push(struct pfsync_softc *sc) +{ + + PFSYNC_LOCK_ASSERT(sc); + + sc->sc_flags |= PFSYNCF_PUSH; + swi_sched(V_pfsync_swi_cookie, 0); +} + +static void +pfsyncintr(void *arg) +{ + struct pfsync_softc *sc = arg; + struct mbuf *m, *n; + + CURVNET_SET(sc->sc_ifp->if_vnet); + + PFSYNC_LOCK(sc); + if ((sc->sc_flags & PFSYNCF_PUSH) && sc->sc_len > PFSYNC_MINPKT) { + pfsync_sendout(0); + sc->sc_flags &= ~PFSYNCF_PUSH; + } + _IF_DEQUEUE_ALL(&sc->sc_ifp->if_snd, m); + PFSYNC_UNLOCK(sc); + + for (; m != NULL; m = n) { + + n = m->m_nextpkt; + m->m_nextpkt = NULL; + + /* + * We distinguish between a deferral packet and our + * own pfsync packet based on M_SKIP_FIREWALL + * flag. This is XXX. + */ + if (m->m_flags & M_SKIP_FIREWALL) + ip_output(m, NULL, NULL, 0, NULL, NULL); + else if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, + NULL) == 0) + V_pfsyncstats.pfsyncs_opackets++; + else + V_pfsyncstats.pfsyncs_oerrors++; + } + CURVNET_RESTORE(); +} + +static int +pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp, void *mship) +{ + struct ip_moptions *imo = &sc->sc_imo; + int error; + + if (!(ifp->if_flags & IFF_MULTICAST)) + return (EADDRNOTAVAIL); + + imo->imo_membership = (struct in_multi **)mship; + imo->imo_max_memberships = IP_MIN_MEMBERSHIPS; + imo->imo_multicast_vif = -1; + + if ((error = in_joingroup(ifp, &sc->sc_sync_peer, NULL, + &imo->imo_membership[0])) != 0) { + imo->imo_membership = NULL; + return (error); + } + imo->imo_num_memberships++; + imo->imo_multicast_ifp = ifp; + imo->imo_multicast_ttl = PFSYNC_DFLTTL; + imo->imo_multicast_loop = 0; + + return (0); +} + +static void +pfsync_multicast_cleanup(struct pfsync_softc *sc) +{ + struct ip_moptions *imo = &sc->sc_imo; + + in_leavegroup(imo->imo_membership[0], NULL); + free(imo->imo_membership, M_PFSYNC); + imo->imo_membership = NULL; + imo->imo_multicast_ifp = NULL; +} + +#ifdef INET +extern struct domain inetdomain; +static struct protosw in_pfsync_protosw = { + .pr_type = SOCK_RAW, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_PFSYNC, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = pfsync_input, + .pr_output = (pr_output_t *)rip_output, + .pr_ctloutput = rip_ctloutput, + .pr_usrreqs = &rip_usrreqs +}; +#endif + +static int +pfsync_init() +{ + VNET_ITERATOR_DECL(vnet_iter); + int error = 0; + + VNET_LIST_RLOCK(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + V_pfsync_cloner = pfsync_cloner; + V_pfsync_cloner_data = pfsync_cloner_data; + V_pfsync_cloner.ifc_data = &V_pfsync_cloner_data; + if_clone_attach(&V_pfsync_cloner); + error = swi_add(NULL, "pfsync", pfsyncintr, V_pfsyncif, + SWI_NET, INTR_MPSAFE, &V_pfsync_swi_cookie); + CURVNET_RESTORE(); + if (error) + goto fail_locked; + } + VNET_LIST_RUNLOCK(); +#ifdef INET + error = pf_proto_register(PF_INET, &in_pfsync_protosw); + if (error) + goto fail; + error = ipproto_register(IPPROTO_PFSYNC); + if (error) { + pf_proto_unregister(PF_INET, IPPROTO_PFSYNC, SOCK_RAW); + goto fail; + } +#endif + PF_RULES_WLOCK(); + pfsync_state_import_ptr = pfsync_state_import; + pfsync_insert_state_ptr = pfsync_insert_state; + pfsync_update_state_ptr = pfsync_update_state; + pfsync_delete_state_ptr = pfsync_delete_state; + pfsync_clear_states_ptr = pfsync_clear_states; + pfsync_defer_ptr = pfsync_defer; + PF_RULES_WUNLOCK(); + + return (0); + +fail: + VNET_LIST_RLOCK(); +fail_locked: + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + if (V_pfsync_swi_cookie) { + swi_remove(V_pfsync_swi_cookie); + if_clone_detach(&V_pfsync_cloner); + } + CURVNET_RESTORE(); + } + VNET_LIST_RUNLOCK(); + + return (error); +} + +static void +pfsync_uninit() +{ + VNET_ITERATOR_DECL(vnet_iter); + + PF_RULES_WLOCK(); + pfsync_state_import_ptr = NULL; + pfsync_insert_state_ptr = NULL; + pfsync_update_state_ptr = NULL; + pfsync_delete_state_ptr = NULL; + pfsync_clear_states_ptr = NULL; + pfsync_defer_ptr = NULL; + PF_RULES_WUNLOCK(); + + ipproto_unregister(IPPROTO_PFSYNC); + pf_proto_unregister(PF_INET, IPPROTO_PFSYNC, SOCK_RAW); + VNET_LIST_RLOCK(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + if_clone_detach(&V_pfsync_cloner); + swi_remove(V_pfsync_swi_cookie); + CURVNET_RESTORE(); + } + VNET_LIST_RUNLOCK(); +} + +static int +pfsync_modevent(module_t mod, int type, void *data) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + error = pfsync_init(); + break; + case MOD_QUIESCE: + /* + * Module should not be unloaded due to race conditions. + */ + error = EPERM; + break; + case MOD_UNLOAD: + pfsync_uninit(); + break; + default: + error = EINVAL; + break; + } + + return (error); +} + +static moduledata_t pfsync_mod = { + "pfsync", + pfsync_modevent, + 0 +}; + +#define PFSYNC_MODVER 1 + +DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); +MODULE_VERSION(pfsync, PFSYNC_MODVER); +MODULE_DEPEND(pfsync, pf, PF_MODVER, PF_MODVER, PF_MODVER); diff --git a/sys/netpfil/pf/in4_cksum.c b/sys/netpfil/pf/in4_cksum.c new file mode 100644 index 0000000000000..bf25baf97a0a1 --- /dev/null +++ b/sys/netpfil/pf/in4_cksum.c @@ -0,0 +1,120 @@ +/* $FreeBSD$ */ +/* $OpenBSD: in4_cksum.c,v 1.7 2003/06/02 23:28:13 millert Exp $ */ +/* $KAME: in4_cksum.c,v 1.10 2001/11/30 10:06:15 itojun Exp $ */ +/* $NetBSD: in_cksum.c,v 1.13 1996/10/13 02:03:03 christos Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * 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 project 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 PROJECT 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 PROJECT 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. + */ + +/* + * Copyright (c) 1988, 1992, 1993 + * 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. + * + * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);} + +int in4_cksum(struct mbuf *, u_int8_t, int, int); + +int +in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len) +{ + union { + struct ipovly ipov; + u_int16_t w[10]; + } u; + union { + u_int16_t s[2]; + u_int32_t l; + } l_util; + + u_int16_t *w; + int psum; + int sum = 0; + + if (nxt != 0) { + /* pseudo header */ + if (off < sizeof(struct ipovly)) + panic("in4_cksum: offset too short"); + if (m->m_len < sizeof(struct ip)) + panic("in4_cksum: bad mbuf chain"); + bzero(&u.ipov, sizeof(u.ipov)); + u.ipov.ih_len = htons(len); + u.ipov.ih_pr = nxt; + u.ipov.ih_src = mtod(m, struct ip *)->ip_src; + u.ipov.ih_dst = mtod(m, struct ip *)->ip_dst; + w = u.w; + /* assumes sizeof(ipov) == 20 */ + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; sum += w[4]; + sum += w[5]; sum += w[6]; sum += w[7]; sum += w[8]; sum += w[9]; + } + + psum = in_cksum_skip(m, len + off, off); + psum = ~psum & 0xffff; + sum += psum; + REDUCE; + return (~sum & 0xffff); +} diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c new file mode 100644 index 0000000000000..a61b87b521d30 --- /dev/null +++ b/sys/netpfil/pf/pf.c @@ -0,0 +1,6271 @@ +/* $OpenBSD: pf.c,v 1.634 2009/02/27 12:37:45 henning Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002 - 2008 Henning Brauer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + */ + +#include + +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_bpf.h" +#include "opt_pf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* XXX: only for DIR_IN/DIR_OUT */ + +#ifdef INET6 +#include +#include +#include +#include +#include +#endif /* INET6 */ + +#include +#include + +#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x + +/* + * Global variables + */ + +/* state tables */ +VNET_DEFINE(struct pf_altqqueue, pf_altqs[2]); +VNET_DEFINE(struct pf_palist, pf_pabuf); +VNET_DEFINE(struct pf_altqqueue *, pf_altqs_active); +VNET_DEFINE(struct pf_altqqueue *, pf_altqs_inactive); +VNET_DEFINE(struct pf_status, pf_status); + +VNET_DEFINE(u_int32_t, ticket_altqs_active); +VNET_DEFINE(u_int32_t, ticket_altqs_inactive); +VNET_DEFINE(int, altqs_inactive_open); +VNET_DEFINE(u_int32_t, ticket_pabuf); + +VNET_DEFINE(MD5_CTX, pf_tcp_secret_ctx); +#define V_pf_tcp_secret_ctx VNET(pf_tcp_secret_ctx) +VNET_DEFINE(u_char, pf_tcp_secret[16]); +#define V_pf_tcp_secret VNET(pf_tcp_secret) +VNET_DEFINE(int, pf_tcp_secret_init); +#define V_pf_tcp_secret_init VNET(pf_tcp_secret_init) +VNET_DEFINE(int, pf_tcp_iss_off); +#define V_pf_tcp_iss_off VNET(pf_tcp_iss_off) + +struct pf_anchor_stackframe { + struct pf_ruleset *rs; + struct pf_rule *r; + struct pf_anchor_node *parent; + struct pf_anchor *child; +}; +VNET_DEFINE(struct pf_anchor_stackframe, pf_anchor_stack[64]); +#define V_pf_anchor_stack VNET(pf_anchor_stack) + +/* + * Queue for pf_intr() sends. + */ +static MALLOC_DEFINE(M_PFTEMP, "pf_temp", "pf(4) temporary allocations"); +struct pf_send_entry { + STAILQ_ENTRY(pf_send_entry) pfse_next; + struct mbuf *pfse_m; + enum { + PFSE_IP, + PFSE_IP6, + PFSE_ICMP, + PFSE_ICMP6, + } pfse_type; + union { + struct route ro; + struct { + int type; + int code; + int mtu; + } icmpopts; + } u; +#define pfse_ro u.ro +#define pfse_icmp_type u.icmpopts.type +#define pfse_icmp_code u.icmpopts.code +#define pfse_icmp_mtu u.icmpopts.mtu +}; + +STAILQ_HEAD(pf_send_head, pf_send_entry); +static VNET_DEFINE(struct pf_send_head, pf_sendqueue); +#define V_pf_sendqueue VNET(pf_sendqueue) + +static struct mtx pf_sendqueue_mtx; +#define PF_SENDQ_LOCK() mtx_lock(&pf_sendqueue_mtx) +#define PF_SENDQ_UNLOCK() mtx_unlock(&pf_sendqueue_mtx) + +/* + * Queue for pf_flush_task() tasks. + */ +struct pf_flush_entry { + SLIST_ENTRY(pf_flush_entry) next; + struct pf_addr addr; + sa_family_t af; + uint8_t dir; + struct pf_rule *rule; /* never dereferenced */ +}; + +SLIST_HEAD(pf_flush_head, pf_flush_entry); +static VNET_DEFINE(struct pf_flush_head, pf_flushqueue); +#define V_pf_flushqueue VNET(pf_flushqueue) +static VNET_DEFINE(struct task, pf_flushtask); +#define V_pf_flushtask VNET(pf_flushtask) + +static struct mtx pf_flushqueue_mtx; +#define PF_FLUSHQ_LOCK() mtx_lock(&pf_flushqueue_mtx) +#define PF_FLUSHQ_UNLOCK() mtx_unlock(&pf_flushqueue_mtx) + +VNET_DEFINE(struct pf_rulequeue, pf_unlinked_rules); +struct mtx pf_unlnkdrules_mtx; + +static VNET_DEFINE(uma_zone_t, pf_sources_z); +#define V_pf_sources_z VNET(pf_sources_z) +static VNET_DEFINE(uma_zone_t, pf_mtag_z); +#define V_pf_mtag_z VNET(pf_mtag_z) +VNET_DEFINE(uma_zone_t, pf_state_z); +VNET_DEFINE(uma_zone_t, pf_state_key_z); + +VNET_DEFINE(uint64_t, pf_stateid[MAXCPU]); +#define PFID_CPUBITS 8 +#define PFID_CPUSHIFT (sizeof(uint64_t) * NBBY - PFID_CPUBITS) +#define PFID_CPUMASK ((uint64_t)((1 << PFID_CPUBITS) - 1) << PFID_CPUSHIFT) +#define PFID_MAXID (~PFID_CPUMASK) +CTASSERT((1 << PFID_CPUBITS) > MAXCPU); + +static void pf_src_tree_remove_state(struct pf_state *); +static void pf_init_threshold(struct pf_threshold *, u_int32_t, + u_int32_t); +static void pf_add_threshold(struct pf_threshold *); +static int pf_check_threshold(struct pf_threshold *); + +static void pf_change_ap(struct pf_addr *, u_int16_t *, + u_int16_t *, u_int16_t *, struct pf_addr *, + u_int16_t, u_int8_t, sa_family_t); +static int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *, + struct tcphdr *, struct pf_state_peer *); +static void pf_change_icmp(struct pf_addr *, u_int16_t *, + struct pf_addr *, struct pf_addr *, u_int16_t, + u_int16_t *, u_int16_t *, u_int16_t *, + u_int16_t *, u_int8_t, sa_family_t); +static void pf_send_tcp(struct mbuf *, + const struct pf_rule *, sa_family_t, + const struct pf_addr *, const struct pf_addr *, + u_int16_t, u_int16_t, u_int32_t, u_int32_t, + u_int8_t, u_int16_t, u_int16_t, u_int8_t, int, + u_int16_t, struct ifnet *); +static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, + sa_family_t, struct pf_rule *); +static void pf_detach_state(struct pf_state *); +static int pf_state_key_attach(struct pf_state_key *, + struct pf_state_key *, struct pf_state *); +static void pf_state_key_detach(struct pf_state *, int); +static int pf_state_key_ctor(void *, int, void *, int); +static u_int32_t pf_tcp_iss(struct pf_pdesc *); +static int pf_test_rule(struct pf_rule **, struct pf_state **, + int, struct pfi_kif *, struct mbuf *, int, + struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **, struct inpcb *); +static int pf_create_state(struct pf_rule *, struct pf_rule *, + struct pf_rule *, struct pf_pdesc *, + struct pf_src_node *, struct pf_state_key *, + struct pf_state_key *, struct mbuf *, int, + u_int16_t, u_int16_t, int *, struct pfi_kif *, + struct pf_state **, int, u_int16_t, u_int16_t, + int); +static int pf_test_fragment(struct pf_rule **, int, + struct pfi_kif *, struct mbuf *, void *, + struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **); +static int pf_tcp_track_full(struct pf_state_peer *, + struct pf_state_peer *, struct pf_state **, + struct pfi_kif *, struct mbuf *, int, + struct pf_pdesc *, u_short *, int *); +static int pf_tcp_track_sloppy(struct pf_state_peer *, + struct pf_state_peer *, struct pf_state **, + struct pf_pdesc *, u_short *); +static int pf_test_state_tcp(struct pf_state **, int, + struct pfi_kif *, struct mbuf *, int, + void *, struct pf_pdesc *, u_short *); +static int pf_test_state_udp(struct pf_state **, int, + struct pfi_kif *, struct mbuf *, int, + void *, struct pf_pdesc *); +static int pf_test_state_icmp(struct pf_state **, int, + struct pfi_kif *, struct mbuf *, int, + void *, struct pf_pdesc *, u_short *); +static int pf_test_state_other(struct pf_state **, int, + struct pfi_kif *, struct mbuf *, struct pf_pdesc *); +static u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, + sa_family_t); +static u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, + sa_family_t); +static u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, + int, u_int16_t); +static void pf_set_rt_ifp(struct pf_state *, + struct pf_addr *); +static int pf_check_proto_cksum(struct mbuf *, int, int, + u_int8_t, sa_family_t); +static void pf_print_state_parts(struct pf_state *, + struct pf_state_key *, struct pf_state_key *); +static int pf_addr_wrap_neq(struct pf_addr_wrap *, + struct pf_addr_wrap *); +static struct pf_state *pf_find_state(struct pfi_kif *, + struct pf_state_key_cmp *, u_int); +static int pf_src_connlimit(struct pf_state **); +static void pf_flush_task(void *c, int pending); +static int pf_insert_src_node(struct pf_src_node **, + struct pf_rule *, struct pf_addr *, sa_family_t); +static int pf_purge_expired_states(int); +static void pf_purge_unlinked_rules(void); +static int pf_mtag_init(void *, int, int); +static void pf_mtag_free(struct m_tag *); +#ifdef INET +static void pf_route(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *, + struct pf_pdesc *); +#endif /* INET */ +#ifdef INET6 +static void pf_change_a6(struct pf_addr *, u_int16_t *, + struct pf_addr *, u_int8_t); +static void pf_route6(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *, + struct pf_pdesc *); +#endif /* INET6 */ + +int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); + +VNET_DECLARE(int, pf_end_threads); + +VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); + +#define PACKET_LOOPED(pd) ((pd)->pf_mtag && \ + (pd)->pf_mtag->flags & PF_PACKET_LOOPED) + +#define STATE_LOOKUP(i, k, d, s, pd) \ + do { \ + (s) = pf_find_state((i), (k), (d)); \ + if ((s) == NULL || (s)->timeout == PFTM_PURGE) \ + return (PF_DROP); \ + if (PACKET_LOOPED(pd)) \ + return (PF_PASS); \ + if ((d) == PF_OUT && \ + (((s)->rule.ptr->rt == PF_ROUTETO && \ + (s)->rule.ptr->direction == PF_OUT) || \ + ((s)->rule.ptr->rt == PF_REPLYTO && \ + (s)->rule.ptr->direction == PF_IN)) && \ + (s)->rt_kif != NULL && \ + (s)->rt_kif != (i)) \ + return (PF_PASS); \ + } while (0) + +#define BOUND_IFACE(r, k) \ + ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all + +#define STATE_INC_COUNTERS(s) \ + do { \ + s->rule.ptr->states_cur++; \ + s->rule.ptr->states_tot++; \ + if (s->anchor.ptr != NULL) { \ + s->anchor.ptr->states_cur++; \ + s->anchor.ptr->states_tot++; \ + } \ + if (s->nat_rule.ptr != NULL) { \ + s->nat_rule.ptr->states_cur++; \ + s->nat_rule.ptr->states_tot++; \ + } \ + } while (0) + +#define STATE_DEC_COUNTERS(s) \ + do { \ + if (s->nat_rule.ptr != NULL) \ + s->nat_rule.ptr->states_cur--; \ + if (s->anchor.ptr != NULL) \ + s->anchor.ptr->states_cur--; \ + s->rule.ptr->states_cur--; \ + } while (0) + +static MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures"); +VNET_DEFINE(struct pf_keyhash *, pf_keyhash); +VNET_DEFINE(struct pf_idhash *, pf_idhash); +VNET_DEFINE(u_long, pf_hashmask); +VNET_DEFINE(struct pf_srchash *, pf_srchash); +VNET_DEFINE(u_long, pf_srchashmask); + +SYSCTL_NODE(_net, OID_AUTO, pf, CTLFLAG_RW, 0, "pf(4)"); + +VNET_DEFINE(u_long, pf_hashsize); +#define V_pf_hashsize VNET(pf_hashsize) +SYSCTL_VNET_UINT(_net_pf, OID_AUTO, states_hashsize, CTLFLAG_RDTUN, + &VNET_NAME(pf_hashsize), 0, "Size of pf(4) states hashtable"); + +VNET_DEFINE(u_long, pf_srchashsize); +#define V_pf_srchashsize VNET(pf_srchashsize) +SYSCTL_VNET_UINT(_net_pf, OID_AUTO, source_nodes_hashsize, CTLFLAG_RDTUN, + &VNET_NAME(pf_srchashsize), 0, "Size of pf(4) source nodes hashtable"); + +VNET_DEFINE(void *, pf_swi_cookie); + +VNET_DEFINE(uint32_t, pf_hashseed); +#define V_pf_hashseed VNET(pf_hashseed) + +static __inline uint32_t +pf_hashkey(struct pf_state_key *sk) +{ + uint32_t h; + + h = jenkins_hash32((uint32_t *)sk, + sizeof(struct pf_state_key_cmp)/sizeof(uint32_t), + V_pf_hashseed); + + return (h & V_pf_hashmask); +} + +#ifdef INET6 +void +pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + dst->addr32[0] = src->addr32[0]; + break; +#endif /* INET */ + case AF_INET6: + dst->addr32[0] = src->addr32[0]; + dst->addr32[1] = src->addr32[1]; + dst->addr32[2] = src->addr32[2]; + dst->addr32[3] = src->addr32[3]; + break; + } +} +#endif /* INET6 */ + +static void +pf_init_threshold(struct pf_threshold *threshold, + u_int32_t limit, u_int32_t seconds) +{ + threshold->limit = limit * PF_THRESHOLD_MULT; + threshold->seconds = seconds; + threshold->count = 0; + threshold->last = time_uptime; +} + +static void +pf_add_threshold(struct pf_threshold *threshold) +{ + u_int32_t t = time_uptime, diff = t - threshold->last; + + if (diff >= threshold->seconds) + threshold->count = 0; + else + threshold->count -= threshold->count * diff / + threshold->seconds; + threshold->count += PF_THRESHOLD_MULT; + threshold->last = t; +} + +static int +pf_check_threshold(struct pf_threshold *threshold) +{ + return (threshold->count > threshold->limit); +} + +static int +pf_src_connlimit(struct pf_state **state) +{ + struct pfr_addr p; + struct pf_flush_entry *pffe; + int bad = 0; + + PF_STATE_LOCK_ASSERT(*state); + + (*state)->src_node->conn++; + (*state)->src.tcp_est = 1; + pf_add_threshold(&(*state)->src_node->conn_rate); + + if ((*state)->rule.ptr->max_src_conn && + (*state)->rule.ptr->max_src_conn < + (*state)->src_node->conn) { + V_pf_status.lcounters[LCNT_SRCCONN]++; + bad++; + } + + if ((*state)->rule.ptr->max_src_conn_rate.limit && + pf_check_threshold(&(*state)->src_node->conn_rate)) { + V_pf_status.lcounters[LCNT_SRCCONNRATE]++; + bad++; + } + + if (!bad) + return (0); + + /* Kill this state. */ + (*state)->timeout = PFTM_PURGE; + (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; + + if ((*state)->rule.ptr->overload_tbl == NULL) + return (1); + + V_pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("%s: blocking address ", __func__); + pf_print_host(&(*state)->src_node->addr, 0, + (*state)->key[PF_SK_WIRE]->af); + printf("\n"); + } + + bzero(&p, sizeof(p)); + p.pfra_af = (*state)->key[PF_SK_WIRE]->af; + switch ((*state)->key[PF_SK_WIRE]->af) { +#ifdef INET + case AF_INET: + p.pfra_net = 32; + p.pfra_ip4addr = (*state)->src_node->addr.v4; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + p.pfra_net = 128; + p.pfra_ip6addr = (*state)->src_node->addr.v6; + break; +#endif /* INET6 */ + } + + pfr_insert_kentry((*state)->rule.ptr->overload_tbl, &p, time_second); + + if ((*state)->rule.ptr->flush == 0) + return (1); + + /* Schedule flushing task. */ + pffe = malloc(sizeof(*pffe), M_PFTEMP, M_NOWAIT); + if (pffe == NULL) + return (1); /* too bad :( */ + + bcopy(&(*state)->src_node->addr, &pffe->addr, sizeof(pffe->addr)); + pffe->af = (*state)->key[PF_SK_WIRE]->af; + pffe->dir = (*state)->direction; + if ((*state)->rule.ptr->flush & PF_FLUSH_GLOBAL) + pffe->rule = NULL; + else + pffe->rule = (*state)->rule.ptr; + PF_FLUSHQ_LOCK(); + SLIST_INSERT_HEAD(&V_pf_flushqueue, pffe, next); + PF_FLUSHQ_UNLOCK(); + taskqueue_enqueue(taskqueue_swi, &V_pf_flushtask); + + return (1); +} + +static void +pf_flush_task(void *c, int pending) +{ + struct pf_flush_head queue; + struct pf_flush_entry *pffe, *pffe1; + uint32_t killed = 0; + + PF_FLUSHQ_LOCK(); + queue = *(struct pf_flush_head *)c; + SLIST_INIT((struct pf_flush_head *)c); + PF_FLUSHQ_UNLOCK(); + + V_pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; + + for (int i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_state_key *sk; + struct pf_state *s; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + sk = s->key[PF_SK_WIRE]; + SLIST_FOREACH(pffe, &queue, next) + if (sk->af == pffe->af && (pffe->rule == NULL || + pffe->rule == s->rule.ptr) && + ((pffe->dir == PF_OUT && + PF_AEQ(&pffe->addr, &sk->addr[1], sk->af)) || + (pffe->dir == PF_IN && + PF_AEQ(&pffe->addr, &sk->addr[0], sk->af)))) { + s->timeout = PFTM_PURGE; + s->src.state = s->dst.state = TCPS_CLOSED; + killed++; + } + } + PF_HASHROW_UNLOCK(ih); + } + SLIST_FOREACH_SAFE(pffe, &queue, next, pffe1) + free(pffe, M_PFTEMP); + if (V_pf_status.debug >= PF_DEBUG_MISC) + printf("%s: %u states killed", __func__, killed); +} + +/* + * Can return locked on failure, so that we can consistently + * allocate and insert a new one. + */ +struct pf_src_node * +pf_find_src_node(struct pf_addr *src, struct pf_rule *rule, sa_family_t af, + int returnlocked) +{ + struct pf_srchash *sh; + struct pf_src_node *n; + + V_pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; + + sh = &V_pf_srchash[pf_hashsrc(src, af)]; + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) + if (n->rule.ptr == rule && n->af == af && + ((af == AF_INET && n->addr.v4.s_addr == src->v4.s_addr) || + (af == AF_INET6 && bcmp(&n->addr, src, sizeof(*src)) == 0))) + break; + if (n != NULL || returnlocked == 0) + PF_HASHROW_UNLOCK(sh); + + return (n); +} + +static int +pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, + struct pf_addr *src, sa_family_t af) +{ + + KASSERT((rule->rule_flag & PFRULE_RULESRCTRACK || + rule->rpool.opts & PF_POOL_STICKYADDR), + ("%s for non-tracking rule %p", __func__, rule)); + + if (*sn == NULL) + *sn = pf_find_src_node(src, rule, af, 1); + + if (*sn == NULL) { + struct pf_srchash *sh = &V_pf_srchash[pf_hashsrc(src, af)]; + + PF_HASHROW_ASSERT(sh); + + if (!rule->max_src_nodes || + rule->src_nodes < rule->max_src_nodes) + (*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO); + else + V_pf_status.lcounters[LCNT_SRCNODES]++; + if ((*sn) == NULL) { + PF_HASHROW_UNLOCK(sh); + return (-1); + } + + pf_init_threshold(&(*sn)->conn_rate, + rule->max_src_conn_rate.limit, + rule->max_src_conn_rate.seconds); + + (*sn)->af = af; + (*sn)->rule.ptr = rule; + PF_ACPY(&(*sn)->addr, src, af); + LIST_INSERT_HEAD(&sh->nodes, *sn, entry); + (*sn)->creation = time_uptime; + (*sn)->ruletype = rule->action; + if ((*sn)->rule.ptr != NULL) + (*sn)->rule.ptr->src_nodes++; + PF_HASHROW_UNLOCK(sh); + V_pf_status.scounters[SCNT_SRC_NODE_INSERT]++; + V_pf_status.src_nodes++; + } else { + if (rule->max_src_states && + (*sn)->states >= rule->max_src_states) { + V_pf_status.lcounters[LCNT_SRCSTATES]++; + return (-1); + } + } + return (0); +} + +static void +pf_remove_src_node(struct pf_src_node *src) +{ + struct pf_srchash *sh; + + sh = &V_pf_srchash[pf_hashsrc(&src->addr, src->af)]; + PF_HASHROW_LOCK(sh); + LIST_REMOVE(src, entry); + PF_HASHROW_UNLOCK(sh); +} + +/* Data storage structures initialization. */ +void +pf_initialize() +{ + struct pf_keyhash *kh; + struct pf_idhash *ih; + struct pf_srchash *sh; + u_int i; + + TUNABLE_ULONG_FETCH("net.pf.states_hashsize", &V_pf_hashsize); + if (V_pf_hashsize == 0 || !powerof2(V_pf_hashsize)) + V_pf_hashsize = PF_HASHSIZ; + TUNABLE_ULONG_FETCH("net.pf.source_nodes_hashsize", &V_pf_srchashsize); + if (V_pf_srchashsize == 0 || !powerof2(V_pf_srchashsize)) + V_pf_srchashsize = PF_HASHSIZ / 4; + + V_pf_hashseed = arc4random(); + + /* States and state keys storage. */ + V_pf_state_z = uma_zcreate("pf states", sizeof(struct pf_state), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + V_pf_limits[PF_LIMIT_STATES].zone = V_pf_state_z; + uma_zone_set_max(V_pf_state_z, PFSTATE_HIWAT); + + V_pf_state_key_z = uma_zcreate("pf state keys", + sizeof(struct pf_state_key), pf_state_key_ctor, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + V_pf_keyhash = malloc(V_pf_hashsize * sizeof(struct pf_keyhash), + M_PFHASH, M_WAITOK | M_ZERO); + V_pf_idhash = malloc(V_pf_hashsize * sizeof(struct pf_idhash), + M_PFHASH, M_WAITOK | M_ZERO); + V_pf_hashmask = V_pf_hashsize - 1; + for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask; + i++, kh++, ih++) { + mtx_init(&kh->lock, "pf_keyhash", NULL, MTX_DEF); + mtx_init(&ih->lock, "pf_idhash", NULL, MTX_DEF); + } + + /* Source nodes. */ + V_pf_sources_z = uma_zcreate("pf source nodes", + sizeof(struct pf_src_node), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, + 0); + V_pf_limits[PF_LIMIT_SRC_NODES].zone = V_pf_sources_z; + uma_zone_set_max(V_pf_sources_z, PFSNODE_HIWAT); + V_pf_srchash = malloc(V_pf_srchashsize * sizeof(struct pf_srchash), + M_PFHASH, M_WAITOK|M_ZERO); + V_pf_srchashmask = V_pf_srchashsize - 1; + for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) + mtx_init(&sh->lock, "pf_srchash", NULL, MTX_DEF); + + /* ALTQ */ + TAILQ_INIT(&V_pf_altqs[0]); + TAILQ_INIT(&V_pf_altqs[1]); + TAILQ_INIT(&V_pf_pabuf); + V_pf_altqs_active = &V_pf_altqs[0]; + V_pf_altqs_inactive = &V_pf_altqs[1]; + + /* Mbuf tags */ + V_pf_mtag_z = uma_zcreate("pf mtags", sizeof(struct m_tag) + + sizeof(struct pf_mtag), NULL, NULL, pf_mtag_init, NULL, + UMA_ALIGN_PTR, 0); + + /* Send & flush queues. */ + STAILQ_INIT(&V_pf_sendqueue); + SLIST_INIT(&V_pf_flushqueue); + TASK_INIT(&V_pf_flushtask, 0, pf_flush_task, &V_pf_flushqueue); + mtx_init(&pf_sendqueue_mtx, "pf send queue", NULL, MTX_DEF); + mtx_init(&pf_flushqueue_mtx, "pf flush queue", NULL, MTX_DEF); + + /* Unlinked, but may be referenced rules. */ + TAILQ_INIT(&V_pf_unlinked_rules); + mtx_init(&pf_unlnkdrules_mtx, "pf unlinked rules", NULL, MTX_DEF); +} + +void +pf_cleanup() +{ + struct pf_keyhash *kh; + struct pf_idhash *ih; + struct pf_srchash *sh; + struct pf_send_entry *pfse, *next; + u_int i; + + for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask; + i++, kh++, ih++) { + KASSERT(LIST_EMPTY(&kh->keys), ("%s: key hash not empty", + __func__)); + KASSERT(LIST_EMPTY(&ih->states), ("%s: id hash not empty", + __func__)); + mtx_destroy(&kh->lock); + mtx_destroy(&ih->lock); + } + free(V_pf_keyhash, M_PFHASH); + free(V_pf_idhash, M_PFHASH); + + for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) { + KASSERT(LIST_EMPTY(&sh->nodes), + ("%s: source node hash not empty", __func__)); + mtx_destroy(&sh->lock); + } + free(V_pf_srchash, M_PFHASH); + + STAILQ_FOREACH_SAFE(pfse, &V_pf_sendqueue, pfse_next, next) { + m_freem(pfse->pfse_m); + free(pfse, M_PFTEMP); + } + + mtx_destroy(&pf_sendqueue_mtx); + mtx_destroy(&pf_flushqueue_mtx); + mtx_destroy(&pf_unlnkdrules_mtx); + + uma_zdestroy(V_pf_mtag_z); + uma_zdestroy(V_pf_sources_z); + uma_zdestroy(V_pf_state_z); + uma_zdestroy(V_pf_state_key_z); +} + +static int +pf_mtag_init(void *mem, int size, int how) +{ + struct m_tag *t; + + t = (struct m_tag *)mem; + t->m_tag_cookie = MTAG_ABI_COMPAT; + t->m_tag_id = PACKET_TAG_PF; + t->m_tag_len = sizeof(struct pf_mtag); + t->m_tag_free = pf_mtag_free; + + return (0); +} + +static void +pf_mtag_free(struct m_tag *t) +{ + + uma_zfree(V_pf_mtag_z, t); +} + +struct pf_mtag * +pf_get_mtag(struct mbuf *m) +{ + struct m_tag *mtag; + + if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) != NULL) + return ((struct pf_mtag *)(mtag + 1)); + + mtag = uma_zalloc(V_pf_mtag_z, M_NOWAIT); + if (mtag == NULL) + return (NULL); + bzero(mtag + 1, sizeof(struct pf_mtag)); + m_tag_prepend(m, mtag); + + return ((struct pf_mtag *)(mtag + 1)); +} + +static int +pf_state_key_attach(struct pf_state_key *skw, struct pf_state_key *sks, + struct pf_state *s) +{ + struct pf_keyhash *kh; + struct pf_state_key *sk, *cur; + struct pf_state *si, *olds = NULL; + int idx; + + KASSERT(s->refs == 0, ("%s: state not pristine", __func__)); + KASSERT(s->key[PF_SK_WIRE] == NULL, ("%s: state has key", __func__)); + KASSERT(s->key[PF_SK_STACK] == NULL, ("%s: state has key", __func__)); + + /* + * First run: start with wire key. + */ + sk = skw; + idx = PF_SK_WIRE; + +keyattach: + kh = &V_pf_keyhash[pf_hashkey(sk)]; + + PF_HASHROW_LOCK(kh); + LIST_FOREACH(cur, &kh->keys, entry) + if (bcmp(cur, sk, sizeof(struct pf_state_key_cmp)) == 0) + break; + + if (cur != NULL) { + /* Key exists. Check for same kif, if none, add to key. */ + TAILQ_FOREACH(si, &cur->states[idx], key_list[idx]) { + struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(si)]; + + PF_HASHROW_LOCK(ih); + if (si->kif == s->kif && + si->direction == s->direction) { + if (sk->proto == IPPROTO_TCP && + si->src.state >= TCPS_FIN_WAIT_2 && + si->dst.state >= TCPS_FIN_WAIT_2) { + si->src.state = si->dst.state = + TCPS_CLOSED; + /* Unlink later or cur can go away. */ + pf_ref_state(si); + olds = si; + } else { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: %s key attach " + "failed on %s: ", + (idx == PF_SK_WIRE) ? + "wire" : "stack", + s->kif->pfik_name); + pf_print_state_parts(s, + (idx == PF_SK_WIRE) ? + sk : NULL, + (idx == PF_SK_STACK) ? + sk : NULL); + printf(", existing: "); + pf_print_state_parts(si, + (idx == PF_SK_WIRE) ? + sk : NULL, + (idx == PF_SK_STACK) ? + sk : NULL); + printf("\n"); + } + PF_HASHROW_UNLOCK(ih); + PF_HASHROW_UNLOCK(kh); + uma_zfree(V_pf_state_key_z, sk); + if (idx == PF_SK_STACK) + pf_detach_state(s); + return (-1); /* collision! */ + } + } + PF_HASHROW_UNLOCK(ih); + } + uma_zfree(V_pf_state_key_z, sk); + s->key[idx] = cur; + } else { + LIST_INSERT_HEAD(&kh->keys, sk, entry); + s->key[idx] = sk; + } + +stateattach: + /* List is sorted, if-bound states before floating. */ + if (s->kif == V_pfi_all) + TAILQ_INSERT_TAIL(&s->key[idx]->states[idx], s, key_list[idx]); + else + TAILQ_INSERT_HEAD(&s->key[idx]->states[idx], s, key_list[idx]); + + /* + * Attach done. See how should we (or should not?) + * attach a second key. + */ + if (sks == skw) { + s->key[PF_SK_STACK] = s->key[PF_SK_WIRE]; + idx = PF_SK_STACK; + sks = NULL; + goto stateattach; + } else if (sks != NULL) { + PF_HASHROW_UNLOCK(kh); + if (olds) { + pf_unlink_state(olds, 0); + pf_release_state(olds); + olds = NULL; + } + /* + * Continue attaching with stack key. + */ + sk = sks; + idx = PF_SK_STACK; + sks = NULL; + goto keyattach; + } else + PF_HASHROW_UNLOCK(kh); + + if (olds) { + pf_unlink_state(olds, 0); + pf_release_state(olds); + } + + KASSERT(s->key[PF_SK_WIRE] != NULL && s->key[PF_SK_STACK] != NULL, + ("%s failure", __func__)); + + return (0); +} + +static void +pf_detach_state(struct pf_state *s) +{ + struct pf_state_key *sks = s->key[PF_SK_STACK]; + struct pf_keyhash *kh; + + if (sks != NULL) { + kh = &V_pf_keyhash[pf_hashkey(sks)]; + PF_HASHROW_LOCK(kh); + if (s->key[PF_SK_STACK] != NULL) + pf_state_key_detach(s, PF_SK_STACK); + /* + * If both point to same key, then we are done. + */ + if (sks == s->key[PF_SK_WIRE]) { + pf_state_key_detach(s, PF_SK_WIRE); + PF_HASHROW_UNLOCK(kh); + return; + } + PF_HASHROW_UNLOCK(kh); + } + + if (s->key[PF_SK_WIRE] != NULL) { + kh = &V_pf_keyhash[pf_hashkey(s->key[PF_SK_WIRE])]; + PF_HASHROW_LOCK(kh); + if (s->key[PF_SK_WIRE] != NULL) + pf_state_key_detach(s, PF_SK_WIRE); + PF_HASHROW_UNLOCK(kh); + } +} + +static void +pf_state_key_detach(struct pf_state *s, int idx) +{ + struct pf_state_key *sk = s->key[idx]; +#ifdef INVARIANTS + struct pf_keyhash *kh = &V_pf_keyhash[pf_hashkey(sk)]; + + PF_HASHROW_ASSERT(kh); +#endif + TAILQ_REMOVE(&sk->states[idx], s, key_list[idx]); + s->key[idx] = NULL; + + if (TAILQ_EMPTY(&sk->states[0]) && TAILQ_EMPTY(&sk->states[1])) { + LIST_REMOVE(sk, entry); + uma_zfree(V_pf_state_key_z, sk); + } +} + +static int +pf_state_key_ctor(void *mem, int size, void *arg, int flags) +{ + struct pf_state_key *sk = mem; + + bzero(sk, sizeof(struct pf_state_key_cmp)); + TAILQ_INIT(&sk->states[PF_SK_WIRE]); + TAILQ_INIT(&sk->states[PF_SK_STACK]); + + return (0); +} + +struct pf_state_key * +pf_state_key_setup(struct pf_pdesc *pd, struct pf_addr *saddr, + struct pf_addr *daddr, u_int16_t sport, u_int16_t dport) +{ + struct pf_state_key *sk; + + sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (sk == NULL) + return (NULL); + + PF_ACPY(&sk->addr[pd->sidx], saddr, pd->af); + PF_ACPY(&sk->addr[pd->didx], daddr, pd->af); + sk->port[pd->sidx] = sport; + sk->port[pd->didx] = dport; + sk->proto = pd->proto; + sk->af = pd->af; + + return (sk); +} + +struct pf_state_key * +pf_state_key_clone(struct pf_state_key *orig) +{ + struct pf_state_key *sk; + + sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (sk == NULL) + return (NULL); + + bcopy(orig, sk, sizeof(struct pf_state_key_cmp)); + + return (sk); +} + +int +pf_state_insert(struct pfi_kif *kif, struct pf_state_key *skw, + struct pf_state_key *sks, struct pf_state *s) +{ + struct pf_idhash *ih; + struct pf_state *cur; + + KASSERT(TAILQ_EMPTY(&sks->states[0]) && TAILQ_EMPTY(&sks->states[1]), + ("%s: sks not pristine", __func__)); + KASSERT(TAILQ_EMPTY(&skw->states[0]) && TAILQ_EMPTY(&skw->states[1]), + ("%s: skw not pristine", __func__)); + KASSERT(s->refs == 0, ("%s: state not pristine", __func__)); + + s->kif = kif; + + if (pf_state_key_attach(skw, sks, s)) + return (-1); + + if (s->id == 0 && s->creatorid == 0) { + /* XXX: should be atomic, but probability of collision low */ + if ((s->id = V_pf_stateid[curcpu]++) == PFID_MAXID) + V_pf_stateid[curcpu] = 1; + s->id |= (uint64_t )curcpu << PFID_CPUSHIFT; + s->id = htobe64(s->id); + s->creatorid = V_pf_status.hostid; + } + + ih = &V_pf_idhash[PF_IDHASH(s)]; + PF_HASHROW_LOCK(ih); + LIST_FOREACH(cur, &ih->states, entry) + if (cur->id == s->id && cur->creatorid == s->creatorid) + break; + + if (cur != NULL) { + PF_HASHROW_UNLOCK(ih); + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: state insert failed: " + "id: %016llx creatorid: %08x", + (unsigned long long)be64toh(s->id), + ntohl(s->creatorid)); + printf("\n"); + } + pf_detach_state(s); + return (-1); + } + LIST_INSERT_HEAD(&ih->states, s, entry); + /* One for keys, one for ID hash. */ + refcount_init(&s->refs, 2); + + V_pf_status.fcounters[FCNT_STATE_INSERT]++; + if (pfsync_insert_state_ptr != NULL) + pfsync_insert_state_ptr(s); + + /* Returns locked. */ + return (0); +} + +/* + * Find state by ID: returns with locked row on success. + */ +struct pf_state * +pf_find_state_byid(uint64_t id, uint32_t creatorid) +{ + struct pf_idhash *ih; + struct pf_state *s; + + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; + + ih = &V_pf_idhash[(be64toh(id) % (V_pf_hashmask + 1))]; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) + if (s->id == id && s->creatorid == creatorid) + break; + + if (s == NULL) + PF_HASHROW_UNLOCK(ih); + + return (s); +} + +/* + * Find state by key. + * Returns with ID hash slot locked on success. + */ +static struct pf_state * +pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir) +{ + struct pf_keyhash *kh; + struct pf_state_key *sk; + struct pf_state *s; + int idx; + + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; + + kh = &V_pf_keyhash[pf_hashkey((struct pf_state_key *)key)]; + + PF_HASHROW_LOCK(kh); + LIST_FOREACH(sk, &kh->keys, entry) + if (bcmp(sk, key, sizeof(struct pf_state_key_cmp)) == 0) + break; + if (sk == NULL) { + PF_HASHROW_UNLOCK(kh); + return (NULL); + } + + idx = (dir == PF_IN ? PF_SK_WIRE : PF_SK_STACK); + + /* List is sorted, if-bound states before floating ones. */ + TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) + if (s->kif == V_pfi_all || s->kif == kif) { + PF_STATE_LOCK(s); + PF_HASHROW_UNLOCK(kh); + if (s->timeout == PFTM_UNLINKED) { + /* + * State is being processed + * by pf_unlink_state() in + * an other thread. + */ + PF_STATE_UNLOCK(s); + return (NULL); + } + return (s); + } + PF_HASHROW_UNLOCK(kh); + + return (NULL); +} + +struct pf_state * +pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more) +{ + struct pf_keyhash *kh; + struct pf_state_key *sk; + struct pf_state *s, *ret = NULL; + int idx, inout = 0; + + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; + + kh = &V_pf_keyhash[pf_hashkey((struct pf_state_key *)key)]; + + PF_HASHROW_LOCK(kh); + LIST_FOREACH(sk, &kh->keys, entry) + if (bcmp(sk, key, sizeof(struct pf_state_key_cmp)) == 0) + break; + if (sk == NULL) { + PF_HASHROW_UNLOCK(kh); + return (NULL); + } + switch (dir) { + case PF_IN: + idx = PF_SK_WIRE; + break; + case PF_OUT: + idx = PF_SK_STACK; + break; + case PF_INOUT: + idx = PF_SK_WIRE; + inout = 1; + break; + default: + panic("%s: dir %u", __func__, dir); + } +second_run: + TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) { + if (more == NULL) { + PF_HASHROW_UNLOCK(kh); + return (s); + } + + if (ret) + (*more)++; + else + ret = s; + } + if (inout == 1) { + inout = 0; + idx = PF_SK_STACK; + goto second_run; + } + PF_HASHROW_UNLOCK(kh); + + return (ret); +} + +/* END state table stuff */ + +static void +pf_send(struct pf_send_entry *pfse) +{ + + PF_SENDQ_LOCK(); + STAILQ_INSERT_TAIL(&V_pf_sendqueue, pfse, pfse_next); + PF_SENDQ_UNLOCK(); + swi_sched(V_pf_swi_cookie, 0); +} + +void +pf_intr(void *v) +{ + struct pf_send_head queue; + struct pf_send_entry *pfse, *next; + + CURVNET_SET((struct vnet *)v); + + PF_SENDQ_LOCK(); + queue = V_pf_sendqueue; + STAILQ_INIT(&V_pf_sendqueue); + PF_SENDQ_UNLOCK(); + + STAILQ_FOREACH_SAFE(pfse, &queue, pfse_next, next) { + switch (pfse->pfse_type) { +#ifdef INET + case PFSE_IP: + ip_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL); + break; + case PFSE_ICMP: + icmp_error(pfse->pfse_m, pfse->pfse_icmp_type, + pfse->pfse_icmp_code, 0, pfse->pfse_icmp_mtu); + break; +#endif /* INET */ +#ifdef INET6 + case PFSE_IP6: + ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL, + NULL); + break; + case PFSE_ICMP6: + icmp6_error(pfse->pfse_m, pfse->pfse_icmp_type, + pfse->pfse_icmp_code, pfse->pfse_icmp_mtu); + break; +#endif /* INET6 */ + default: + panic("%s: unknown type", __func__); + } + free(pfse, M_PFTEMP); + } + CURVNET_RESTORE(); +} + +void +pf_purge_thread(void *v) +{ + int fullrun; + + CURVNET_SET((struct vnet *)v); + + for (;;) { + PF_RULES_RLOCK(); + rw_sleep(pf_purge_thread, &pf_rules_lock, 0, "pftm", hz / 10); + + if (V_pf_end_threads) { + /* + * To cleanse up all kifs and rules we need + * two runs: first one clears reference flags, + * then pf_purge_expired_states() doesn't + * raise them, and then second run frees. + */ + PF_RULES_RUNLOCK(); + pf_purge_unlinked_rules(); + pfi_kif_purge(); + + /* + * Now purge everything. + */ + pf_purge_expired_states(V_pf_hashmask + 1); + pf_purge_expired_fragments(); + pf_purge_expired_src_nodes(); + + /* + * Now all kifs & rules should be unreferenced, + * thus should be successfully freed. + */ + pf_purge_unlinked_rules(); + pfi_kif_purge(); + + /* + * Announce success and exit. + */ + PF_RULES_RLOCK(); + V_pf_end_threads++; + PF_RULES_RUNLOCK(); + wakeup(pf_purge_thread); + kproc_exit(0); + } + PF_RULES_RUNLOCK(); + + /* Process 1/interval fraction of the state table every run. */ + fullrun = pf_purge_expired_states(V_pf_hashmask / + (V_pf_default_rule.timeout[PFTM_INTERVAL] * 10)); + + /* Purge other expired types every PFTM_INTERVAL seconds. */ + if (fullrun) { + /* + * Order is important: + * - states and src nodes reference rules + * - states and rules reference kifs + */ + pf_purge_expired_fragments(); + pf_purge_expired_src_nodes(); + pf_purge_unlinked_rules(); + pfi_kif_purge(); + } + } + /* not reached */ + CURVNET_RESTORE(); +} + +u_int32_t +pf_state_expires(const struct pf_state *state) +{ + u_int32_t timeout; + u_int32_t start; + u_int32_t end; + u_int32_t states; + + /* handle all PFTM_* > PFTM_MAX here */ + if (state->timeout == PFTM_PURGE) + return (time_uptime); + if (state->timeout == PFTM_UNTIL_PACKET) + return (0); + KASSERT(state->timeout != PFTM_UNLINKED, + ("pf_state_expires: timeout == PFTM_UNLINKED")); + KASSERT((state->timeout < PFTM_MAX), + ("pf_state_expires: timeout > PFTM_MAX")); + timeout = state->rule.ptr->timeout[state->timeout]; + if (!timeout) + timeout = V_pf_default_rule.timeout[state->timeout]; + start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; + if (start) { + end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; + states = state->rule.ptr->states_cur; /* XXXGL */ + } else { + start = V_pf_default_rule.timeout[PFTM_ADAPTIVE_START]; + end = V_pf_default_rule.timeout[PFTM_ADAPTIVE_END]; + states = V_pf_status.states; + } + if (end && states > start && start < end) { + if (states < end) + return (state->expire + timeout * (end - states) / + (end - start)); + else + return (time_uptime); + } + return (state->expire + timeout); +} + +void +pf_purge_expired_src_nodes() +{ + struct pf_srchash *sh; + struct pf_src_node *cur, *next; + int i; + + for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH_SAFE(cur, &sh->nodes, entry, next) + if (cur->states <= 0 && cur->expire <= time_uptime) { + if (cur->rule.ptr != NULL) + cur->rule.ptr->src_nodes--; + LIST_REMOVE(cur, entry); + V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + V_pf_status.src_nodes--; + uma_zfree(V_pf_sources_z, cur); + } else if (cur->rule.ptr != NULL) + cur->rule.ptr->rule_flag |= PFRULE_REFS; + PF_HASHROW_UNLOCK(sh); + } +} + +static void +pf_src_tree_remove_state(struct pf_state *s) +{ + u_int32_t timeout; + + if (s->src_node != NULL) { + if (s->src.tcp_est) + --s->src_node->conn; + if (--s->src_node->states <= 0) { + timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; + if (!timeout) + timeout = + V_pf_default_rule.timeout[PFTM_SRC_NODE]; + s->src_node->expire = time_uptime + timeout; + } + } + if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { + if (--s->nat_src_node->states <= 0) { + timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; + if (!timeout) + timeout = + V_pf_default_rule.timeout[PFTM_SRC_NODE]; + s->nat_src_node->expire = time_uptime + timeout; + } + } + s->src_node = s->nat_src_node = NULL; +} + +/* + * Unlink and potentilly free a state. Function may be + * called with ID hash row locked, but always returns + * unlocked, since it needs to go through key hash locking. + */ +int +pf_unlink_state(struct pf_state *s, u_int flags) +{ + struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(s)]; + + if ((flags & PF_ENTER_LOCKED) == 0) + PF_HASHROW_LOCK(ih); + else + PF_HASHROW_ASSERT(ih); + + if (s->timeout == PFTM_UNLINKED) { + /* + * State is being processed + * by pf_unlink_state() in + * an other thread. + */ + PF_HASHROW_UNLOCK(ih); + return (0); /* XXXGL: undefined actually */ + } + + s->timeout = PFTM_UNLINKED; + + if (s->src.state == PF_TCPS_PROXY_DST) { + /* XXX wire key the right one? */ + pf_send_tcp(NULL, s->rule.ptr, s->key[PF_SK_WIRE]->af, + &s->key[PF_SK_WIRE]->addr[1], + &s->key[PF_SK_WIRE]->addr[0], + s->key[PF_SK_WIRE]->port[1], + s->key[PF_SK_WIRE]->port[0], + s->src.seqhi, s->src.seqlo + 1, + TH_RST|TH_ACK, 0, 0, 0, 1, s->tag, NULL); + } + + LIST_REMOVE(s, entry); + pf_src_tree_remove_state(s); + PF_HASHROW_UNLOCK(ih); + + if (pfsync_delete_state_ptr != NULL) + pfsync_delete_state_ptr(s); + + pf_detach_state(s); + refcount_release(&s->refs); + + return (pf_release_state(s)); +} + +void +pf_free_state(struct pf_state *cur) +{ + + KASSERT(cur->refs == 0, ("%s: %p has refs", __func__, cur)); + KASSERT(cur->timeout == PFTM_UNLINKED, ("%s: timeout %u", __func__, + cur->timeout)); + --cur->rule.ptr->states_cur; + if (cur->nat_rule.ptr != NULL) + --cur->nat_rule.ptr->states_cur; + if (cur->anchor.ptr != NULL) + --cur->anchor.ptr->states_cur; + pf_normalize_tcp_cleanup(cur); + uma_zfree(V_pf_state_z, cur); + V_pf_status.fcounters[FCNT_STATE_REMOVALS]++; +} + +/* + * Called only from pf_purge_thread(), thus serialized. + */ +static int +pf_purge_expired_states(int maxcheck) +{ + static u_int i = 0; + + struct pf_idhash *ih; + struct pf_state *s; + int rv = 0; + + V_pf_status.states = uma_zone_get_cur(V_pf_state_z); + + /* + * Go through hash and unlink states that expire now. + */ + while (maxcheck > 0) { + + /* Wrap to start of hash when we hit the end. */ + if (i > V_pf_hashmask) { + i = 0; + rv = 1; + } + + ih = &V_pf_idhash[i]; +relock: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (pf_state_expires(s) <= time_uptime) { + V_pf_status.states -= + pf_unlink_state(s, PF_ENTER_LOCKED); + goto relock; + } + s->rule.ptr->rule_flag |= PFRULE_REFS; + if (s->nat_rule.ptr != NULL) + s->nat_rule.ptr->rule_flag |= PFRULE_REFS; + if (s->anchor.ptr != NULL) + s->anchor.ptr->rule_flag |= PFRULE_REFS; + s->kif->pfik_flags |= PFI_IFLAG_REFS; + if (s->rt_kif) + s->rt_kif->pfik_flags |= PFI_IFLAG_REFS; + } + PF_HASHROW_UNLOCK(ih); + i++; + maxcheck--; + } + + V_pf_status.states = uma_zone_get_cur(V_pf_state_z); + + return (rv); +} + +static void +pf_purge_unlinked_rules() +{ + struct pf_rulequeue tmpq; + struct pf_rule *r, *r1; + + /* + * Do naive mark-and-sweep garbage collecting of old rules. + * Reference flag is raised by pf_purge_expired_states() + * and pf_purge_expired_src_nodes(). + * + * To avoid LOR between PF_UNLNKDRULES_LOCK/PF_RULES_WLOCK, + * use a temporary queue. + */ + TAILQ_INIT(&tmpq); + PF_UNLNKDRULES_LOCK(); + TAILQ_FOREACH_SAFE(r, &V_pf_unlinked_rules, entries, r1) { + if (!(r->rule_flag & PFRULE_REFS)) { + TAILQ_REMOVE(&V_pf_unlinked_rules, r, entries); + TAILQ_INSERT_TAIL(&tmpq, r, entries); + } else + r->rule_flag &= ~PFRULE_REFS; + } + PF_UNLNKDRULES_UNLOCK(); + + if (!TAILQ_EMPTY(&tmpq)) { + PF_RULES_WLOCK(); + TAILQ_FOREACH_SAFE(r, &tmpq, entries, r1) { + TAILQ_REMOVE(&tmpq, r, entries); + pf_free_rule(r); + } + PF_RULES_WUNLOCK(); + } +} + +void +pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: { + u_int32_t a = ntohl(addr->addr32[0]); + printf("%u.%u.%u.%u", (a>>24)&255, (a>>16)&255, + (a>>8)&255, a&255); + if (p) { + p = ntohs(p); + printf(":%u", p); + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + u_int16_t b; + u_int8_t i, curstart, curend, maxstart, maxend; + curstart = curend = maxstart = maxend = 255; + for (i = 0; i < 8; i++) { + if (!addr->addr16[i]) { + if (curstart == 255) + curstart = i; + curend = i; + } else { + if ((curend - curstart) > + (maxend - maxstart)) { + maxstart = curstart; + maxend = curend; + } + curstart = curend = 255; + } + } + if ((curend - curstart) > + (maxend - maxstart)) { + maxstart = curstart; + maxend = curend; + } + for (i = 0; i < 8; i++) { + if (i >= maxstart && i <= maxend) { + if (i == 0) + printf(":"); + if (i == maxend) + printf(":"); + } else { + b = ntohs(addr->addr16[i]); + printf("%x", b); + if (i < 7) + printf(":"); + } + } + if (p) { + p = ntohs(p); + printf("[%u]", p); + } + break; + } +#endif /* INET6 */ + } +} + +void +pf_print_state(struct pf_state *s) +{ + pf_print_state_parts(s, NULL, NULL); +} + +static void +pf_print_state_parts(struct pf_state *s, + struct pf_state_key *skwp, struct pf_state_key *sksp) +{ + struct pf_state_key *skw, *sks; + u_int8_t proto, dir; + + /* Do our best to fill these, but they're skipped if NULL */ + skw = skwp ? skwp : (s ? s->key[PF_SK_WIRE] : NULL); + sks = sksp ? sksp : (s ? s->key[PF_SK_STACK] : NULL); + proto = skw ? skw->proto : (sks ? sks->proto : 0); + dir = s ? s->direction : 0; + + switch (proto) { + case IPPROTO_IPV4: + printf("IPv4"); + break; + case IPPROTO_IPV6: + printf("IPv6"); + break; + case IPPROTO_TCP: + printf("TCP"); + break; + case IPPROTO_UDP: + printf("UDP"); + break; + case IPPROTO_ICMP: + printf("ICMP"); + break; + case IPPROTO_ICMPV6: + printf("ICMPv6"); + break; + default: + printf("%u", skw->proto); + break; + } + switch (dir) { + case PF_IN: + printf(" in"); + break; + case PF_OUT: + printf(" out"); + break; + } + if (skw) { + printf(" wire: "); + pf_print_host(&skw->addr[0], skw->port[0], skw->af); + printf(" "); + pf_print_host(&skw->addr[1], skw->port[1], skw->af); + } + if (sks) { + printf(" stack: "); + if (sks != skw) { + pf_print_host(&sks->addr[0], sks->port[0], sks->af); + printf(" "); + pf_print_host(&sks->addr[1], sks->port[1], sks->af); + } else + printf("-"); + } + if (s) { + if (proto == IPPROTO_TCP) { + printf(" [lo=%u high=%u win=%u modulator=%u", + s->src.seqlo, s->src.seqhi, + s->src.max_win, s->src.seqdiff); + if (s->src.wscale && s->dst.wscale) + printf(" wscale=%u", + s->src.wscale & PF_WSCALE_MASK); + printf("]"); + printf(" [lo=%u high=%u win=%u modulator=%u", + s->dst.seqlo, s->dst.seqhi, + s->dst.max_win, s->dst.seqdiff); + if (s->src.wscale && s->dst.wscale) + printf(" wscale=%u", + s->dst.wscale & PF_WSCALE_MASK); + printf("]"); + } + printf(" %u:%u", s->src.state, s->dst.state); + } +} + +void +pf_print_flags(u_int8_t f) +{ + if (f) + printf(" "); + if (f & TH_FIN) + printf("F"); + if (f & TH_SYN) + printf("S"); + if (f & TH_RST) + printf("R"); + if (f & TH_PUSH) + printf("P"); + if (f & TH_ACK) + printf("A"); + if (f & TH_URG) + printf("U"); + if (f & TH_ECE) + printf("E"); + if (f & TH_CWR) + printf("W"); +} + +#define PF_SET_SKIP_STEPS(i) \ + do { \ + while (head[i] != cur) { \ + head[i]->skip[i].ptr = cur; \ + head[i] = TAILQ_NEXT(head[i], entries); \ + } \ + } while (0) + +void +pf_calc_skip_steps(struct pf_rulequeue *rules) +{ + struct pf_rule *cur, *prev, *head[PF_SKIP_COUNT]; + int i; + + cur = TAILQ_FIRST(rules); + prev = cur; + for (i = 0; i < PF_SKIP_COUNT; ++i) + head[i] = cur; + while (cur != NULL) { + + if (cur->kif != prev->kif || cur->ifnot != prev->ifnot) + PF_SET_SKIP_STEPS(PF_SKIP_IFP); + if (cur->direction != prev->direction) + PF_SET_SKIP_STEPS(PF_SKIP_DIR); + if (cur->af != prev->af) + PF_SET_SKIP_STEPS(PF_SKIP_AF); + if (cur->proto != prev->proto) + PF_SET_SKIP_STEPS(PF_SKIP_PROTO); + if (cur->src.neg != prev->src.neg || + pf_addr_wrap_neq(&cur->src.addr, &prev->src.addr)) + PF_SET_SKIP_STEPS(PF_SKIP_SRC_ADDR); + if (cur->src.port[0] != prev->src.port[0] || + cur->src.port[1] != prev->src.port[1] || + cur->src.port_op != prev->src.port_op) + PF_SET_SKIP_STEPS(PF_SKIP_SRC_PORT); + if (cur->dst.neg != prev->dst.neg || + pf_addr_wrap_neq(&cur->dst.addr, &prev->dst.addr)) + PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR); + if (cur->dst.port[0] != prev->dst.port[0] || + cur->dst.port[1] != prev->dst.port[1] || + cur->dst.port_op != prev->dst.port_op) + PF_SET_SKIP_STEPS(PF_SKIP_DST_PORT); + + prev = cur; + cur = TAILQ_NEXT(cur, entries); + } + for (i = 0; i < PF_SKIP_COUNT; ++i) + PF_SET_SKIP_STEPS(i); +} + +static int +pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) +{ + if (aw1->type != aw2->type) + return (1); + switch (aw1->type) { + case PF_ADDR_ADDRMASK: + case PF_ADDR_RANGE: + if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, 0)) + return (1); + if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0)) + return (1); + return (0); + case PF_ADDR_DYNIFTL: + return (aw1->p.dyn->pfid_kt != aw2->p.dyn->pfid_kt); + case PF_ADDR_NOROUTE: + case PF_ADDR_URPFFAILED: + return (0); + case PF_ADDR_TABLE: + return (aw1->p.tbl != aw2->p.tbl); + default: + printf("invalid address type: %d\n", aw1->type); + return (1); + } +} + +u_int16_t +pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) +{ + u_int32_t l; + + if (udp && !cksum) + return (0x0000); + l = cksum + old - new; + l = (l >> 16) + (l & 65535); + l = l & 65535; + if (udp && !l) + return (0xFFFF); + return (l); +} + +static void +pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, + struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af) +{ + struct pf_addr ao; + u_int16_t po = *p; + + PF_ACPY(&ao, a, af); + PF_ACPY(a, an, af); + + *p = pn; + + switch (af) { +#ifdef INET + case AF_INET: + *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, + ao.addr16[0], an->addr16[0], 0), + ao.addr16[1], an->addr16[1], 0); + *p = pn; + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + po, pn, u); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + ao.addr16[2], an->addr16[2], u), + ao.addr16[3], an->addr16[3], u), + ao.addr16[4], an->addr16[4], u), + ao.addr16[5], an->addr16[5], u), + ao.addr16[6], an->addr16[6], u), + ao.addr16[7], an->addr16[7], u), + po, pn, u); + break; +#endif /* INET6 */ + } +} + + +/* Changes a u_int32_t. Uses a void * so there are no align restrictions */ +void +pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) +{ + u_int32_t ao; + + memcpy(&ao, a, sizeof(ao)); + memcpy(a, &an, sizeof(u_int32_t)); + *c = pf_cksum_fixup(pf_cksum_fixup(*c, ao / 65536, an / 65536, u), + ao % 65536, an % 65536, u); +} + +#ifdef INET6 +static void +pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) +{ + struct pf_addr ao; + + PF_ACPY(&ao, a, AF_INET6); + PF_ACPY(a, an, AF_INET6); + + *c = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*c, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + ao.addr16[2], an->addr16[2], u), + ao.addr16[3], an->addr16[3], u), + ao.addr16[4], an->addr16[4], u), + ao.addr16[5], an->addr16[5], u), + ao.addr16[6], an->addr16[6], u), + ao.addr16[7], an->addr16[7], u); +} +#endif /* INET6 */ + +static void +pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, + struct pf_addr *na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c, + u_int16_t *ic, u_int16_t *hc, u_int8_t u, sa_family_t af) +{ + struct pf_addr oia, ooa; + + PF_ACPY(&oia, ia, af); + if (oa) + PF_ACPY(&ooa, oa, af); + + /* Change inner protocol port, fix inner protocol checksum. */ + if (ip != NULL) { + u_int16_t oip = *ip; + u_int32_t opc; + + if (pc != NULL) + opc = *pc; + *ip = np; + if (pc != NULL) + *pc = pf_cksum_fixup(*pc, oip, *ip, u); + *ic = pf_cksum_fixup(*ic, oip, *ip, 0); + if (pc != NULL) + *ic = pf_cksum_fixup(*ic, opc, *pc, 0); + } + /* Change inner ip address, fix inner ip and icmp checksums. */ + PF_ACPY(ia, na, af); + switch (af) { +#ifdef INET + case AF_INET: { + u_int32_t oh2c = *h2c; + + *h2c = pf_cksum_fixup(pf_cksum_fixup(*h2c, + oia.addr16[0], ia->addr16[0], 0), + oia.addr16[1], ia->addr16[1], 0); + *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, + oia.addr16[0], ia->addr16[0], 0), + oia.addr16[1], ia->addr16[1], 0); + *ic = pf_cksum_fixup(*ic, oh2c, *h2c, 0); + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*ic, + oia.addr16[0], ia->addr16[0], u), + oia.addr16[1], ia->addr16[1], u), + oia.addr16[2], ia->addr16[2], u), + oia.addr16[3], ia->addr16[3], u), + oia.addr16[4], ia->addr16[4], u), + oia.addr16[5], ia->addr16[5], u), + oia.addr16[6], ia->addr16[6], u), + oia.addr16[7], ia->addr16[7], u); + break; +#endif /* INET6 */ + } + /* Outer ip address, fix outer ip or icmpv6 checksum, if necessary. */ + if (oa) { + PF_ACPY(oa, na, af); + switch (af) { +#ifdef INET + case AF_INET: + *hc = pf_cksum_fixup(pf_cksum_fixup(*hc, + ooa.addr16[0], oa->addr16[0], 0), + ooa.addr16[1], oa->addr16[1], 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*ic, + ooa.addr16[0], oa->addr16[0], u), + ooa.addr16[1], oa->addr16[1], u), + ooa.addr16[2], oa->addr16[2], u), + ooa.addr16[3], oa->addr16[3], u), + ooa.addr16[4], oa->addr16[4], u), + ooa.addr16[5], oa->addr16[5], u), + ooa.addr16[6], oa->addr16[6], u), + ooa.addr16[7], oa->addr16[7], u); + break; +#endif /* INET6 */ + } + } +} + + +/* + * Need to modulate the sequence numbers in the TCP SACK option + * (credits to Krzysztof Pfaff for report and patch) + */ +static int +pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, + struct tcphdr *th, struct pf_state_peer *dst) +{ + int hlen = (th->th_off << 2) - sizeof(*th), thoptlen = hlen; + u_int8_t opts[TCP_MAXOLEN], *opt = opts; + int copyback = 0, i, olen; + struct sackblk sack; + +#define TCPOLEN_SACKLEN (TCPOLEN_SACK + 2) + if (hlen < TCPOLEN_SACKLEN || + !pf_pull_hdr(m, off + sizeof(*th), opts, hlen, NULL, NULL, pd->af)) + return 0; + + while (hlen >= TCPOLEN_SACKLEN) { + olen = opt[1]; + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUGH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_SACK: + if (olen > hlen) + olen = hlen; + if (olen >= TCPOLEN_SACKLEN) { + for (i = 2; i + TCPOLEN_SACK <= olen; + i += TCPOLEN_SACK) { + memcpy(&sack, &opt[i], sizeof(sack)); + pf_change_a(&sack.start, &th->th_sum, + htonl(ntohl(sack.start) - + dst->seqdiff), 0); + pf_change_a(&sack.end, &th->th_sum, + htonl(ntohl(sack.end) - + dst->seqdiff), 0); + memcpy(&opt[i], &sack, sizeof(sack)); + } + copyback = 1; + } + /* FALLTHROUGH */ + default: + if (olen < 2) + olen = 2; + hlen -= olen; + opt += olen; + } + } + + if (copyback) + m_copyback(m, off + sizeof(*th), thoptlen, (caddr_t)opts); + return (copyback); +} + +static void +pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af, + const struct pf_addr *saddr, const struct pf_addr *daddr, + u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, + u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag, + u_int16_t rtag, struct ifnet *ifp) +{ + struct pf_send_entry *pfse; + struct mbuf *m; + int len, tlen; +#ifdef INET + struct ip *h = NULL; +#endif /* INET */ +#ifdef INET6 + struct ip6_hdr *h6 = NULL; +#endif /* INET6 */ + struct tcphdr *th; + char *opt; + struct pf_mtag *pf_mtag; + + len = 0; + th = NULL; + + /* maximum segment size tcp option */ + tlen = sizeof(struct tcphdr); + if (mss) + tlen += 4; + + switch (af) { +#ifdef INET + case AF_INET: + len = sizeof(struct ip) + tlen; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + len = sizeof(struct ip6_hdr) + tlen; + break; +#endif /* INET6 */ + default: + panic("%s: unsupported af %d", __func__, af); + } + + /* Allocate outgoing queue entry, mbuf and mbuf tag. */ + pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); + if (pfse == NULL) + return; + m = m_gethdr(M_NOWAIT, MT_HEADER); + if (m == NULL) { + free(pfse, M_PFTEMP); + return; + } +#ifdef MAC + mac_netinet_firewall_send(m); +#endif + if ((pf_mtag = pf_get_mtag(m)) == NULL) { + free(pfse, M_PFTEMP); + m_freem(m); + return; + } + if (tag) + m->m_flags |= M_SKIP_FIREWALL; + pf_mtag->tag = rtag; + + if (r != NULL && r->rtableid >= 0) + M_SETFIB(m, r->rtableid); + +#ifdef ALTQ + if (r != NULL && r->qid) { + pf_mtag->qid = r->qid; + + /* add hints for ecn */ + pf_mtag->hdr = mtod(m, struct ip *); + } +#endif /* ALTQ */ + m->m_data += max_linkhdr; + m->m_pkthdr.len = m->m_len = len; + m->m_pkthdr.rcvif = NULL; + bzero(m->m_data, len); + switch (af) { +#ifdef INET + case AF_INET: + h = mtod(m, struct ip *); + + /* IP header fields included in the TCP checksum */ + h->ip_p = IPPROTO_TCP; + h->ip_len = htons(tlen); + h->ip_src.s_addr = saddr->v4.s_addr; + h->ip_dst.s_addr = daddr->v4.s_addr; + + th = (struct tcphdr *)((caddr_t)h + sizeof(struct ip)); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + h6 = mtod(m, struct ip6_hdr *); + + /* IP header fields included in the TCP checksum */ + h6->ip6_nxt = IPPROTO_TCP; + h6->ip6_plen = htons(tlen); + memcpy(&h6->ip6_src, &saddr->v6, sizeof(struct in6_addr)); + memcpy(&h6->ip6_dst, &daddr->v6, sizeof(struct in6_addr)); + + th = (struct tcphdr *)((caddr_t)h6 + sizeof(struct ip6_hdr)); + break; +#endif /* INET6 */ + } + + /* TCP header */ + th->th_sport = sport; + th->th_dport = dport; + th->th_seq = htonl(seq); + th->th_ack = htonl(ack); + th->th_off = tlen >> 2; + th->th_flags = flags; + th->th_win = htons(win); + + if (mss) { + opt = (char *)(th + 1); + opt[0] = TCPOPT_MAXSEG; + opt[1] = 4; + HTONS(mss); + bcopy((caddr_t)&mss, (caddr_t)(opt + 2), 2); + } + + switch (af) { +#ifdef INET + case AF_INET: + /* TCP checksum */ + th->th_sum = in_cksum(m, len); + + /* Finish the IP header */ + h->ip_v = 4; + h->ip_hl = sizeof(*h) >> 2; + h->ip_tos = IPTOS_LOWDELAY; + h->ip_off = V_path_mtu_discovery ? IP_DF : 0; + h->ip_len = len; + h->ip_ttl = ttl ? ttl : V_ip_defttl; + h->ip_sum = 0; + + pfse->pfse_type = PFSE_IP; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + /* TCP checksum */ + th->th_sum = in6_cksum(m, IPPROTO_TCP, + sizeof(struct ip6_hdr), tlen); + + h6->ip6_vfc |= IPV6_VERSION; + h6->ip6_hlim = IPV6_DEFHLIM; + + pfse->pfse_type = PFSE_IP6; + break; +#endif /* INET6 */ + } + pfse->pfse_m = m; + pf_send(pfse); +} + +static void +pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, + struct pf_rule *r) +{ + struct pf_send_entry *pfse; + struct mbuf *m0; + struct pf_mtag *pf_mtag; + + /* Allocate outgoing queue entry, mbuf and mbuf tag. */ + pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); + if (pfse == NULL) + return; + + if ((m0 = m_copypacket(m, M_NOWAIT)) == NULL) { + free(pfse, M_PFTEMP); + return; + } + + if ((pf_mtag = pf_get_mtag(m0)) == NULL) { + free(pfse, M_PFTEMP); + return; + } + /* XXX: revisit */ + m0->m_flags |= M_SKIP_FIREWALL; + + if (r->rtableid >= 0) + M_SETFIB(m0, r->rtableid); + +#ifdef ALTQ + if (r->qid) { + pf_mtag->qid = r->qid; + /* add hints for ecn */ + pf_mtag->hdr = mtod(m0, struct ip *); + } +#endif /* ALTQ */ + + switch (af) { +#ifdef INET + case AF_INET: + { + struct ip *ip; + + /* icmp_error() expects host byte ordering */ + ip = mtod(m0, struct ip *); + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + + pfse->pfse_type = PFSE_ICMP; + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + pfse->pfse_type = PFSE_ICMP6; + break; +#endif /* INET6 */ + } + pfse->pfse_m = m0; + pfse->pfse_icmp_type = type; + pfse->pfse_icmp_code = code; + pf_send(pfse); +} + +/* + * Return 1 if the addresses a and b match (with mask m), otherwise return 0. + * If n is 0, they match if they are equal. If n is != 0, they match if they + * are different. + */ +int +pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m, + struct pf_addr *b, sa_family_t af) +{ + int match = 0; + + switch (af) { +#ifdef INET + case AF_INET: + if ((a->addr32[0] & m->addr32[0]) == + (b->addr32[0] & m->addr32[0])) + match++; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (((a->addr32[0] & m->addr32[0]) == + (b->addr32[0] & m->addr32[0])) && + ((a->addr32[1] & m->addr32[1]) == + (b->addr32[1] & m->addr32[1])) && + ((a->addr32[2] & m->addr32[2]) == + (b->addr32[2] & m->addr32[2])) && + ((a->addr32[3] & m->addr32[3]) == + (b->addr32[3] & m->addr32[3]))) + match++; + break; +#endif /* INET6 */ + } + if (match) { + if (n) + return (0); + else + return (1); + } else { + if (n) + return (1); + else + return (0); + } +} + +/* + * Return 1 if b <= a <= e, otherwise return 0. + */ +int +pf_match_addr_range(struct pf_addr *b, struct pf_addr *e, + struct pf_addr *a, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + if ((a->addr32[0] < b->addr32[0]) || + (a->addr32[0] > e->addr32[0])) + return (0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + int i; + + /* check a >= b */ + for (i = 0; i < 4; ++i) + if (a->addr32[i] > b->addr32[i]) + break; + else if (a->addr32[i] < b->addr32[i]) + return (0); + /* check a <= e */ + for (i = 0; i < 4; ++i) + if (a->addr32[i] < e->addr32[i]) + break; + else if (a->addr32[i] > e->addr32[i]) + return (0); + break; + } +#endif /* INET6 */ + } + return (1); +} + +static int +pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p) +{ + switch (op) { + case PF_OP_IRG: + return ((p > a1) && (p < a2)); + case PF_OP_XRG: + return ((p < a1) || (p > a2)); + case PF_OP_RRG: + return ((p >= a1) && (p <= a2)); + case PF_OP_EQ: + return (p == a1); + case PF_OP_NE: + return (p != a1); + case PF_OP_LT: + return (p < a1); + case PF_OP_LE: + return (p <= a1); + case PF_OP_GT: + return (p > a1); + case PF_OP_GE: + return (p >= a1); + } + return (0); /* never reached */ +} + +int +pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) +{ + NTOHS(a1); + NTOHS(a2); + NTOHS(p); + return (pf_match(op, a1, a2, p)); +} + +static int +pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) +{ + if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE) + return (0); + return (pf_match(op, a1, a2, u)); +} + +static int +pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) +{ + if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) + return (0); + return (pf_match(op, a1, a2, g)); +} + +int +pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag, int mtag) +{ + if (*tag == -1) + *tag = mtag; + + return ((!r->match_tag_not && r->match_tag == *tag) || + (r->match_tag_not && r->match_tag != *tag)); +} + +int +pf_tag_packet(struct mbuf *m, struct pf_pdesc *pd, int tag) +{ + + KASSERT(tag > 0, ("%s: tag %d", __func__, tag)); + + if (pd->pf_mtag == NULL && ((pd->pf_mtag = pf_get_mtag(m)) == NULL)) + return (ENOMEM); + + pd->pf_mtag->tag = tag; + + return (0); +} + +void +pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n, + struct pf_rule **r, struct pf_rule **a, int *match) +{ + struct pf_anchor_stackframe *f; + + PF_RULES_RASSERT(); + + (*r)->anchor->match = 0; + if (match) + *match = 0; + if (*depth >= sizeof(V_pf_anchor_stack) / + sizeof(V_pf_anchor_stack[0])) { + printf("pf_step_into_anchor: stack overflow\n"); + *r = TAILQ_NEXT(*r, entries); + return; + } else if (*depth == 0 && a != NULL) + *a = *r; + f = V_pf_anchor_stack + (*depth)++; + f->rs = *rs; + f->r = *r; + if ((*r)->anchor_wildcard) { + f->parent = &(*r)->anchor->children; + if ((f->child = RB_MIN(pf_anchor_node, f->parent)) == + NULL) { + *r = NULL; + return; + } + *rs = &f->child->ruleset; + } else { + f->parent = NULL; + f->child = NULL; + *rs = &(*r)->anchor->ruleset; + } + *r = TAILQ_FIRST((*rs)->rules[n].active.ptr); +} + +int +pf_step_out_of_anchor(int *depth, struct pf_ruleset **rs, int n, + struct pf_rule **r, struct pf_rule **a, int *match) +{ + struct pf_anchor_stackframe *f; + int quick = 0; + + PF_RULES_RASSERT(); + + do { + if (*depth <= 0) + break; + f = V_pf_anchor_stack + *depth - 1; + if (f->parent != NULL && f->child != NULL) { + if (f->child->match || + (match != NULL && *match)) { + f->r->anchor->match = 1; + *match = 0; + } + f->child = RB_NEXT(pf_anchor_node, f->parent, f->child); + if (f->child != NULL) { + *rs = &f->child->ruleset; + *r = TAILQ_FIRST((*rs)->rules[n].active.ptr); + if (*r == NULL) + continue; + else + break; + } + } + (*depth)--; + if (*depth == 0 && a != NULL) + *a = NULL; + *rs = f->rs; + if (f->r->anchor->match || (match != NULL && *match)) + quick = f->r->quick; + *r = TAILQ_NEXT(f->r, entries); + } while (*r == NULL); + + return (quick); +} + +#ifdef INET6 +void +pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr, + struct pf_addr *rmask, struct pf_addr *saddr, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | + ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); + break; +#endif /* INET */ + case AF_INET6: + naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | + ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); + naddr->addr32[1] = (raddr->addr32[1] & rmask->addr32[1]) | + ((rmask->addr32[1] ^ 0xffffffff ) & saddr->addr32[1]); + naddr->addr32[2] = (raddr->addr32[2] & rmask->addr32[2]) | + ((rmask->addr32[2] ^ 0xffffffff ) & saddr->addr32[2]); + naddr->addr32[3] = (raddr->addr32[3] & rmask->addr32[3]) | + ((rmask->addr32[3] ^ 0xffffffff ) & saddr->addr32[3]); + break; + } +} + +void +pf_addr_inc(struct pf_addr *addr, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + addr->addr32[0] = htonl(ntohl(addr->addr32[0]) + 1); + break; +#endif /* INET */ + case AF_INET6: + if (addr->addr32[3] == 0xffffffff) { + addr->addr32[3] = 0; + if (addr->addr32[2] == 0xffffffff) { + addr->addr32[2] = 0; + if (addr->addr32[1] == 0xffffffff) { + addr->addr32[1] = 0; + addr->addr32[0] = + htonl(ntohl(addr->addr32[0]) + 1); + } else + addr->addr32[1] = + htonl(ntohl(addr->addr32[1]) + 1); + } else + addr->addr32[2] = + htonl(ntohl(addr->addr32[2]) + 1); + } else + addr->addr32[3] = + htonl(ntohl(addr->addr32[3]) + 1); + break; + } +} +#endif /* INET6 */ + +int +pf_socket_lookup(int direction, struct pf_pdesc *pd, struct mbuf *m) +{ + struct pf_addr *saddr, *daddr; + u_int16_t sport, dport; + struct inpcbinfo *pi; + struct inpcb *inp; + + pd->lookup.uid = UID_MAX; + pd->lookup.gid = GID_MAX; + + switch (pd->proto) { + case IPPROTO_TCP: + if (pd->hdr.tcp == NULL) + return (-1); + sport = pd->hdr.tcp->th_sport; + dport = pd->hdr.tcp->th_dport; + pi = &V_tcbinfo; + break; + case IPPROTO_UDP: + if (pd->hdr.udp == NULL) + return (-1); + sport = pd->hdr.udp->uh_sport; + dport = pd->hdr.udp->uh_dport; + pi = &V_udbinfo; + break; + default: + return (-1); + } + if (direction == PF_IN) { + saddr = pd->src; + daddr = pd->dst; + } else { + u_int16_t p; + + p = sport; + sport = dport; + dport = p; + saddr = pd->dst; + daddr = pd->src; + } + switch (pd->af) { +#ifdef INET + case AF_INET: + inp = in_pcblookup_mbuf(pi, saddr->v4, sport, daddr->v4, + dport, INPLOOKUP_RLOCKPCB, NULL, m); + if (inp == NULL) { + inp = in_pcblookup_mbuf(pi, saddr->v4, sport, + daddr->v4, dport, INPLOOKUP_WILDCARD | + INPLOOKUP_RLOCKPCB, NULL, m); + if (inp == NULL) + return (-1); + } + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + inp = in6_pcblookup_mbuf(pi, &saddr->v6, sport, &daddr->v6, + dport, INPLOOKUP_RLOCKPCB, NULL, m); + if (inp == NULL) { + inp = in6_pcblookup_mbuf(pi, &saddr->v6, sport, + &daddr->v6, dport, INPLOOKUP_WILDCARD | + INPLOOKUP_RLOCKPCB, NULL, m); + if (inp == NULL) + return (-1); + } + break; +#endif /* INET6 */ + + default: + return (-1); + } + INP_RLOCK_ASSERT(inp); + pd->lookup.uid = inp->inp_cred->cr_uid; + pd->lookup.gid = inp->inp_cred->cr_groups[0]; + INP_RUNLOCK(inp); + + return (1); +} + +static u_int8_t +pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) +{ + int hlen; + u_int8_t hdr[60]; + u_int8_t *opt, optlen; + u_int8_t wscale = 0; + + hlen = th_off << 2; /* hlen <= sizeof(hdr) */ + if (hlen <= sizeof(struct tcphdr)) + return (0); + if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) + return (0); + opt = hdr + sizeof(struct tcphdr); + hlen -= sizeof(struct tcphdr); + while (hlen >= 3) { + switch (*opt) { + case TCPOPT_EOL: + case TCPOPT_NOP: + ++opt; + --hlen; + break; + case TCPOPT_WINDOW: + wscale = opt[2]; + if (wscale > TCP_MAX_WINSHIFT) + wscale = TCP_MAX_WINSHIFT; + wscale |= PF_WSCALE_FLAG; + /* FALLTHROUGH */ + default: + optlen = opt[1]; + if (optlen < 2) + optlen = 2; + hlen -= optlen; + opt += optlen; + break; + } + } + return (wscale); +} + +static u_int16_t +pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) +{ + int hlen; + u_int8_t hdr[60]; + u_int8_t *opt, optlen; + u_int16_t mss = V_tcp_mssdflt; + + hlen = th_off << 2; /* hlen <= sizeof(hdr) */ + if (hlen <= sizeof(struct tcphdr)) + return (0); + if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) + return (0); + opt = hdr + sizeof(struct tcphdr); + hlen -= sizeof(struct tcphdr); + while (hlen >= TCPOLEN_MAXSEG) { + switch (*opt) { + case TCPOPT_EOL: + case TCPOPT_NOP: + ++opt; + --hlen; + break; + case TCPOPT_MAXSEG: + bcopy((caddr_t)(opt + 2), (caddr_t)&mss, 2); + NTOHS(mss); + /* FALLTHROUGH */ + default: + optlen = opt[1]; + if (optlen < 2) + optlen = 2; + hlen -= optlen; + opt += optlen; + break; + } + } + return (mss); +} + +static u_int16_t +pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer) +{ +#ifdef INET + struct sockaddr_in *dst; + struct route ro; +#endif /* INET */ +#ifdef INET6 + struct sockaddr_in6 *dst6; + struct route_in6 ro6; +#endif /* INET6 */ + struct rtentry *rt = NULL; + int hlen = 0; + u_int16_t mss = V_tcp_mssdflt; + + switch (af) { +#ifdef INET + case AF_INET: + hlen = sizeof(struct ip); + bzero(&ro, sizeof(ro)); + dst = (struct sockaddr_in *)&ro.ro_dst; + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = addr->v4; + in_rtalloc_ign(&ro, 0, rtableid); + rt = ro.ro_rt; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + hlen = sizeof(struct ip6_hdr); + bzero(&ro6, sizeof(ro6)); + dst6 = (struct sockaddr_in6 *)&ro6.ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(*dst6); + dst6->sin6_addr = addr->v6; + in6_rtalloc_ign(&ro6, 0, rtableid); + rt = ro6.ro_rt; + break; +#endif /* INET6 */ + } + + if (rt && rt->rt_ifp) { + mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); + mss = max(V_tcp_mssdflt, mss); + RTFREE(rt); + } + mss = min(mss, offer); + mss = max(mss, 64); /* sanity - at least max opt space */ + return (mss); +} + +static void +pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) +{ + struct pf_rule *r = s->rule.ptr; + struct pf_src_node *sn = NULL; + + s->rt_kif = NULL; + if (!r->rt || r->rt == PF_FASTROUTE) + return; + switch (s->key[PF_SK_WIRE]->af) { +#ifdef INET + case AF_INET: + pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, &sn); + s->rt_kif = r->rpool.cur->kif; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, &sn); + s->rt_kif = r->rpool.cur->kif; + break; +#endif /* INET6 */ + } +} + +static u_int32_t +pf_tcp_iss(struct pf_pdesc *pd) +{ + MD5_CTX ctx; + u_int32_t digest[4]; + + if (V_pf_tcp_secret_init == 0) { + read_random(&V_pf_tcp_secret, sizeof(V_pf_tcp_secret)); + MD5Init(&V_pf_tcp_secret_ctx); + MD5Update(&V_pf_tcp_secret_ctx, V_pf_tcp_secret, + sizeof(V_pf_tcp_secret)); + V_pf_tcp_secret_init = 1; + } + + ctx = V_pf_tcp_secret_ctx; + + MD5Update(&ctx, (char *)&pd->hdr.tcp->th_sport, sizeof(u_short)); + MD5Update(&ctx, (char *)&pd->hdr.tcp->th_dport, sizeof(u_short)); + if (pd->af == AF_INET6) { + MD5Update(&ctx, (char *)&pd->src->v6, sizeof(struct in6_addr)); + MD5Update(&ctx, (char *)&pd->dst->v6, sizeof(struct in6_addr)); + } else { + MD5Update(&ctx, (char *)&pd->src->v4, sizeof(struct in_addr)); + MD5Update(&ctx, (char *)&pd->dst->v4, sizeof(struct in_addr)); + } + MD5Final((u_char *)digest, &ctx); + V_pf_tcp_iss_off += 4096; +#define ISN_RANDOM_INCREMENT (4096 - 1) + return (digest[0] + (arc4random() & ISN_RANDOM_INCREMENT) + + V_pf_tcp_iss_off); +#undef ISN_RANDOM_INCREMENT +} + +static int +pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, + struct pfi_kif *kif, struct mbuf *m, int off, struct pf_pdesc *pd, + struct pf_rule **am, struct pf_ruleset **rsm, struct inpcb *inp) +{ + struct pf_rule *nr = NULL; + struct pf_addr * const saddr = pd->src; + struct pf_addr * const daddr = pd->dst; + sa_family_t af = pd->af; + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_src_node *nsn = NULL; + struct tcphdr *th = pd->hdr.tcp; + struct pf_state_key *sk = NULL, *nk = NULL; + u_short reason; + int rewrite = 0, hdrlen = 0; + int tag = -1, rtableid = -1; + int asd = 0; + int match = 0; + int state_icmp = 0; + u_int16_t sport = 0, dport = 0; + u_int16_t bproto_sum = 0, bip_sum = 0; + u_int8_t icmptype = 0, icmpcode = 0; + + PF_RULES_RASSERT(); + + if (inp != NULL) { + INP_LOCK_ASSERT(inp); + pd->lookup.uid = inp->inp_cred->cr_uid; + pd->lookup.gid = inp->inp_cred->cr_groups[0]; + pd->lookup.done = 1; + } + + switch (pd->proto) { + case IPPROTO_TCP: + sport = th->th_sport; + dport = th->th_dport; + hdrlen = sizeof(*th); + break; + case IPPROTO_UDP: + sport = pd->hdr.udp->uh_sport; + dport = pd->hdr.udp->uh_dport; + hdrlen = sizeof(*pd->hdr.udp); + break; +#ifdef INET + case IPPROTO_ICMP: + if (pd->af != AF_INET) + break; + sport = dport = pd->hdr.icmp->icmp_id; + hdrlen = sizeof(*pd->hdr.icmp); + icmptype = pd->hdr.icmp->icmp_type; + icmpcode = pd->hdr.icmp->icmp_code; + + if (icmptype == ICMP_UNREACH || + icmptype == ICMP_SOURCEQUENCH || + icmptype == ICMP_REDIRECT || + icmptype == ICMP_TIMXCEED || + icmptype == ICMP_PARAMPROB) + state_icmp++; + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + if (af != AF_INET6) + break; + sport = dport = pd->hdr.icmp6->icmp6_id; + hdrlen = sizeof(*pd->hdr.icmp6); + icmptype = pd->hdr.icmp6->icmp6_type; + icmpcode = pd->hdr.icmp6->icmp6_code; + + if (icmptype == ICMP6_DST_UNREACH || + icmptype == ICMP6_PACKET_TOO_BIG || + icmptype == ICMP6_TIME_EXCEEDED || + icmptype == ICMP6_PARAM_PROB) + state_icmp++; + break; +#endif /* INET6 */ + default: + sport = dport = hdrlen = 0; + break; + } + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + + /* check packet for BINAT/NAT/RDR */ + if ((nr = pf_get_translation(pd, m, off, direction, kif, &nsn, &sk, + &nk, saddr, daddr, sport, dport)) != NULL) { + KASSERT(sk != NULL, ("%s: null sk", __func__)); + KASSERT(nk != NULL, ("%s: null nk", __func__)); + + if (pd->ip_sum) + bip_sum = *pd->ip_sum; + + switch (pd->proto) { + case IPPROTO_TCP: + bproto_sum = th->th_sum; + pd->proto_sum = &th->th_sum; + + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || + nk->port[pd->sidx] != sport) { + pf_change_ap(saddr, &th->th_sport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 0, af); + pd->sport = &th->th_sport; + sport = th->th_sport; + } + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || + nk->port[pd->didx] != dport) { + pf_change_ap(daddr, &th->th_dport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 0, af); + dport = th->th_dport; + pd->dport = &th->th_dport; + } + rewrite++; + break; + case IPPROTO_UDP: + bproto_sum = pd->hdr.udp->uh_sum; + pd->proto_sum = &pd->hdr.udp->uh_sum; + + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || + nk->port[pd->sidx] != sport) { + pf_change_ap(saddr, &pd->hdr.udp->uh_sport, + pd->ip_sum, &pd->hdr.udp->uh_sum, + &nk->addr[pd->sidx], + nk->port[pd->sidx], 1, af); + sport = pd->hdr.udp->uh_sport; + pd->sport = &pd->hdr.udp->uh_sport; + } + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || + nk->port[pd->didx] != dport) { + pf_change_ap(daddr, &pd->hdr.udp->uh_dport, + pd->ip_sum, &pd->hdr.udp->uh_sum, + &nk->addr[pd->didx], + nk->port[pd->didx], 1, af); + dport = pd->hdr.udp->uh_dport; + pd->dport = &pd->hdr.udp->uh_dport; + } + rewrite++; + break; +#ifdef INET + case IPPROTO_ICMP: + nk->port[0] = nk->port[1]; + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET)) + pf_change_a(&saddr->v4.s_addr, pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, 0); + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], AF_INET)) + pf_change_a(&daddr->v4.s_addr, pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, 0); + + if (nk->port[1] != pd->hdr.icmp->icmp_id) { + pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( + pd->hdr.icmp->icmp_cksum, sport, + nk->port[1], 0); + pd->hdr.icmp->icmp_id = nk->port[1]; + pd->sport = &pd->hdr.icmp->icmp_id; + } + m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + nk->port[0] = nk->port[1]; + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET6)) + pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->sidx], 0); + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], AF_INET6)) + pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->didx], 0); + rewrite++; + break; +#endif /* INET */ + default: + switch (af) { +#ifdef INET + case AF_INET: + if (PF_ANEQ(saddr, + &nk->addr[pd->sidx], AF_INET)) + pf_change_a(&saddr->v4.s_addr, + pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, 0); + + if (PF_ANEQ(daddr, + &nk->addr[pd->didx], AF_INET)) + pf_change_a(&daddr->v4.s_addr, + pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (PF_ANEQ(saddr, + &nk->addr[pd->sidx], AF_INET6)) + PF_ACPY(saddr, &nk->addr[pd->sidx], af); + + if (PF_ANEQ(daddr, + &nk->addr[pd->didx], AF_INET6)) + PF_ACPY(saddr, &nk->addr[pd->didx], af); + break; +#endif /* INET */ + } + break; + } + if (nr->natpass) + r = NULL; + pd->nat_rule = nr; + } + + while (r != NULL) { + r->evaluations++; + if (pfi_kif_match(r->kif, kif) == r->ifnot) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, saddr, af, + r->src.neg, kif, M_GETFIB(m))) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + /* tcp/udp only. port_op always 0 in other cases */ + else if (r->src.port_op && !pf_match_port(r->src.port_op, + r->src.port[0], r->src.port[1], sport)) + r = r->skip[PF_SKIP_SRC_PORT].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, + r->dst.neg, NULL, M_GETFIB(m))) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + /* tcp/udp only. port_op always 0 in other cases */ + else if (r->dst.port_op && !pf_match_port(r->dst.port_op, + r->dst.port[0], r->dst.port[1], dport)) + r = r->skip[PF_SKIP_DST_PORT].ptr; + /* icmp only. type always 0 in other cases */ + else if (r->type && r->type != icmptype + 1) + r = TAILQ_NEXT(r, entries); + /* icmp only. type always 0 in other cases */ + else if (r->code && r->code != icmpcode + 1) + r = TAILQ_NEXT(r, entries); + else if (r->tos && !(r->tos == pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->rule_flag & PFRULE_FRAGMENT) + r = TAILQ_NEXT(r, entries); + else if (pd->proto == IPPROTO_TCP && + (r->flagset & th->th_flags) != r->flags) + r = TAILQ_NEXT(r, entries); + /* tcp/udp only. uid.op always 0 in other cases */ + else if (r->uid.op && (pd->lookup.done || (pd->lookup.done = + pf_socket_lookup(direction, pd, m), 1)) && + !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], + pd->lookup.uid)) + r = TAILQ_NEXT(r, entries); + /* tcp/udp only. gid.op always 0 in other cases */ + else if (r->gid.op && (pd->lookup.done || (pd->lookup.done = + pf_socket_lookup(direction, pd, m), 1)) && + !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], + pd->lookup.gid)) + r = TAILQ_NEXT(r, entries); + else if (r->prob && + r->prob <= arc4random()) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) + r = TAILQ_NEXT(r, entries); + else if (r->os_fingerprint != PF_OSFP_ANY && + (pd->proto != IPPROTO_TCP || !pf_osfp_match( + pf_osfp_fingerprint(pd, m, off, th), + r->os_fingerprint))) + r = TAILQ_NEXT(r, entries); + else { + if (r->tag) + tag = r->tag; + if (r->rtableid >= 0) + rtableid = r->rtableid; + if (r->anchor == NULL) { + match = 1; + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + pf_step_into_anchor(&asd, &ruleset, + PF_RULESET_FILTER, &r, &a, &match); + } + if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, + PF_RULESET_FILTER, &r, &a, &match)) + break; + } + r = *rm; + a = *am; + ruleset = *rsm; + + REASON_SET(&reason, PFRES_MATCH); + + if (r->log || (nr != NULL && nr->log)) { + if (rewrite) + m_copyback(m, off, hdrlen, pd->hdr.any); + PFLOG_PACKET(kif, m, af, direction, reason, r->log ? r : nr, a, + ruleset, pd, 1); + } + + if ((r->action == PF_DROP) && + ((r->rule_flag & PFRULE_RETURNRST) || + (r->rule_flag & PFRULE_RETURNICMP) || + (r->rule_flag & PFRULE_RETURN))) { + /* undo NAT changes, if they have taken place */ + if (nr != NULL) { + PF_ACPY(saddr, &sk->addr[pd->sidx], af); + PF_ACPY(daddr, &sk->addr[pd->didx], af); + if (pd->sport) + *pd->sport = sk->port[pd->sidx]; + if (pd->dport) + *pd->dport = sk->port[pd->didx]; + if (pd->proto_sum) + *pd->proto_sum = bproto_sum; + if (pd->ip_sum) + *pd->ip_sum = bip_sum; + m_copyback(m, off, hdrlen, pd->hdr.any); + } + if (pd->proto == IPPROTO_TCP && + ((r->rule_flag & PFRULE_RETURNRST) || + (r->rule_flag & PFRULE_RETURN)) && + !(th->th_flags & TH_RST)) { + u_int32_t ack = ntohl(th->th_seq) + pd->p_len; + int len = 0; +#ifdef INET + struct ip *h4; +#endif +#ifdef INET6 + struct ip6_hdr *h6; +#endif + + switch (af) { +#ifdef INET + case AF_INET: + h4 = mtod(m, struct ip *); + len = ntohs(h4->ip_len) - off; + break; +#endif +#ifdef INET6 + case AF_INET6: + h6 = mtod(m, struct ip6_hdr *); + len = ntohs(h6->ip6_plen) - (off - sizeof(*h6)); + break; +#endif + } + + if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, af)) + REASON_SET(&reason, PFRES_PROTCKSUM); + else { + if (th->th_flags & TH_SYN) + ack++; + if (th->th_flags & TH_FIN) + ack++; + pf_send_tcp(m, r, af, pd->dst, + pd->src, th->th_dport, th->th_sport, + ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, + r->return_ttl, 1, 0, kif->pfik_ifp); + } + } else if (pd->proto != IPPROTO_ICMP && af == AF_INET && + r->return_icmp) + pf_send_icmp(m, r->return_icmp >> 8, + r->return_icmp & 255, af, r); + else if (pd->proto != IPPROTO_ICMPV6 && af == AF_INET6 && + r->return_icmp6) + pf_send_icmp(m, r->return_icmp6 >> 8, + r->return_icmp6 & 255, af, r); + } + + if (r->action == PF_DROP) + goto cleanup; + + if (tag > 0 && pf_tag_packet(m, pd, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + goto cleanup; + } + if (rtableid >= 0) + M_SETFIB(m, rtableid); + + if (!state_icmp && (r->keep_state || nr != NULL || + (pd->flags & PFDESC_TCP_NORM))) { + int action; + action = pf_create_state(r, nr, a, pd, nsn, nk, sk, m, off, + sport, dport, &rewrite, kif, sm, tag, bproto_sum, bip_sum, + hdrlen); + if (action != PF_PASS) + return (action); + } else { + if (sk != NULL) + uma_zfree(V_pf_state_key_z, sk); + if (nk != NULL) + uma_zfree(V_pf_state_key_z, nk); + } + + /* copy back packet headers if we performed NAT operations */ + if (rewrite) + m_copyback(m, off, hdrlen, pd->hdr.any); + + if (*sm != NULL && !((*sm)->state_flags & PFSTATE_NOSYNC) && + direction == PF_OUT && + pfsync_defer_ptr != NULL && pfsync_defer_ptr(*sm, m)) + /* + * We want the state created, but we dont + * want to send this in case a partner + * firewall has to know about it to allow + * replies through it. + */ + return (PF_DEFER); + + return (PF_PASS); + +cleanup: + if (sk != NULL) + uma_zfree(V_pf_state_key_z, sk); + if (nk != NULL) + uma_zfree(V_pf_state_key_z, nk); + return (PF_DROP); +} + +static int +pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, + struct pf_pdesc *pd, struct pf_src_node *nsn, struct pf_state_key *nk, + struct pf_state_key *sk, struct mbuf *m, int off, u_int16_t sport, + u_int16_t dport, int *rewrite, struct pfi_kif *kif, struct pf_state **sm, + int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen) +{ + struct pf_state *s = NULL; + struct pf_src_node *sn = NULL; + struct tcphdr *th = pd->hdr.tcp; + u_int16_t mss = V_tcp_mssdflt; + u_short reason; + + /* check maximums */ + if (r->max_states && (r->states_cur >= r->max_states)) { + V_pf_status.lcounters[LCNT_STATES]++; + REASON_SET(&reason, PFRES_MAXSTATES); + return (PF_DROP); + } + /* src node for filter rule */ + if ((r->rule_flag & PFRULE_SRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&sn, r, pd->src, pd->af) != 0) { + REASON_SET(&reason, PFRES_SRCLIMIT); + goto csfailed; + } + /* src node for translation rule */ + if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&nsn, nr, &sk->addr[pd->sidx], pd->af)) { + REASON_SET(&reason, PFRES_SRCLIMIT); + goto csfailed; + } + s = uma_zalloc(V_pf_state_z, M_NOWAIT | M_ZERO); + if (s == NULL) { + REASON_SET(&reason, PFRES_MEMORY); + goto csfailed; + } + s->rule.ptr = r; + s->nat_rule.ptr = nr; + s->anchor.ptr = a; + STATE_INC_COUNTERS(s); + if (r->allow_opts) + s->state_flags |= PFSTATE_ALLOWOPTS; + if (r->rule_flag & PFRULE_STATESLOPPY) + s->state_flags |= PFSTATE_SLOPPY; + s->log = r->log & PF_LOG_ALL; + s->sync_state = PFSYNC_S_NONE; + if (nr != NULL) + s->log |= nr->log & PF_LOG_ALL; + switch (pd->proto) { + case IPPROTO_TCP: + s->src.seqlo = ntohl(th->th_seq); + s->src.seqhi = s->src.seqlo + pd->p_len + 1; + if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && + r->keep_state == PF_STATE_MODULATE) { + /* Generate sequence number modulator */ + if ((s->src.seqdiff = pf_tcp_iss(pd) - s->src.seqlo) == + 0) + s->src.seqdiff = 1; + pf_change_a(&th->th_seq, &th->th_sum, + htonl(s->src.seqlo + s->src.seqdiff), 0); + *rewrite = 1; + } else + s->src.seqdiff = 0; + if (th->th_flags & TH_SYN) { + s->src.seqhi++; + s->src.wscale = pf_get_wscale(m, off, + th->th_off, pd->af); + } + s->src.max_win = MAX(ntohs(th->th_win), 1); + if (s->src.wscale & PF_WSCALE_MASK) { + /* Remove scale factor from initial window */ + int win = s->src.max_win; + win += 1 << (s->src.wscale & PF_WSCALE_MASK); + s->src.max_win = (win - 1) >> + (s->src.wscale & PF_WSCALE_MASK); + } + if (th->th_flags & TH_FIN) + s->src.seqhi++; + s->dst.seqhi = 1; + s->dst.max_win = 1; + s->src.state = TCPS_SYN_SENT; + s->dst.state = TCPS_CLOSED; + s->timeout = PFTM_TCP_FIRST_PACKET; + break; + case IPPROTO_UDP: + s->src.state = PFUDPS_SINGLE; + s->dst.state = PFUDPS_NO_TRAFFIC; + s->timeout = PFTM_UDP_FIRST_PACKET; + break; + case IPPROTO_ICMP: +#ifdef INET6 + case IPPROTO_ICMPV6: +#endif + s->timeout = PFTM_ICMP_FIRST_PACKET; + break; + default: + s->src.state = PFOTHERS_SINGLE; + s->dst.state = PFOTHERS_NO_TRAFFIC; + s->timeout = PFTM_OTHER_FIRST_PACKET; + } + + s->creation = time_uptime; + s->expire = time_uptime; + + if (sn != NULL) { + s->src_node = sn; + s->src_node->states++; + } + if (nsn != NULL) { + /* XXX We only modify one side for now. */ + PF_ACPY(&nsn->raddr, &nk->addr[1], pd->af); + s->nat_src_node = nsn; + s->nat_src_node->states++; + } + if (pd->proto == IPPROTO_TCP) { + if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, + off, pd, th, &s->src, &s->dst)) { + REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); + STATE_DEC_COUNTERS(s); + uma_zfree(V_pf_state_z, s); + return (PF_DROP); + } + if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub && + pf_normalize_tcp_stateful(m, off, pd, &reason, th, s, + &s->src, &s->dst, rewrite)) { + /* This really shouldn't happen!!! */ + DPFPRINTF(PF_DEBUG_URGENT, + ("pf_normalize_tcp_stateful failed on first pkt")); + pf_normalize_tcp_cleanup(s); + pf_src_tree_remove_state(s); + STATE_DEC_COUNTERS(s); + uma_zfree(V_pf_state_z, s); + return (PF_DROP); + } + } + s->direction = pd->dir; + + /* + * sk/nk could already been setup by pf_get_translation(). + */ + if (nr == NULL) { + KASSERT((sk == NULL && nk == NULL), ("%s: nr %p sk %p, nk %p", + __func__, nr, sk, nk)); + sk = pf_state_key_setup(pd, pd->src, pd->dst, sport, dport); + if (sk == NULL) + goto csfailed; + nk = sk; + } else + KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", + __func__, nr, sk, nk)); + + /* Swap sk/nk for PF_OUT. */ + if (pf_state_insert(BOUND_IFACE(r, kif), + (pd->dir == PF_IN) ? sk : nk, + (pd->dir == PF_IN) ? nk : sk, s)) { + if (pd->proto == IPPROTO_TCP) + pf_normalize_tcp_cleanup(s); + REASON_SET(&reason, PFRES_STATEINS); + pf_src_tree_remove_state(s); + STATE_DEC_COUNTERS(s); + uma_zfree(V_pf_state_z, s); + return (PF_DROP); + } else + *sm = s; + + pf_set_rt_ifp(s, pd->src); /* needs s->state_key set */ + if (tag > 0) + s->tag = tag; + if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) == + TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { + s->src.state = PF_TCPS_PROXY_SRC; + /* undo NAT changes, if they have taken place */ + if (nr != NULL) { + struct pf_state_key *skt = s->key[PF_SK_WIRE]; + if (pd->dir == PF_OUT) + skt = s->key[PF_SK_STACK]; + PF_ACPY(pd->src, &skt->addr[pd->sidx], pd->af); + PF_ACPY(pd->dst, &skt->addr[pd->didx], pd->af); + if (pd->sport) + *pd->sport = skt->port[pd->sidx]; + if (pd->dport) + *pd->dport = skt->port[pd->didx]; + if (pd->proto_sum) + *pd->proto_sum = bproto_sum; + if (pd->ip_sum) + *pd->ip_sum = bip_sum; + m_copyback(m, off, hdrlen, pd->hdr.any); + } + s->src.seqhi = htonl(arc4random()); + /* Find mss option */ + int rtid = M_GETFIB(m); + mss = pf_get_mss(m, off, th->th_off, pd->af); + mss = pf_calc_mss(pd->src, pd->af, rtid, mss); + mss = pf_calc_mss(pd->dst, pd->af, rtid, mss); + s->src.mss = mss; + pf_send_tcp(NULL, r, pd->af, pd->dst, pd->src, th->th_dport, + th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, + TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL); + REASON_SET(&reason, PFRES_SYNPROXY); + return (PF_SYNPROXY_DROP); + } + + return (PF_PASS); + +csfailed: + if (sk != NULL) + uma_zfree(V_pf_state_key_z, sk); + if (nk != NULL) + uma_zfree(V_pf_state_key_z, nk); + + if (sn != NULL && sn->states == 0 && sn->expire == 0) { + pf_remove_src_node(sn); + V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + V_pf_status.src_nodes--; + uma_zfree(V_pf_sources_z, sn); + } + if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { + pf_remove_src_node(nsn); + V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + V_pf_status.src_nodes--; + uma_zfree(V_pf_sources_z, nsn); + } + return (PF_DROP); +} + +static int +pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, + struct mbuf *m, void *h, struct pf_pdesc *pd, struct pf_rule **am, + struct pf_ruleset **rsm) +{ + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + sa_family_t af = pd->af; + u_short reason; + int tag = -1; + int asd = 0; + int match = 0; + + PF_RULES_RASSERT(); + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + while (r != NULL) { + r->evaluations++; + if (pfi_kif_match(r->kif, kif) == r->ifnot) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, + r->src.neg, kif, M_GETFIB(m))) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, + r->dst.neg, NULL, M_GETFIB(m))) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->tos && !(r->tos == pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->os_fingerprint != PF_OSFP_ANY) + r = TAILQ_NEXT(r, entries); + else if (pd->proto == IPPROTO_UDP && + (r->src.port_op || r->dst.port_op)) + r = TAILQ_NEXT(r, entries); + else if (pd->proto == IPPROTO_TCP && + (r->src.port_op || r->dst.port_op || r->flagset)) + r = TAILQ_NEXT(r, entries); + else if ((pd->proto == IPPROTO_ICMP || + pd->proto == IPPROTO_ICMPV6) && + (r->type || r->code)) + r = TAILQ_NEXT(r, entries); + else if (r->prob && r->prob <= + (arc4random() % (UINT_MAX - 1) + 1)) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) + r = TAILQ_NEXT(r, entries); + else { + if (r->anchor == NULL) { + match = 1; + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + pf_step_into_anchor(&asd, &ruleset, + PF_RULESET_FILTER, &r, &a, &match); + } + if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, + PF_RULESET_FILTER, &r, &a, &match)) + break; + } + r = *rm; + a = *am; + ruleset = *rsm; + + REASON_SET(&reason, PFRES_MATCH); + + if (r->log) + PFLOG_PACKET(kif, m, af, direction, reason, r, a, ruleset, pd, + 1); + + if (r->action != PF_PASS) + return (PF_DROP); + + if (tag > 0 && pf_tag_packet(m, pd, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + + return (PF_PASS); +} + +static int +pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, + struct pf_state **state, struct pfi_kif *kif, struct mbuf *m, int off, + struct pf_pdesc *pd, u_short *reason, int *copyback) +{ + struct tcphdr *th = pd->hdr.tcp; + u_int16_t win = ntohs(th->th_win); + u_int32_t ack, end, seq, orig_seq; + u_int8_t sws, dws; + int ackskew; + + if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) { + sws = src->wscale & PF_WSCALE_MASK; + dws = dst->wscale & PF_WSCALE_MASK; + } else + sws = dws = 0; + + /* + * Sequence tracking algorithm from Guido van Rooij's paper: + * http://www.madison-gurkha.com/publications/tcp_filtering/ + * tcp_filtering.ps + */ + + orig_seq = seq = ntohl(th->th_seq); + if (src->seqlo == 0) { + /* First packet from this end. Set its state */ + + if ((pd->flags & PFDESC_TCP_NORM || dst->scrub) && + src->scrub == NULL) { + if (pf_normalize_tcp_init(m, off, pd, th, src, dst)) { + REASON_SET(reason, PFRES_MEMORY); + return (PF_DROP); + } + } + + /* Deferred generation of sequence number modulator */ + if (dst->seqdiff && !src->seqdiff) { + /* use random iss for the TCP server */ + while ((src->seqdiff = arc4random() - seq) == 0) + ; + ack = ntohl(th->th_ack) - dst->seqdiff; + pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + + src->seqdiff), 0); + pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + *copyback = 1; + } else { + ack = ntohl(th->th_ack); + } + + end = seq + pd->p_len; + if (th->th_flags & TH_SYN) { + end++; + if (dst->wscale & PF_WSCALE_FLAG) { + src->wscale = pf_get_wscale(m, off, th->th_off, + pd->af); + if (src->wscale & PF_WSCALE_FLAG) { + /* Remove scale factor from initial + * window */ + sws = src->wscale & PF_WSCALE_MASK; + win = ((u_int32_t)win + (1 << sws) - 1) + >> sws; + dws = dst->wscale & PF_WSCALE_MASK; + } else { + /* fixup other window */ + dst->max_win <<= dst->wscale & + PF_WSCALE_MASK; + /* in case of a retrans SYN|ACK */ + dst->wscale = 0; + } + } + } + if (th->th_flags & TH_FIN) + end++; + + src->seqlo = seq; + if (src->state < TCPS_SYN_SENT) + src->state = TCPS_SYN_SENT; + + /* + * May need to slide the window (seqhi may have been set by + * the crappy stack check or if we picked up the connection + * after establishment) + */ + if (src->seqhi == 1 || + SEQ_GEQ(end + MAX(1, dst->max_win << dws), src->seqhi)) + src->seqhi = end + MAX(1, dst->max_win << dws); + if (win > src->max_win) + src->max_win = win; + + } else { + ack = ntohl(th->th_ack) - dst->seqdiff; + if (src->seqdiff) { + /* Modulate sequence numbers */ + pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + + src->seqdiff), 0); + pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + *copyback = 1; + } + end = seq + pd->p_len; + if (th->th_flags & TH_SYN) + end++; + if (th->th_flags & TH_FIN) + end++; + } + + if ((th->th_flags & TH_ACK) == 0) { + /* Let it pass through the ack skew check */ + ack = dst->seqlo; + } else if ((ack == 0 && + (th->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) || + /* broken tcp stacks do not set ack */ + (dst->state < TCPS_SYN_SENT)) { + /* + * Many stacks (ours included) will set the ACK number in an + * FIN|ACK if the SYN times out -- no sequence to ACK. + */ + ack = dst->seqlo; + } + + if (seq == end) { + /* Ease sequencing restrictions on no data packets */ + seq = src->seqlo; + end = seq; + } + + ackskew = dst->seqlo - ack; + + + /* + * Need to demodulate the sequence numbers in any TCP SACK options + * (Selective ACK). We could optionally validate the SACK values + * against the current ACK window, either forwards or backwards, but + * I'm not confident that SACK has been implemented properly + * everywhere. It wouldn't surprise me if several stacks accidently + * SACK too far backwards of previously ACKed data. There really aren't + * any security implications of bad SACKing unless the target stack + * doesn't validate the option length correctly. Someone trying to + * spoof into a TCP connection won't bother blindly sending SACK + * options anyway. + */ + if (dst->seqdiff && (th->th_off << 2) > sizeof(struct tcphdr)) { + if (pf_modulate_sack(m, off, pd, th, dst)) + *copyback = 1; + } + + +#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */ + if (SEQ_GEQ(src->seqhi, end) && + /* Last octet inside other's window space */ + SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) && + /* Retrans: not more than one window back */ + (ackskew >= -MAXACKWINDOW) && + /* Acking not more than one reassembled fragment backwards */ + (ackskew <= (MAXACKWINDOW << sws)) && + /* Acking not more than one window forward */ + ((th->th_flags & TH_RST) == 0 || orig_seq == src->seqlo || + (orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo) || + (pd->flags & PFDESC_IP_REAS) == 0)) { + /* Require an exact/+1 sequence match on resets when possible */ + + if (dst->scrub || src->scrub) { + if (pf_normalize_tcp_stateful(m, off, pd, reason, th, + *state, src, dst, copyback)) + return (PF_DROP); + } + + /* update max window */ + if (src->max_win < win) + src->max_win = win; + /* synchronize sequencing */ + if (SEQ_GT(end, src->seqlo)) + src->seqlo = end; + /* slide the window of what the other end can send */ + if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) + dst->seqhi = ack + MAX((win << sws), 1); + + + /* update states */ + if (th->th_flags & TH_SYN) + if (src->state < TCPS_SYN_SENT) + src->state = TCPS_SYN_SENT; + if (th->th_flags & TH_FIN) + if (src->state < TCPS_CLOSING) + src->state = TCPS_CLOSING; + if (th->th_flags & TH_ACK) { + if (dst->state == TCPS_SYN_SENT) { + dst->state = TCPS_ESTABLISHED; + if (src->state == TCPS_ESTABLISHED && + (*state)->src_node != NULL && + pf_src_connlimit(state)) { + REASON_SET(reason, PFRES_SRCLIMIT); + return (PF_DROP); + } + } else if (dst->state == TCPS_CLOSING) + dst->state = TCPS_FIN_WAIT_2; + } + if (th->th_flags & TH_RST) + src->state = dst->state = TCPS_TIME_WAIT; + + /* update expire time */ + (*state)->expire = time_uptime; + if (src->state >= TCPS_FIN_WAIT_2 && + dst->state >= TCPS_FIN_WAIT_2) + (*state)->timeout = PFTM_TCP_CLOSED; + else if (src->state >= TCPS_CLOSING && + dst->state >= TCPS_CLOSING) + (*state)->timeout = PFTM_TCP_FIN_WAIT; + else if (src->state < TCPS_ESTABLISHED || + dst->state < TCPS_ESTABLISHED) + (*state)->timeout = PFTM_TCP_OPENING; + else if (src->state >= TCPS_CLOSING || + dst->state >= TCPS_CLOSING) + (*state)->timeout = PFTM_TCP_CLOSING; + else + (*state)->timeout = PFTM_TCP_ESTABLISHED; + + /* Fall through to PASS packet */ + + } else if ((dst->state < TCPS_SYN_SENT || + dst->state >= TCPS_FIN_WAIT_2 || + src->state >= TCPS_FIN_WAIT_2) && + SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) && + /* Within a window forward of the originating packet */ + SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW)) { + /* Within a window backward of the originating packet */ + + /* + * This currently handles three situations: + * 1) Stupid stacks will shotgun SYNs before their peer + * replies. + * 2) When PF catches an already established stream (the + * firewall rebooted, the state table was flushed, routes + * changed...) + * 3) Packets get funky immediately after the connection + * closes (this should catch Solaris spurious ACK|FINs + * that web servers like to spew after a close) + * + * This must be a little more careful than the above code + * since packet floods will also be caught here. We don't + * update the TTL here to mitigate the damage of a packet + * flood and so the same code can handle awkward establishment + * and a loosened connection close. + * In the establishment case, a correct peer response will + * validate the connection, go through the normal state code + * and keep updating the state TTL. + */ + + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: loose state match: "); + pf_print_state(*state); + pf_print_flags(th->th_flags); + printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " + "pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack, + pd->p_len, ackskew, (unsigned long long)(*state)->packets[0], + (unsigned long long)(*state)->packets[1], + pd->dir == PF_IN ? "in" : "out", + pd->dir == (*state)->direction ? "fwd" : "rev"); + } + + if (dst->scrub || src->scrub) { + if (pf_normalize_tcp_stateful(m, off, pd, reason, th, + *state, src, dst, copyback)) + return (PF_DROP); + } + + /* update max window */ + if (src->max_win < win) + src->max_win = win; + /* synchronize sequencing */ + if (SEQ_GT(end, src->seqlo)) + src->seqlo = end; + /* slide the window of what the other end can send */ + if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) + dst->seqhi = ack + MAX((win << sws), 1); + + /* + * Cannot set dst->seqhi here since this could be a shotgunned + * SYN and not an already established connection. + */ + + if (th->th_flags & TH_FIN) + if (src->state < TCPS_CLOSING) + src->state = TCPS_CLOSING; + if (th->th_flags & TH_RST) + src->state = dst->state = TCPS_TIME_WAIT; + + /* Fall through to PASS packet */ + + } else { + if ((*state)->dst.state == TCPS_SYN_SENT && + (*state)->src.state == TCPS_SYN_SENT) { + /* Send RST for state mismatches during handshake */ + if (!(th->th_flags & TH_RST)) + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, + pd->dst, pd->src, th->th_dport, + th->th_sport, ntohl(th->th_ack), 0, + TH_RST, 0, 0, + (*state)->rule.ptr->return_ttl, 1, 0, + kif->pfik_ifp); + src->seqlo = 0; + src->seqhi = 1; + src->max_win = 1; + } else if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: BAD state: "); + pf_print_state(*state); + pf_print_flags(th->th_flags); + printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " + "pkts=%llu:%llu dir=%s,%s\n", + seq, orig_seq, ack, pd->p_len, ackskew, + (unsigned long long)(*state)->packets[0], + (unsigned long long)(*state)->packets[1], + pd->dir == PF_IN ? "in" : "out", + pd->dir == (*state)->direction ? "fwd" : "rev"); + printf("pf: State failure on: %c %c %c %c | %c %c\n", + SEQ_GEQ(src->seqhi, end) ? ' ' : '1', + SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ? + ' ': '2', + (ackskew >= -MAXACKWINDOW) ? ' ' : '3', + (ackskew <= (MAXACKWINDOW << sws)) ? ' ' : '4', + SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) ?' ' :'5', + SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW) ?' ' :'6'); + } + REASON_SET(reason, PFRES_BADSTATE); + return (PF_DROP); + } + + return (PF_PASS); +} + +static int +pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst, + struct pf_state **state, struct pf_pdesc *pd, u_short *reason) +{ + struct tcphdr *th = pd->hdr.tcp; + + if (th->th_flags & TH_SYN) + if (src->state < TCPS_SYN_SENT) + src->state = TCPS_SYN_SENT; + if (th->th_flags & TH_FIN) + if (src->state < TCPS_CLOSING) + src->state = TCPS_CLOSING; + if (th->th_flags & TH_ACK) { + if (dst->state == TCPS_SYN_SENT) { + dst->state = TCPS_ESTABLISHED; + if (src->state == TCPS_ESTABLISHED && + (*state)->src_node != NULL && + pf_src_connlimit(state)) { + REASON_SET(reason, PFRES_SRCLIMIT); + return (PF_DROP); + } + } else if (dst->state == TCPS_CLOSING) { + dst->state = TCPS_FIN_WAIT_2; + } else if (src->state == TCPS_SYN_SENT && + dst->state < TCPS_SYN_SENT) { + /* + * Handle a special sloppy case where we only see one + * half of the connection. If there is a ACK after + * the initial SYN without ever seeing a packet from + * the destination, set the connection to established. + */ + dst->state = src->state = TCPS_ESTABLISHED; + if ((*state)->src_node != NULL && + pf_src_connlimit(state)) { + REASON_SET(reason, PFRES_SRCLIMIT); + return (PF_DROP); + } + } else if (src->state == TCPS_CLOSING && + dst->state == TCPS_ESTABLISHED && + dst->seqlo == 0) { + /* + * Handle the closing of half connections where we + * don't see the full bidirectional FIN/ACK+ACK + * handshake. + */ + dst->state = TCPS_CLOSING; + } + } + if (th->th_flags & TH_RST) + src->state = dst->state = TCPS_TIME_WAIT; + + /* update expire time */ + (*state)->expire = time_uptime; + if (src->state >= TCPS_FIN_WAIT_2 && + dst->state >= TCPS_FIN_WAIT_2) + (*state)->timeout = PFTM_TCP_CLOSED; + else if (src->state >= TCPS_CLOSING && + dst->state >= TCPS_CLOSING) + (*state)->timeout = PFTM_TCP_FIN_WAIT; + else if (src->state < TCPS_ESTABLISHED || + dst->state < TCPS_ESTABLISHED) + (*state)->timeout = PFTM_TCP_OPENING; + else if (src->state >= TCPS_CLOSING || + dst->state >= TCPS_CLOSING) + (*state)->timeout = PFTM_TCP_CLOSING; + else + (*state)->timeout = PFTM_TCP_ESTABLISHED; + + return (PF_PASS); +} + +static int +pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, int off, void *h, struct pf_pdesc *pd, + u_short *reason) +{ + struct pf_state_key_cmp key; + struct tcphdr *th = pd->hdr.tcp; + int copyback = 0; + struct pf_state_peer *src, *dst; + struct pf_state_key *sk; + + bzero(&key, sizeof(key)); + key.af = pd->af; + key.proto = IPPROTO_TCP; + if (direction == PF_IN) { /* wire side, straight */ + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = th->th_sport; + key.port[1] = th->th_dport; + } else { /* stack side, reverse */ + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + key.port[1] = th->th_sport; + key.port[0] = th->th_dport; + } + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + if (direction == (*state)->direction) { + src = &(*state)->src; + dst = &(*state)->dst; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + } + + sk = (*state)->key[pd->didx]; + + if ((*state)->src.state == PF_TCPS_PROXY_SRC) { + if (direction != (*state)->direction) { + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_SYNPROXY_DROP); + } + if (th->th_flags & TH_SYN) { + if (ntohl(th->th_seq) != (*state)->src.seqlo) { + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_DROP); + } + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, + pd->src, th->th_dport, th->th_sport, + (*state)->src.seqhi, ntohl(th->th_seq) + 1, + TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1, 0, NULL); + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_SYNPROXY_DROP); + } else if (!(th->th_flags & TH_ACK) || + (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || + (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) { + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_DROP); + } else if ((*state)->src_node != NULL && + pf_src_connlimit(state)) { + REASON_SET(reason, PFRES_SRCLIMIT); + return (PF_DROP); + } else + (*state)->src.state = PF_TCPS_PROXY_DST; + } + if ((*state)->src.state == PF_TCPS_PROXY_DST) { + if (direction == (*state)->direction) { + if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) || + (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || + (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) { + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_DROP); + } + (*state)->src.max_win = MAX(ntohs(th->th_win), 1); + if ((*state)->dst.seqhi == 1) + (*state)->dst.seqhi = htonl(arc4random()); + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, + &sk->addr[pd->sidx], &sk->addr[pd->didx], + sk->port[pd->sidx], sk->port[pd->didx], + (*state)->dst.seqhi, 0, TH_SYN, 0, + (*state)->src.mss, 0, 0, (*state)->tag, NULL); + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_SYNPROXY_DROP); + } else if (((th->th_flags & (TH_SYN|TH_ACK)) != + (TH_SYN|TH_ACK)) || + (ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) { + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_DROP); + } else { + (*state)->dst.max_win = MAX(ntohs(th->th_win), 1); + (*state)->dst.seqlo = ntohl(th->th_seq); + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, + pd->src, th->th_dport, th->th_sport, + ntohl(th->th_ack), ntohl(th->th_seq) + 1, + TH_ACK, (*state)->src.max_win, 0, 0, 0, + (*state)->tag, NULL); + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, + &sk->addr[pd->sidx], &sk->addr[pd->didx], + sk->port[pd->sidx], sk->port[pd->didx], + (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, + TH_ACK, (*state)->dst.max_win, 0, 0, 1, 0, NULL); + (*state)->src.seqdiff = (*state)->dst.seqhi - + (*state)->src.seqlo; + (*state)->dst.seqdiff = (*state)->src.seqhi - + (*state)->dst.seqlo; + (*state)->src.seqhi = (*state)->src.seqlo + + (*state)->dst.max_win; + (*state)->dst.seqhi = (*state)->dst.seqlo + + (*state)->src.max_win; + (*state)->src.wscale = (*state)->dst.wscale = 0; + (*state)->src.state = (*state)->dst.state = + TCPS_ESTABLISHED; + REASON_SET(reason, PFRES_SYNPROXY); + return (PF_SYNPROXY_DROP); + } + } + + if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) && + dst->state >= TCPS_FIN_WAIT_2 && + src->state >= TCPS_FIN_WAIT_2) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: state reuse "); + pf_print_state(*state); + pf_print_flags(th->th_flags); + printf("\n"); + } + /* XXX make sure it's the same direction ?? */ + (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; + pf_unlink_state(*state, PF_ENTER_LOCKED); + *state = NULL; + return (PF_DROP); + } + + if ((*state)->state_flags & PFSTATE_SLOPPY) { + if (pf_tcp_track_sloppy(src, dst, state, pd, reason) == PF_DROP) + return (PF_DROP); + } else { + if (pf_tcp_track_full(src, dst, state, kif, m, off, pd, reason, + ©back) == PF_DROP) + return (PF_DROP); + } + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || + nk->port[pd->sidx] != th->th_sport) + pf_change_ap(pd->src, &th->th_sport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 0, pd->af); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || + nk->port[pd->didx] != th->th_dport) + pf_change_ap(pd->dst, &th->th_dport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 0, pd->af); + copyback = 1; + } + + /* Copyback sequence modulation or stateful scrub changes if needed */ + if (copyback) + m_copyback(m, off, sizeof(*th), (caddr_t)th); + + return (PF_PASS); +} + +static int +pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, int off, void *h, struct pf_pdesc *pd) +{ + struct pf_state_peer *src, *dst; + struct pf_state_key_cmp key; + struct udphdr *uh = pd->hdr.udp; + + bzero(&key, sizeof(key)); + key.af = pd->af; + key.proto = IPPROTO_UDP; + if (direction == PF_IN) { /* wire side, straight */ + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = uh->uh_sport; + key.port[1] = uh->uh_dport; + } else { /* stack side, reverse */ + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + key.port[1] = uh->uh_sport; + key.port[0] = uh->uh_dport; + } + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + if (direction == (*state)->direction) { + src = &(*state)->src; + dst = &(*state)->dst; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + } + + /* update states */ + if (src->state < PFUDPS_SINGLE) + src->state = PFUDPS_SINGLE; + if (dst->state == PFUDPS_SINGLE) + dst->state = PFUDPS_MULTIPLE; + + /* update expire time */ + (*state)->expire = time_uptime; + if (src->state == PFUDPS_MULTIPLE && dst->state == PFUDPS_MULTIPLE) + (*state)->timeout = PFTM_UDP_MULTIPLE; + else + (*state)->timeout = PFTM_UDP_SINGLE; + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || + nk->port[pd->sidx] != uh->uh_sport) + pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 1, pd->af); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || + nk->port[pd->didx] != uh->uh_dport) + pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 1, pd->af); + m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + } + + return (PF_PASS); +} + +static int +pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) +{ + struct pf_addr *saddr = pd->src, *daddr = pd->dst; + u_int16_t icmpid = 0, *icmpsum; + u_int8_t icmptype; + int state_icmp = 0; + struct pf_state_key_cmp key; + + bzero(&key, sizeof(key)); + switch (pd->proto) { +#ifdef INET + case IPPROTO_ICMP: + icmptype = pd->hdr.icmp->icmp_type; + icmpid = pd->hdr.icmp->icmp_id; + icmpsum = &pd->hdr.icmp->icmp_cksum; + + if (icmptype == ICMP_UNREACH || + icmptype == ICMP_SOURCEQUENCH || + icmptype == ICMP_REDIRECT || + icmptype == ICMP_TIMXCEED || + icmptype == ICMP_PARAMPROB) + state_icmp++; + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + icmptype = pd->hdr.icmp6->icmp6_type; + icmpid = pd->hdr.icmp6->icmp6_id; + icmpsum = &pd->hdr.icmp6->icmp6_cksum; + + if (icmptype == ICMP6_DST_UNREACH || + icmptype == ICMP6_PACKET_TOO_BIG || + icmptype == ICMP6_TIME_EXCEEDED || + icmptype == ICMP6_PARAM_PROB) + state_icmp++; + break; +#endif /* INET6 */ + } + + if (!state_icmp) { + + /* + * ICMP query/reply message not related to a TCP/UDP packet. + * Search for an ICMP state. + */ + key.af = pd->af; + key.proto = pd->proto; + key.port[0] = key.port[1] = icmpid; + if (direction == PF_IN) { /* wire side, straight */ + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + } else { /* stack side, reverse */ + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + } + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + (*state)->expire = time_uptime; + (*state)->timeout = PFTM_ICMP_ERROR_REPLY; + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + switch (pd->af) { +#ifdef INET + case AF_INET: + if (PF_ANEQ(pd->src, + &nk->addr[pd->sidx], AF_INET)) + pf_change_a(&saddr->v4.s_addr, + pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, 0); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], + AF_INET)) + pf_change_a(&daddr->v4.s_addr, + pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, 0); + + if (nk->port[0] != + pd->hdr.icmp->icmp_id) { + pd->hdr.icmp->icmp_cksum = + pf_cksum_fixup( + pd->hdr.icmp->icmp_cksum, icmpid, + nk->port[pd->sidx], 0); + pd->hdr.icmp->icmp_id = + nk->port[pd->sidx]; + } + + m_copyback(m, off, ICMP_MINLEN, + (caddr_t )pd->hdr.icmp); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (PF_ANEQ(pd->src, + &nk->addr[pd->sidx], AF_INET6)) + pf_change_a6(saddr, + &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->sidx], 0); + + if (PF_ANEQ(pd->dst, + &nk->addr[pd->didx], AF_INET6)) + pf_change_a6(daddr, + &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->didx], 0); + + m_copyback(m, off, sizeof(struct icmp6_hdr), + (caddr_t )pd->hdr.icmp6); + break; +#endif /* INET6 */ + } + } + return (PF_PASS); + + } else { + /* + * ICMP error message in response to a TCP/UDP packet. + * Extract the inner TCP/UDP header and search for that state. + */ + + struct pf_pdesc pd2; + bzero(&pd2, sizeof pd2); +#ifdef INET + struct ip h2; +#endif /* INET */ +#ifdef INET6 + struct ip6_hdr h2_6; + int terminal = 0; +#endif /* INET6 */ + int ipoff2 = 0; + int off2 = 0; + + pd2.af = pd->af; + /* Payload packet is from the opposite direction. */ + pd2.sidx = (direction == PF_IN) ? 1 : 0; + pd2.didx = (direction == PF_IN) ? 0 : 1; + switch (pd->af) { +#ifdef INET + case AF_INET: + /* offset of h2 in mbuf chain */ + ipoff2 = off + ICMP_MINLEN; + + if (!pf_pull_hdr(m, ipoff2, &h2, sizeof(h2), + NULL, reason, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(ip)\n")); + return (PF_DROP); + } + /* + * ICMP error messages don't refer to non-first + * fragments + */ + if (h2.ip_off & htons(IP_OFFMASK)) { + REASON_SET(reason, PFRES_FRAG); + return (PF_DROP); + } + + /* offset of protocol header that follows h2 */ + off2 = ipoff2 + (h2.ip_hl << 2); + + pd2.proto = h2.ip_p; + pd2.src = (struct pf_addr *)&h2.ip_src; + pd2.dst = (struct pf_addr *)&h2.ip_dst; + pd2.ip_sum = &h2.ip_sum; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + ipoff2 = off + sizeof(struct icmp6_hdr); + + if (!pf_pull_hdr(m, ipoff2, &h2_6, sizeof(h2_6), + NULL, reason, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(ip6)\n")); + return (PF_DROP); + } + pd2.proto = h2_6.ip6_nxt; + pd2.src = (struct pf_addr *)&h2_6.ip6_src; + pd2.dst = (struct pf_addr *)&h2_6.ip6_dst; + pd2.ip_sum = NULL; + off2 = ipoff2 + sizeof(h2_6); + do { + switch (pd2.proto) { + case IPPROTO_FRAGMENT: + /* + * ICMPv6 error messages for + * non-first fragments + */ + REASON_SET(reason, PFRES_FRAG); + return (PF_DROP); + case IPPROTO_AH: + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: { + /* get next header and header length */ + struct ip6_ext opt6; + + if (!pf_pull_hdr(m, off2, &opt6, + sizeof(opt6), NULL, reason, + pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMPv6 short opt\n")); + return (PF_DROP); + } + if (pd2.proto == IPPROTO_AH) + off2 += (opt6.ip6e_len + 2) * 4; + else + off2 += (opt6.ip6e_len + 1) * 8; + pd2.proto = opt6.ip6e_nxt; + /* goto the next header */ + break; + } + default: + terminal++; + break; + } + } while (!terminal); + break; +#endif /* INET6 */ + } + + switch (pd2.proto) { + case IPPROTO_TCP: { + struct tcphdr th; + u_int32_t seq; + struct pf_state_peer *src, *dst; + u_int8_t dws; + int copyback = 0; + + /* + * Only the first 8 bytes of the TCP header can be + * expected. Don't access any TCP header fields after + * th_seq, an ackskew test is not possible. + */ + if (!pf_pull_hdr(m, off2, &th, 8, NULL, reason, + pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(tcp)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_TCP; + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[pd2.sidx] = th.th_sport; + key.port[pd2.didx] = th.th_dport; + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + if (direction == (*state)->direction) { + src = &(*state)->dst; + dst = &(*state)->src; + } else { + src = &(*state)->src; + dst = &(*state)->dst; + } + + if (src->wscale && dst->wscale) + dws = dst->wscale & PF_WSCALE_MASK; + else + dws = 0; + + /* Demodulate sequence number */ + seq = ntohl(th.th_seq) - src->seqdiff; + if (src->seqdiff) { + pf_change_a(&th.th_seq, icmpsum, + htonl(seq), 0); + copyback = 1; + } + + if (!((*state)->state_flags & PFSTATE_SLOPPY) && + (!SEQ_GEQ(src->seqhi, seq) || + !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)))) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: BAD ICMP %d:%d ", + icmptype, pd->hdr.icmp->icmp_code); + pf_print_host(pd->src, 0, pd->af); + printf(" -> "); + pf_print_host(pd->dst, 0, pd->af); + printf(" state: "); + pf_print_state(*state); + printf(" seq=%u\n", seq); + } + REASON_SET(reason, PFRES_BADSTATE); + return (PF_DROP); + } else { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: OK ICMP %d:%d ", + icmptype, pd->hdr.icmp->icmp_code); + pf_print_host(pd->src, 0, pd->af); + printf(" -> "); + pf_print_host(pd->dst, 0, pd->af); + printf(" state: "); + pf_print_state(*state); + printf(" seq=%u\n", seq); + } + } + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != th.th_sport) + pf_change_icmp(pd2.src, &th.th_sport, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != th.th_dport) + pf_change_icmp(pd2.dst, &th.th_dport, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + copyback = 1; + } + + if (copyback) { + switch (pd2.af) { +#ifdef INET + case AF_INET: + m_copyback(m, off, ICMP_MINLEN, + (caddr_t )pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), + (caddr_t )&h2); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t )pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t )&h2_6); + break; +#endif /* INET6 */ + } + m_copyback(m, off2, 8, (caddr_t)&th); + } + + return (PF_PASS); + break; + } + case IPPROTO_UDP: { + struct udphdr uh; + + if (!pf_pull_hdr(m, off2, &uh, sizeof(uh), + NULL, reason, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(udp)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_UDP; + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[pd2.sidx] = uh.uh_sport; + key.port[pd2.didx] = uh.uh_dport; + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != uh.uh_sport) + pf_change_icmp(pd2.src, &uh.uh_sport, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], &uh.uh_sum, + pd2.ip_sum, icmpsum, + pd->ip_sum, 1, pd2.af); + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != uh.uh_dport) + pf_change_icmp(pd2.dst, &uh.uh_dport, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], &uh.uh_sum, + pd2.ip_sum, icmpsum, + pd->ip_sum, 1, pd2.af); + + switch (pd2.af) { +#ifdef INET + case AF_INET: + m_copyback(m, off, ICMP_MINLEN, + (caddr_t )pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t )pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t )&h2_6); + break; +#endif /* INET6 */ + } + m_copyback(m, off2, sizeof(uh), (caddr_t)&uh); + } + return (PF_PASS); + break; + } +#ifdef INET + case IPPROTO_ICMP: { + struct icmp iih; + + if (!pf_pull_hdr(m, off2, &iih, ICMP_MINLEN, + NULL, reason, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short i" + "(icmp)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_ICMP; + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[0] = key.port[1] = iih.icmp_id; + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != iih.icmp_id) + pf_change_icmp(pd2.src, &iih.icmp_id, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET); + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != iih.icmp_id) + pf_change_icmp(pd2.dst, &iih.icmp_id, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET); + + m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); + m_copyback(m, off2, ICMP_MINLEN, (caddr_t)&iih); + } + return (PF_PASS); + break; + } +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: { + struct icmp6_hdr iih; + + if (!pf_pull_hdr(m, off2, &iih, + sizeof(struct icmp6_hdr), NULL, reason, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(icmp6)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_ICMPV6; + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[0] = key.port[1] = iih.icmp6_id; + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != iih.icmp6_id) + pf_change_icmp(pd2.src, &iih.icmp6_id, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET6); + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != iih.icmp6_id) + pf_change_icmp(pd2.dst, &iih.icmp6_id, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET6); + + m_copyback(m, off, sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); + m_copyback(m, off2, sizeof(struct icmp6_hdr), + (caddr_t)&iih); + } + return (PF_PASS); + break; + } +#endif /* INET6 */ + default: { + key.af = pd2.af; + key.proto = pd2.proto; + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[0] = key.port[1] = 0; + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af)) + pf_change_icmp(pd2.src, NULL, daddr, + &nk->addr[pd2.sidx], 0, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af)) + pf_change_icmp(pd2.src, NULL, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], 0, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + + switch (pd2.af) { +#ifdef INET + case AF_INET: + m_copyback(m, off, ICMP_MINLEN, + (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t )pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t )&h2_6); + break; +#endif /* INET6 */ + } + } + return (PF_PASS); + break; + } + } + } +} + +static int +pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, struct pf_pdesc *pd) +{ + struct pf_state_peer *src, *dst; + struct pf_state_key_cmp key; + + bzero(&key, sizeof(key)); + key.af = pd->af; + key.proto = pd->proto; + if (direction == PF_IN) { + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = key.port[1] = 0; + } else { + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + key.port[1] = key.port[0] = 0; + } + + STATE_LOOKUP(kif, &key, direction, *state, pd); + + if (direction == (*state)->direction) { + src = &(*state)->src; + dst = &(*state)->dst; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + } + + /* update states */ + if (src->state < PFOTHERS_SINGLE) + src->state = PFOTHERS_SINGLE; + if (dst->state == PFOTHERS_SINGLE) + dst->state = PFOTHERS_MULTIPLE; + + /* update expire time */ + (*state)->expire = time_uptime; + if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE) + (*state)->timeout = PFTM_OTHER_MULTIPLE; + else + (*state)->timeout = PFTM_OTHER_SINGLE; + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + KASSERT(nk, ("%s: nk is null", __func__)); + KASSERT(pd, ("%s: pd is null", __func__)); + KASSERT(pd->src, ("%s: pd->src is null", __func__)); + KASSERT(pd->dst, ("%s: pd->dst is null", __func__)); + switch (pd->af) { +#ifdef INET + case AF_INET: + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) + pf_change_a(&pd->src->v4.s_addr, + pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, + 0); + + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) + pf_change_a(&pd->dst->v4.s_addr, + pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, + 0); + + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) + PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) + PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); +#endif /* INET6 */ + } + } + return (PF_PASS); +} + +/* + * ipoff and off are measured from the start of the mbuf chain. + * h must be at "ipoff" on the mbuf chain. + */ +void * +pf_pull_hdr(struct mbuf *m, int off, void *p, int len, + u_short *actionp, u_short *reasonp, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: { + struct ip *h = mtod(m, struct ip *); + u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; + + if (fragoff) { + if (fragoff >= len) + ACTION_SET(actionp, PF_PASS); + else { + ACTION_SET(actionp, PF_DROP); + REASON_SET(reasonp, PFRES_FRAG); + } + return (NULL); + } + if (m->m_pkthdr.len < off + len || + ntohs(h->ip_len) < off + len) { + ACTION_SET(actionp, PF_DROP); + REASON_SET(reasonp, PFRES_SHORT); + return (NULL); + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + + if (m->m_pkthdr.len < off + len || + (ntohs(h->ip6_plen) + sizeof(struct ip6_hdr)) < + (unsigned)(off + len)) { + ACTION_SET(actionp, PF_DROP); + REASON_SET(reasonp, PFRES_SHORT); + return (NULL); + } + break; + } +#endif /* INET6 */ + } + m_copydata(m, off, len, p); + return (p); +} + +int +pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif, + int rtableid) +{ +#ifdef RADIX_MPATH + struct radix_node_head *rnh; +#endif + struct sockaddr_in *dst; + int ret = 1; + int check_mpath; +#ifdef INET6 + struct sockaddr_in6 *dst6; + struct route_in6 ro; +#else + struct route ro; +#endif + struct radix_node *rn; + struct rtentry *rt; + struct ifnet *ifp; + + check_mpath = 0; +#ifdef RADIX_MPATH + /* XXX: stick to table 0 for now */ + rnh = rt_tables_get_rnh(0, af); + if (rnh != NULL && rn_mpath_capable(rnh)) + check_mpath = 1; +#endif + bzero(&ro, sizeof(ro)); + switch (af) { + case AF_INET: + dst = satosin(&ro.ro_dst); + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = addr->v4; + break; +#ifdef INET6 + case AF_INET6: + /* + * Skip check for addresses with embedded interface scope, + * as they would always match anyway. + */ + if (IN6_IS_SCOPE_EMBED(&addr->v6)) + goto out; + dst6 = (struct sockaddr_in6 *)&ro.ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(*dst6); + dst6->sin6_addr = addr->v6; + break; +#endif /* INET6 */ + default: + return (0); + } + + /* Skip checks for ipsec interfaces */ + if (kif != NULL && kif->pfik_ifp->if_type == IFT_ENC) + goto out; + + switch (af) { +#ifdef INET6 + case AF_INET6: + in6_rtalloc_ign(&ro, 0, rtableid); + break; +#endif +#ifdef INET + case AF_INET: + in_rtalloc_ign((struct route *)&ro, 0, rtableid); + break; +#endif + default: + rtalloc_ign((struct route *)&ro, 0); /* No/default FIB. */ + break; + } + + if (ro.ro_rt != NULL) { + /* No interface given, this is a no-route check */ + if (kif == NULL) + goto out; + + if (kif->pfik_ifp == NULL) { + ret = 0; + goto out; + } + + /* Perform uRPF check if passed input interface */ + ret = 0; + rn = (struct radix_node *)ro.ro_rt; + do { + rt = (struct rtentry *)rn; + ifp = rt->rt_ifp; + + if (kif->pfik_ifp == ifp) + ret = 1; +#ifdef RADIX_MPATH + rn = rn_mpath_next(rn); +#endif + } while (check_mpath == 1 && rn != NULL && ret == 0); + } else + ret = 0; +out: + if (ro.ro_rt != NULL) + RTFREE(ro.ro_rt); + return (ret); +} + +#ifdef INET +static void +pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, + struct pf_state *s, struct pf_pdesc *pd) +{ + struct mbuf *m0, *m1; + struct sockaddr_in dst; + struct ip *ip; + struct ifnet *ifp = NULL; + struct pf_addr naddr; + struct pf_src_node *sn = NULL; + int error = 0; + int sw_csum; + + KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); + KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", + __func__)); + + if ((pd->pf_mtag == NULL && + ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || + pd->pf_mtag->routed++ > 3) { + m0 = *m; + *m = NULL; + goto bad_locked; + } + + if (r->rt == PF_DUPTO) { + if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + } else { + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + m0 = *m; + } + + ip = mtod(m0, struct ip *); + + bzero(&dst, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(dst); + dst.sin_addr = ip->ip_dst; + + if (r->rt == PF_FASTROUTE) { + struct rtentry *rt; + + if (s) + PF_STATE_UNLOCK(s); + rt = rtalloc1_fib(sintosa(&dst), 0, 0, M_GETFIB(m0)); + if (rt == NULL) { + RTFREE_LOCKED(rt); + KMOD_IPSTAT_INC(ips_noroute); + error = EHOSTUNREACH; + goto bad; + } + + ifp = rt->rt_ifp; + rt->rt_rmx.rmx_pksent++; + + if (rt->rt_flags & RTF_GATEWAY) + bcopy(satosin(rt->rt_gateway), &dst, sizeof(dst)); + RTFREE_LOCKED(rt); + } else { + if (TAILQ_EMPTY(&r->rpool.list)) { + DPFPRINTF(PF_DEBUG_URGENT, + ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); + goto bad_locked; + } + if (s == NULL) { + pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, + &naddr, NULL, &sn); + if (!PF_AZERO(&naddr, AF_INET)) + dst.sin_addr.s_addr = naddr.v4.s_addr; + ifp = r->rpool.cur->kif ? + r->rpool.cur->kif->pfik_ifp : NULL; + } else { + if (!PF_AZERO(&s->rt_addr, AF_INET)) + dst.sin_addr.s_addr = + s->rt_addr.v4.s_addr; + ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; + PF_STATE_UNLOCK(s); + } + } + if (ifp == NULL) + goto bad; + + if (oifp != ifp) { + if (pf_test(PF_OUT, ifp, &m0, NULL) != PF_PASS) + goto bad; + else if (m0 == NULL) + goto done; + if (m0->m_len < sizeof(struct ip)) { + DPFPRINTF(PF_DEBUG_URGENT, + ("%s: m0->m_len < sizeof(struct ip)\n", __func__)); + goto bad; + } + ip = mtod(m0, struct ip *); + } + + if (ifp->if_flags & IFF_LOOPBACK) + m0->m_flags |= M_SKIP_FIREWALL; + + /* Back to host byte order. */ + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + + /* Copied from FreeBSD 10.0-CURRENT ip_output. */ + m0->m_pkthdr.csum_flags |= CSUM_IP; + sw_csum = m0->m_pkthdr.csum_flags & ~ifp->if_hwassist; + if (sw_csum & CSUM_DELAY_DATA) { + in_delayed_cksum(m0); + sw_csum &= ~CSUM_DELAY_DATA; + } +#ifdef SCTP + if (sw_csum & CSUM_SCTP) { + sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2)); + sw_csum &= ~CSUM_SCTP; + } +#endif + m0->m_pkthdr.csum_flags &= ifp->if_hwassist; + + /* + * If small enough for interface, or the interface will take + * care of the fragmentation for us, we can just send directly. + */ + if (ip->ip_len <= ifp->if_mtu || + (m0->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0 || + ((ip->ip_off & IP_DF) == 0 && (ifp->if_hwassist & CSUM_FRAGMENT))) { + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + ip->ip_sum = 0; + if (sw_csum & CSUM_DELAY_IP) + ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); + m0->m_flags &= ~(M_PROTOFLAGS); + error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); + goto done; + } + + /* Balk when DF bit is set or the interface didn't support TSO. */ + if ((ip->ip_off & IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) { + error = EMSGSIZE; + KMOD_IPSTAT_INC(ips_cantfrag); + if (r->rt != PF_DUPTO) { + icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, + ifp->if_mtu); + goto done; + } else + goto bad; + } + + error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist, sw_csum); + if (error) + goto bad; + + for (; m0; m0 = m1) { + m1 = m0->m_nextpkt; + m0->m_nextpkt = NULL; + if (error == 0) { + m0->m_flags &= ~(M_PROTOFLAGS); + error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); + } else + m_freem(m0); + } + + if (error == 0) + KMOD_IPSTAT_INC(ips_fragmented); + +done: + if (r->rt != PF_DUPTO) + *m = NULL; + return; + +bad_locked: + if (s) + PF_STATE_UNLOCK(s); +bad: + m_freem(m0); + goto done; +} +#endif /* INET */ + +#ifdef INET6 +static void +pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, + struct pf_state *s, struct pf_pdesc *pd) +{ + struct mbuf *m0; + struct sockaddr_in6 dst; + struct ip6_hdr *ip6; + struct ifnet *ifp = NULL; + struct pf_addr naddr; + struct pf_src_node *sn = NULL; + + KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); + KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", + __func__)); + + if ((pd->pf_mtag == NULL && + ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || + pd->pf_mtag->routed++ > 3) { + m0 = *m; + *m = NULL; + goto bad_locked; + } + + if (r->rt == PF_DUPTO) { + if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + } else { + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + m0 = *m; + } + + ip6 = mtod(m0, struct ip6_hdr *); + + bzero(&dst, sizeof(dst)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(dst); + dst.sin6_addr = ip6->ip6_dst; + + /* Cheat. XXX why only in the v6 case??? */ + if (r->rt == PF_FASTROUTE) { + if (s) + PF_STATE_UNLOCK(s); + m0->m_flags |= M_SKIP_FIREWALL; + ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); + return; + } + + if (TAILQ_EMPTY(&r->rpool.list)) { + DPFPRINTF(PF_DEBUG_URGENT, + ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); + goto bad_locked; + } + if (s == NULL) { + pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, + &naddr, NULL, &sn); + if (!PF_AZERO(&naddr, AF_INET6)) + PF_ACPY((struct pf_addr *)&dst.sin6_addr, + &naddr, AF_INET6); + ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; + } else { + if (!PF_AZERO(&s->rt_addr, AF_INET6)) + PF_ACPY((struct pf_addr *)&dst.sin6_addr, + &s->rt_addr, AF_INET6); + ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; + } + + if (s) + PF_STATE_UNLOCK(s); + + if (ifp == NULL) + goto bad; + + if (oifp != ifp) { + if (pf_test6(PF_OUT, ifp, &m0, NULL) != PF_PASS) + goto bad; + else if (m0 == NULL) + goto done; + if (m0->m_len < sizeof(struct ip6_hdr)) { + DPFPRINTF(PF_DEBUG_URGENT, + ("%s: m0->m_len < sizeof(struct ip6_hdr)\n", + __func__)); + goto bad; + } + ip6 = mtod(m0, struct ip6_hdr *); + } + + if (ifp->if_flags & IFF_LOOPBACK) + m0->m_flags |= M_SKIP_FIREWALL; + + /* + * If the packet is too large for the outgoing interface, + * send back an icmp6 error. + */ + if (IN6_IS_SCOPE_EMBED(&dst.sin6_addr)) + dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) + nd6_output(ifp, ifp, m0, &dst, NULL); + else { + in6_ifstat_inc(ifp, ifs6_in_toobig); + if (r->rt != PF_DUPTO) + icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); + else + goto bad; + } + +done: + if (r->rt != PF_DUPTO) + *m = NULL; + return; + +bad_locked: + if (s) + PF_STATE_UNLOCK(s); +bad: + m_freem(m0); + goto done; +} +#endif /* INET6 */ + +/* + * FreeBSD supports cksum offloads for the following drivers. + * em(4), fxp(4), ixgb(4), lge(4), ndis(4), nge(4), re(4), + * ti(4), txp(4), xl(4) + * + * CSUM_DATA_VALID | CSUM_PSEUDO_HDR : + * network driver performed cksum including pseudo header, need to verify + * csum_data + * CSUM_DATA_VALID : + * network driver performed cksum, needs to additional pseudo header + * cksum computation with partial csum_data(i.e. lack of H/W support for + * pseudo header, for instance hme(4), sk(4) and possibly gem(4)) + * + * After validating the cksum of packet, set both flag CSUM_DATA_VALID and + * CSUM_PSEUDO_HDR in order to avoid recomputation of the cksum in upper + * TCP/UDP layer. + * Also, set csum_data to 0xffff to force cksum validation. + */ +static int +pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) +{ + u_int16_t sum = 0; + int hw_assist = 0; + struct ip *ip; + + if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) + return (1); + if (m->m_pkthdr.len < off + len) + return (1); + + switch (p) { + case IPPROTO_TCP: + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { + if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { + sum = m->m_pkthdr.csum_data; + } else { + ip = mtod(m, struct ip *); + sum = in_pseudo(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htonl((u_short)len + + m->m_pkthdr.csum_data + IPPROTO_TCP)); + } + sum ^= 0xffff; + ++hw_assist; + } + break; + case IPPROTO_UDP: + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { + if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { + sum = m->m_pkthdr.csum_data; + } else { + ip = mtod(m, struct ip *); + sum = in_pseudo(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htonl((u_short)len + + m->m_pkthdr.csum_data + IPPROTO_UDP)); + } + sum ^= 0xffff; + ++hw_assist; + } + break; + case IPPROTO_ICMP: +#ifdef INET6 + case IPPROTO_ICMPV6: +#endif /* INET6 */ + break; + default: + return (1); + } + + if (!hw_assist) { + switch (af) { + case AF_INET: + if (p == IPPROTO_ICMP) { + if (m->m_len < off) + return (1); + m->m_data += off; + m->m_len -= off; + sum = in_cksum(m, len); + m->m_data -= off; + m->m_len += off; + } else { + if (m->m_len < sizeof(struct ip)) + return (1); + sum = in4_cksum(m, p, off, len); + } + break; +#ifdef INET6 + case AF_INET6: + if (m->m_len < sizeof(struct ip6_hdr)) + return (1); + sum = in6_cksum(m, p, off, len); + break; +#endif /* INET6 */ + default: + return (1); + } + } + if (sum) { + switch (p) { + case IPPROTO_TCP: + { + KMOD_TCPSTAT_INC(tcps_rcvbadsum); + break; + } + case IPPROTO_UDP: + { + KMOD_UDPSTAT_INC(udps_badsum); + break; + } +#ifdef INET + case IPPROTO_ICMP: + { + KMOD_ICMPSTAT_INC(icps_checksum); + break; + } +#endif +#ifdef INET6 + case IPPROTO_ICMPV6: + { + KMOD_ICMP6STAT_INC(icp6s_checksum); + break; + } +#endif /* INET6 */ + } + return (1); + } else { + if (p == IPPROTO_TCP || p == IPPROTO_UDP) { + m->m_pkthdr.csum_flags |= + (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + m->m_pkthdr.csum_data = 0xffff; + } + } + return (0); +} + + +#ifdef INET +int +pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) +{ + struct pfi_kif *kif; + u_short action, reason = 0, log = 0; + struct mbuf *m = *m0; + struct ip *h = NULL; + struct m_tag *ipfwtag; + struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; + struct pf_state *s = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_pdesc pd; + int off, dirndx, pqid = 0; + + M_ASSERTPKTHDR(m); + + if (!V_pf_status.running) + return (PF_PASS); + + memset(&pd, 0, sizeof(pd)); + + kif = (struct pfi_kif *)ifp->if_pf_kif; + + if (kif == NULL) { + DPFPRINTF(PF_DEBUG_URGENT, + ("pf_test: kif == NULL, if_xname %s\n", ifp->if_xname)); + return (PF_DROP); + } + if (kif->pfik_flags & PFI_IFLAG_SKIP) + return (PF_PASS); + + if (m->m_flags & M_SKIP_FIREWALL) + return (PF_PASS); + + if (m->m_pkthdr.len < (int)sizeof(struct ip)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + + pd.pf_mtag = pf_find_mtag(m); + + PF_RULES_RLOCK(); + + if (ip_divert_ptr != NULL && + ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { + struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); + if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + goto done; + } + pd.pf_mtag->flags |= PF_PACKET_LOOPED; + m_tag_delete(m, ipfwtag); + } + if (pd.pf_mtag && pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { + m->m_flags |= M_FASTFWD_OURS; + pd.pf_mtag->flags &= ~PF_FASTFWD_OURS_PRESENT; + } + } else if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { + /* We do IP header normalization and packet reassembly here */ + action = PF_DROP; + goto done; + } + m = *m0; /* pf_normalize messes with m0 */ + h = mtod(m, struct ip *); + + off = h->ip_hl << 2; + if (off < (int)sizeof(struct ip)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + + pd.src = (struct pf_addr *)&h->ip_src; + pd.dst = (struct pf_addr *)&h->ip_dst; + pd.sport = pd.dport = NULL; + pd.ip_sum = &h->ip_sum; + pd.proto_sum = NULL; + pd.proto = h->ip_p; + pd.dir = dir; + pd.sidx = (dir == PF_IN) ? 0 : 1; + pd.didx = (dir == PF_IN) ? 1 : 0; + pd.af = AF_INET; + pd.tos = h->ip_tos; + pd.tot_len = ntohs(h->ip_len); + + /* handle fragments that didn't get reassembled by normalization */ + if (h->ip_off & htons(IP_MF | IP_OFFMASK)) { + action = pf_test_fragment(&r, dir, kif, m, h, + &pd, &a, &ruleset); + goto done; + } + + switch (h->ip_p) { + + case IPPROTO_TCP: { + struct tcphdr th; + + pd.hdr.tcp = &th; + if (!pf_pull_hdr(m, off, &th, sizeof(th), + &action, &reason, AF_INET)) { + log = action != PF_PASS; + goto done; + } + pd.p_len = pd.tot_len - off - (th.th_off << 2); + if ((th.th_flags & TH_ACK) && pd.p_len == 0) + pqid = 1; + action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); + if (action == PF_DROP) + goto done; + action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, + &reason); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + + case IPPROTO_UDP: { + struct udphdr uh; + + pd.hdr.udp = &uh; + if (!pf_pull_hdr(m, off, &uh, sizeof(uh), + &action, &reason, AF_INET)) { + log = action != PF_PASS; + goto done; + } + if (uh.uh_dport == 0 || + ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || + ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + goto done; + } + action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + + case IPPROTO_ICMP: { + struct icmp ih; + + pd.hdr.icmp = &ih; + if (!pf_pull_hdr(m, off, &ih, ICMP_MINLEN, + &action, &reason, AF_INET)) { + log = action != PF_PASS; + goto done; + } + action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, + &reason); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + +#ifdef INET6 + case IPPROTO_ICMPV6: { + action = PF_DROP; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping IPv4 packet with ICMPv6 payload\n")); + goto done; + } +#endif + + default: + action = pf_test_state_other(&s, dir, kif, m, &pd); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + +done: + PF_RULES_RUNLOCK(); + if (action == PF_PASS && h->ip_hl > 5 && + !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_IPOPTIONS); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping packet with ip options\n")); + } + + if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (r->rtableid >= 0) + M_SETFIB(m, r->rtableid); + +#ifdef ALTQ + if (action == PF_PASS && r->qid) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (pqid || (pd.tos & IPTOS_LOWDELAY)) + pd.pf_mtag->qid = r->pqid; + else + pd.pf_mtag->qid = r->qid; + /* add hints for ecn */ + pd.pf_mtag->hdr = h; + + } +#endif /* ALTQ */ + + /* + * connections redirected to loopback should not match sockets + * bound specifically to loopback due to security implications, + * see tcp_input() and in_pcblookup_listen(). + */ + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || + pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && + (s->nat_rule.ptr->action == PF_RDR || + s->nat_rule.ptr->action == PF_BINAT) && + (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) + m->m_flags |= M_SKIP_FIREWALL; + + if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && + !PACKET_LOOPED(&pd)) { + + ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); + if (ipfwtag != NULL) { + ((struct ipfw_rule_ref *)(ipfwtag+1))->info = + ntohs(r->divert.port); + ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; + + if (s) + PF_STATE_UNLOCK(s); + + m_tag_prepend(m, ipfwtag); + if (m->m_flags & M_FASTFWD_OURS) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate tag\n")); + } + pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; + m->m_flags &= ~M_FASTFWD_OURS; + } + ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); + *m0 = NULL; + + return (action); + } else { + /* XXX: ipfw has the same behaviour! */ + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate divert tag\n")); + } + } + + if (log) { + struct pf_rule *lr; + + if (s != NULL && s->nat_rule.ptr != NULL && + s->nat_rule.ptr->log & PF_LOG_ALL) + lr = s->nat_rule.ptr; + else + lr = r; + PFLOG_PACKET(kif, m, AF_INET, dir, reason, lr, a, ruleset, &pd, + (s == NULL)); + } + + kif->pfik_bytes[0][dir == PF_OUT][action != PF_PASS] += pd.tot_len; + kif->pfik_packets[0][dir == PF_OUT][action != PF_PASS]++; + + if (action == PF_PASS || r->action == PF_DROP) { + dirndx = (dir == PF_OUT); + r->packets[dirndx]++; + r->bytes[dirndx] += pd.tot_len; + if (a != NULL) { + a->packets[dirndx]++; + a->bytes[dirndx] += pd.tot_len; + } + if (s != NULL) { + if (s->nat_rule.ptr != NULL) { + s->nat_rule.ptr->packets[dirndx]++; + s->nat_rule.ptr->bytes[dirndx] += pd.tot_len; + } + if (s->src_node != NULL) { + s->src_node->packets[dirndx]++; + s->src_node->bytes[dirndx] += pd.tot_len; + } + if (s->nat_src_node != NULL) { + s->nat_src_node->packets[dirndx]++; + s->nat_src_node->bytes[dirndx] += pd.tot_len; + } + dirndx = (dir == s->direction) ? 0 : 1; + s->packets[dirndx]++; + s->bytes[dirndx] += pd.tot_len; + } + tr = r; + nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; + if (nr != NULL && r == &V_pf_default_rule) + tr = nr; + if (tr->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->src.addr.p.tbl, + (s == NULL) ? pd.src : + &s->key[(s->direction == PF_IN)]-> + addr[(s->direction == PF_OUT)], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->src.neg); + if (tr->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->dst.addr.p.tbl, + (s == NULL) ? pd.dst : + &s->key[(s->direction == PF_IN)]-> + addr[(s->direction == PF_IN)], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->dst.neg); + } + + switch (action) { + case PF_SYNPROXY_DROP: + m_freem(*m0); + case PF_DEFER: + *m0 = NULL; + action = PF_PASS; + break; + default: + /* pf_route() returns unlocked. */ + if (r->rt) { + pf_route(m0, r, dir, kif->pfik_ifp, s, &pd); + return (action); + } + break; + } + if (s) + PF_STATE_UNLOCK(s); + + return (action); +} +#endif /* INET */ + +#ifdef INET6 +int +pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) +{ + struct pfi_kif *kif; + u_short action, reason = 0, log = 0; + struct mbuf *m = *m0, *n = NULL; + struct ip6_hdr *h = NULL; + struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; + struct pf_state *s = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_pdesc pd; + int off, terminal = 0, dirndx, rh_cnt = 0; + + M_ASSERTPKTHDR(m); + + if (!V_pf_status.running) + return (PF_PASS); + + memset(&pd, 0, sizeof(pd)); + pd.pf_mtag = pf_find_mtag(m); + + if (pd.pf_mtag && pd.pf_mtag->flags & PF_TAG_GENERATED) + return (PF_PASS); + + kif = (struct pfi_kif *)ifp->if_pf_kif; + if (kif == NULL) { + DPFPRINTF(PF_DEBUG_URGENT, + ("pf_test6: kif == NULL, if_xname %s\n", ifp->if_xname)); + return (PF_DROP); + } + if (kif->pfik_flags & PFI_IFLAG_SKIP) + return (PF_PASS); + + if (m->m_pkthdr.len < (int)sizeof(*h)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + + PF_RULES_RLOCK(); + + /* We do IP header normalization and packet reassembly here */ + if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { + action = PF_DROP; + goto done; + } + m = *m0; /* pf_normalize messes with m0 */ + h = mtod(m, struct ip6_hdr *); + +#if 1 + /* + * we do not support jumbogram yet. if we keep going, zero ip6_plen + * will do something bad, so drop the packet for now. + */ + if (htons(h->ip6_plen) == 0) { + action = PF_DROP; + REASON_SET(&reason, PFRES_NORM); /*XXX*/ + goto done; + } +#endif + + pd.src = (struct pf_addr *)&h->ip6_src; + pd.dst = (struct pf_addr *)&h->ip6_dst; + pd.sport = pd.dport = NULL; + pd.ip_sum = NULL; + pd.proto_sum = NULL; + pd.dir = dir; + pd.sidx = (dir == PF_IN) ? 0 : 1; + pd.didx = (dir == PF_IN) ? 1 : 0; + pd.af = AF_INET6; + pd.tos = 0; + pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); + + off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr); + pd.proto = h->ip6_nxt; + do { + switch (pd.proto) { + case IPPROTO_FRAGMENT: + action = pf_test_fragment(&r, dir, kif, m, h, + &pd, &a, &ruleset); + if (action == PF_DROP) + REASON_SET(&reason, PFRES_FRAG); + goto done; + case IPPROTO_ROUTING: { + struct ip6_rthdr rthdr; + + if (rh_cnt++) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: IPv6 more than one rthdr\n")); + action = PF_DROP; + REASON_SET(&reason, PFRES_IPOPTIONS); + log = 1; + goto done; + } + if (!pf_pull_hdr(m, off, &rthdr, sizeof(rthdr), NULL, + &reason, pd.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: IPv6 short rthdr\n")); + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: IPv6 rthdr0\n")); + action = PF_DROP; + REASON_SET(&reason, PFRES_IPOPTIONS); + log = 1; + goto done; + } + /* FALLTHROUGH */ + } + case IPPROTO_AH: + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: { + /* get next header and header length */ + struct ip6_ext opt6; + + if (!pf_pull_hdr(m, off, &opt6, sizeof(opt6), + NULL, &reason, pd.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: IPv6 short opt\n")); + action = PF_DROP; + log = 1; + goto done; + } + if (pd.proto == IPPROTO_AH) + off += (opt6.ip6e_len + 2) * 4; + else + off += (opt6.ip6e_len + 1) * 8; + pd.proto = opt6.ip6e_nxt; + /* goto the next header */ + break; + } + default: + terminal++; + break; + } + } while (!terminal); + + /* if there's no routing header, use unmodified mbuf for checksumming */ + if (!n) + n = m; + + switch (pd.proto) { + + case IPPROTO_TCP: { + struct tcphdr th; + + pd.hdr.tcp = &th; + if (!pf_pull_hdr(m, off, &th, sizeof(th), + &action, &reason, AF_INET6)) { + log = action != PF_PASS; + goto done; + } + pd.p_len = pd.tot_len - off - (th.th_off << 2); + action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); + if (action == PF_DROP) + goto done; + action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, + &reason); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + + case IPPROTO_UDP: { + struct udphdr uh; + + pd.hdr.udp = &uh; + if (!pf_pull_hdr(m, off, &uh, sizeof(uh), + &action, &reason, AF_INET6)) { + log = action != PF_PASS; + goto done; + } + if (uh.uh_dport == 0 || + ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || + ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + goto done; + } + action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + + case IPPROTO_ICMP: { + action = PF_DROP; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping IPv6 packet with ICMPv4 payload\n")); + goto done; + } + + case IPPROTO_ICMPV6: { + struct icmp6_hdr ih; + + pd.hdr.icmp6 = &ih; + if (!pf_pull_hdr(m, off, &ih, sizeof(ih), + &action, &reason, AF_INET6)) { + log = action != PF_PASS; + goto done; + } + action = pf_test_state_icmp(&s, dir, kif, + m, off, h, &pd, &reason); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + + default: + action = pf_test_state_other(&s, dir, kif, m, &pd); + if (action == PF_PASS) { + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_rule(&r, &s, dir, kif, m, off, &pd, + &a, &ruleset, inp); + break; + } + +done: + PF_RULES_RUNLOCK(); + if (n != m) { + m_freem(n); + n = NULL; + } + + /* handle dangerous IPv6 extension headers. */ + if (action == PF_PASS && rh_cnt && + !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_IPOPTIONS); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping packet with dangerous v6 headers\n")); + } + + if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (r->rtableid >= 0) + M_SETFIB(m, r->rtableid); + +#ifdef ALTQ + if (action == PF_PASS && r->qid) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (pd.tos & IPTOS_LOWDELAY) + pd.pf_mtag->qid = r->pqid; + else + pd.pf_mtag->qid = r->qid; + /* add hints for ecn */ + pd.pf_mtag->hdr = h; + } +#endif /* ALTQ */ + + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || + pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && + (s->nat_rule.ptr->action == PF_RDR || + s->nat_rule.ptr->action == PF_BINAT) && + IN6_IS_ADDR_LOOPBACK(&pd.dst->v6)) + m->m_flags |= M_SKIP_FIREWALL; + + /* XXX: Anybody working on it?! */ + if (r->divert.port) + printf("pf: divert(9) is not supported for IPv6\n"); + + if (log) { + struct pf_rule *lr; + + if (s != NULL && s->nat_rule.ptr != NULL && + s->nat_rule.ptr->log & PF_LOG_ALL) + lr = s->nat_rule.ptr; + else + lr = r; + PFLOG_PACKET(kif, m, AF_INET6, dir, reason, lr, a, ruleset, + &pd, (s == NULL)); + } + + kif->pfik_bytes[1][dir == PF_OUT][action != PF_PASS] += pd.tot_len; + kif->pfik_packets[1][dir == PF_OUT][action != PF_PASS]++; + + if (action == PF_PASS || r->action == PF_DROP) { + dirndx = (dir == PF_OUT); + r->packets[dirndx]++; + r->bytes[dirndx] += pd.tot_len; + if (a != NULL) { + a->packets[dirndx]++; + a->bytes[dirndx] += pd.tot_len; + } + if (s != NULL) { + if (s->nat_rule.ptr != NULL) { + s->nat_rule.ptr->packets[dirndx]++; + s->nat_rule.ptr->bytes[dirndx] += pd.tot_len; + } + if (s->src_node != NULL) { + s->src_node->packets[dirndx]++; + s->src_node->bytes[dirndx] += pd.tot_len; + } + if (s->nat_src_node != NULL) { + s->nat_src_node->packets[dirndx]++; + s->nat_src_node->bytes[dirndx] += pd.tot_len; + } + dirndx = (dir == s->direction) ? 0 : 1; + s->packets[dirndx]++; + s->bytes[dirndx] += pd.tot_len; + } + tr = r; + nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; + if (nr != NULL && r == &V_pf_default_rule) + tr = nr; + if (tr->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->src.addr.p.tbl, + (s == NULL) ? pd.src : + &s->key[(s->direction == PF_IN)]->addr[0], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->src.neg); + if (tr->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->dst.addr.p.tbl, + (s == NULL) ? pd.dst : + &s->key[(s->direction == PF_IN)]->addr[1], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->dst.neg); + } + + switch (action) { + case PF_SYNPROXY_DROP: + m_freem(*m0); + case PF_DEFER: + *m0 = NULL; + action = PF_PASS; + break; + default: + /* pf_route6() returns unlocked. */ + if (r->rt) { + pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd); + return (action); + } + break; + } + + if (s) + PF_STATE_UNLOCK(s); + + return (action); +} +#endif /* INET6 */ diff --git a/sys/netpfil/pf/pf_if.c b/sys/netpfil/pf/pf_if.c new file mode 100644 index 0000000000000..c010b654c0d02 --- /dev/null +++ b/sys/netpfil/pf/pf_if.c @@ -0,0 +1,859 @@ +/* $OpenBSD: pf_if.c,v 1.54 2008/06/14 16:55:28 mk Exp $ */ + +/* + * Copyright 2005 Henning Brauer + * Copyright 2005 Ryan McBride + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2003 Cedric Berger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include + +#include +#include +#include + +VNET_DEFINE(struct pfi_kif *, pfi_all); +static VNET_DEFINE(long, pfi_update); +#define V_pfi_update VNET(pfi_update) +#define PFI_BUFFER_MAX 0x10000 + +static VNET_DEFINE(struct pfr_addr *, pfi_buffer); +static VNET_DEFINE(int, pfi_buffer_cnt); +static VNET_DEFINE(int, pfi_buffer_max); +#define V_pfi_buffer VNET(pfi_buffer) +#define V_pfi_buffer_cnt VNET(pfi_buffer_cnt) +#define V_pfi_buffer_max VNET(pfi_buffer_max) + +eventhandler_tag pfi_attach_cookie; +eventhandler_tag pfi_detach_cookie; +eventhandler_tag pfi_attach_group_cookie; +eventhandler_tag pfi_change_group_cookie; +eventhandler_tag pfi_detach_group_cookie; +eventhandler_tag pfi_ifaddr_event_cookie; + +static void pfi_attach_ifnet(struct ifnet *); +static void pfi_attach_ifgroup(struct ifg_group *); + +static void pfi_kif_update(struct pfi_kif *); +static void pfi_dynaddr_update(struct pfi_dynaddr *dyn); +static void pfi_table_update(struct pfr_ktable *, struct pfi_kif *, int, + int); +static void pfi_instance_add(struct ifnet *, int, int); +static void pfi_address_add(struct sockaddr *, int, int); +static int pfi_if_compare(struct pfi_kif *, struct pfi_kif *); +static int pfi_skip_if(const char *, struct pfi_kif *); +static int pfi_unmask(void *); +static void pfi_attach_ifnet_event(void * __unused, struct ifnet *); +static void pfi_detach_ifnet_event(void * __unused, struct ifnet *); +static void pfi_attach_group_event(void *, struct ifg_group *); +static void pfi_change_group_event(void *, char *); +static void pfi_detach_group_event(void *, struct ifg_group *); +static void pfi_ifaddr_event(void * __unused, struct ifnet *); + +RB_HEAD(pfi_ifhead, pfi_kif); +static RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); +static RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); +static VNET_DEFINE(struct pfi_ifhead, pfi_ifs); +#define V_pfi_ifs VNET(pfi_ifs) + +#define PFI_BUFFER_MAX 0x10000 +MALLOC_DEFINE(PFI_MTYPE, "pf_ifnet", "pf(4) interface database"); + +LIST_HEAD(pfi_list, pfi_kif); +static VNET_DEFINE(struct pfi_list, pfi_unlinked_kifs); +#define V_pfi_unlinked_kifs VNET(pfi_unlinked_kifs) +static struct mtx pfi_unlnkdkifs_mtx; + +void +pfi_initialize(void) +{ + struct ifg_group *ifg; + struct ifnet *ifp; + struct pfi_kif *kif; + + V_pfi_buffer_max = 64; + V_pfi_buffer = malloc(V_pfi_buffer_max * sizeof(*V_pfi_buffer), + PFI_MTYPE, M_WAITOK); + + mtx_init(&pfi_unlnkdkifs_mtx, "pf unlinked interfaces", NULL, MTX_DEF); + + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + PF_RULES_WLOCK(); + V_pfi_all = pfi_kif_attach(kif, IFG_ALL); + PF_RULES_WUNLOCK(); + + IFNET_RLOCK(); + TAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) + pfi_attach_ifgroup(ifg); + TAILQ_FOREACH(ifp, &V_ifnet, if_link) + pfi_attach_ifnet(ifp); + IFNET_RUNLOCK(); + + pfi_attach_cookie = EVENTHANDLER_REGISTER(ifnet_arrival_event, + pfi_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); + pfi_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, + pfi_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); + pfi_attach_group_cookie = EVENTHANDLER_REGISTER(group_attach_event, + pfi_attach_group_event, curvnet, EVENTHANDLER_PRI_ANY); + pfi_change_group_cookie = EVENTHANDLER_REGISTER(group_change_event, + pfi_change_group_event, curvnet, EVENTHANDLER_PRI_ANY); + pfi_detach_group_cookie = EVENTHANDLER_REGISTER(group_detach_event, + pfi_detach_group_event, curvnet, EVENTHANDLER_PRI_ANY); + pfi_ifaddr_event_cookie = EVENTHANDLER_REGISTER(ifaddr_event, + pfi_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY); +} + +void +pfi_cleanup(void) +{ + struct pfi_kif *p; + + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, pfi_attach_cookie); + EVENTHANDLER_DEREGISTER(ifnet_departure_event, pfi_detach_cookie); + EVENTHANDLER_DEREGISTER(group_attach_event, pfi_attach_group_cookie); + EVENTHANDLER_DEREGISTER(group_change_event, pfi_change_group_cookie); + EVENTHANDLER_DEREGISTER(group_detach_event, pfi_detach_group_cookie); + EVENTHANDLER_DEREGISTER(ifaddr_event, pfi_ifaddr_event_cookie); + + V_pfi_all = NULL; + while ((p = RB_MIN(pfi_ifhead, &V_pfi_ifs))) { + RB_REMOVE(pfi_ifhead, &V_pfi_ifs, p); + free(p, PFI_MTYPE); + } + + while ((p = LIST_FIRST(&V_pfi_unlinked_kifs))) { + LIST_REMOVE(p, pfik_list); + free(p, PFI_MTYPE); + } + + mtx_destroy(&pfi_unlnkdkifs_mtx); + + free(V_pfi_buffer, PFI_MTYPE); +} + +struct pfi_kif * +pfi_kif_find(const char *kif_name) +{ + struct pfi_kif_cmp s; + + PF_RULES_ASSERT(); + + bzero(&s, sizeof(s)); + strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name)); + + return (RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&s)); +} + +struct pfi_kif * +pfi_kif_attach(struct pfi_kif *kif, const char *kif_name) +{ + struct pfi_kif *kif1; + + PF_RULES_WASSERT(); + KASSERT(kif != NULL, ("%s: null kif", __func__)); + + kif1 = pfi_kif_find(kif_name); + if (kif1 != NULL) { + free(kif, PFI_MTYPE); + return (kif1); + } + + bzero(kif, sizeof(*kif)); + strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name)); + /* + * It seems that the value of time_second is in unintialzied state + * when pf sets interface statistics clear time in boot phase if pf + * was statically linked to kernel. Instead of setting the bogus + * time value have pfi_get_ifaces handle this case. In + * pfi_get_ifaces it uses time_second if it sees the time is 0. + */ + kif->pfik_tzero = time_second > 1 ? time_second : 0; + TAILQ_INIT(&kif->pfik_dynaddrs); + + RB_INSERT(pfi_ifhead, &V_pfi_ifs, kif); + + return (kif); +} + +void +pfi_kif_ref(struct pfi_kif *kif) +{ + + PF_RULES_WASSERT(); + kif->pfik_rulerefs++; +} + +void +pfi_kif_unref(struct pfi_kif *kif) +{ + + PF_RULES_WASSERT(); + KASSERT(kif->pfik_rulerefs > 0, ("%s: %p has zero refs", __func__, kif)); + + kif->pfik_rulerefs--; + + if (kif->pfik_rulerefs > 0) + return; + + /* kif referencing an existing ifnet or group should exist. */ + if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == V_pfi_all) + return; + + RB_REMOVE(pfi_ifhead, &V_pfi_ifs, kif); + + kif->pfik_flags |= PFI_IFLAG_REFS; + + mtx_lock(&pfi_unlnkdkifs_mtx); + LIST_INSERT_HEAD(&V_pfi_unlinked_kifs, kif, pfik_list); + mtx_unlock(&pfi_unlnkdkifs_mtx); +} + +void +pfi_kif_purge(void) +{ + struct pfi_kif *kif, *kif1; + + /* + * Do naive mark-and-sweep garbage collecting of old kifs. + * Reference flag is raised by pf_purge_expired_states(). + */ + mtx_lock(&pfi_unlnkdkifs_mtx); + LIST_FOREACH_SAFE(kif, &V_pfi_unlinked_kifs, pfik_list, kif1) { + if (!(kif->pfik_flags & PFI_IFLAG_REFS)) { + LIST_REMOVE(kif, pfik_list); + free(kif, PFI_MTYPE); + } else + kif->pfik_flags &= ~PFI_IFLAG_REFS; + } + mtx_unlock(&pfi_unlnkdkifs_mtx); +} + +int +pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif) +{ + struct ifg_list *p; + + if (rule_kif == NULL || rule_kif == packet_kif) + return (1); + + if (rule_kif->pfik_group != NULL) + /* XXXGL: locking? */ + TAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next) + if (p->ifgl_group == rule_kif->pfik_group) + return (1); + + return (0); +} + +static void +pfi_attach_ifnet(struct ifnet *ifp) +{ + struct pfi_kif *kif; + + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + + PF_RULES_WLOCK(); + V_pfi_update++; + kif = pfi_kif_attach(kif, ifp->if_xname); + + kif->pfik_ifp = ifp; + ifp->if_pf_kif = kif; + + pfi_kif_update(kif); + PF_RULES_WUNLOCK(); +} + +static void +pfi_attach_ifgroup(struct ifg_group *ifg) +{ + struct pfi_kif *kif; + + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + + PF_RULES_WLOCK(); + V_pfi_update++; + kif = pfi_kif_attach(kif, ifg->ifg_group); + + kif->pfik_group = ifg; + ifg->ifg_pf_kif = kif; + PF_RULES_WUNLOCK(); +} + +int +pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + switch (dyn->pfid_acnt4) { + case 0: + return (0); + case 1: + return (PF_MATCHA(0, &dyn->pfid_addr4, + &dyn->pfid_mask4, a, AF_INET)); + default: + return (pfr_match_addr(dyn->pfid_kt, a, AF_INET)); + } + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + switch (dyn->pfid_acnt6) { + case 0: + return (0); + case 1: + return (PF_MATCHA(0, &dyn->pfid_addr6, + &dyn->pfid_mask6, a, AF_INET6)); + default: + return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6)); + } + break; +#endif /* INET6 */ + default: + return (0); + } +} + +int +pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) +{ + struct pfi_dynaddr *dyn; + char tblname[PF_TABLE_NAME_SIZE]; + struct pf_ruleset *ruleset = NULL; + struct pfi_kif *kif; + int rv = 0; + + PF_RULES_WASSERT(); + KASSERT(aw->type == PF_ADDR_DYNIFTL, ("%s: type %u", + __func__, aw->type)); + KASSERT(aw->p.dyn == NULL, ("%s: dyn is %p", __func__, aw->p.dyn)); + + if ((dyn = malloc(sizeof(*dyn), PFI_MTYPE, M_NOWAIT | M_ZERO)) == NULL) + return (ENOMEM); + + if ((kif = malloc(sizeof(*kif), PFI_MTYPE, M_NOWAIT)) == NULL) { + free(dyn, PFI_MTYPE); + return (ENOMEM); + } + + if (!strcmp(aw->v.ifname, "self")) + dyn->pfid_kif = pfi_kif_attach(kif, IFG_ALL); + else + dyn->pfid_kif = pfi_kif_attach(kif, aw->v.ifname); + pfi_kif_ref(dyn->pfid_kif); + + dyn->pfid_net = pfi_unmask(&aw->v.a.mask); + if (af == AF_INET && dyn->pfid_net == 32) + dyn->pfid_net = 128; + strlcpy(tblname, aw->v.ifname, sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_NETWORK) + strlcat(tblname, ":network", sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_BROADCAST) + strlcat(tblname, ":broadcast", sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_PEER) + strlcat(tblname, ":peer", sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_NOALIAS) + strlcat(tblname, ":0", sizeof(tblname)); + if (dyn->pfid_net != 128) + snprintf(tblname + strlen(tblname), + sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net); + if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == NULL) { + rv = ENOMEM; + goto _bad; + } + + if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname)) == NULL) { + rv = ENOMEM; + goto _bad; + } + + dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE; + dyn->pfid_iflags = aw->iflags; + dyn->pfid_af = af; + + TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); + aw->p.dyn = dyn; + pfi_kif_update(dyn->pfid_kif); + + return (0); + +_bad: + if (dyn->pfid_kt != NULL) + pfr_detach_table(dyn->pfid_kt); + if (ruleset != NULL) + pf_remove_if_empty_ruleset(ruleset); + if (dyn->pfid_kif != NULL) + pfi_kif_unref(dyn->pfid_kif); + free(dyn, PFI_MTYPE); + + return (rv); +} + +static void +pfi_kif_update(struct pfi_kif *kif) +{ + struct ifg_list *ifgl; + struct pfi_dynaddr *p; + + PF_RULES_WASSERT(); + + /* update all dynaddr */ + TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry) + pfi_dynaddr_update(p); + + /* again for all groups kif is member of */ + if (kif->pfik_ifp != NULL) { + IF_ADDR_RLOCK(kif->pfik_ifp); + TAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next) + pfi_kif_update((struct pfi_kif *) + ifgl->ifgl_group->ifg_pf_kif); + IF_ADDR_RUNLOCK(kif->pfik_ifp); + } +} + +static void +pfi_dynaddr_update(struct pfi_dynaddr *dyn) +{ + struct pfi_kif *kif; + struct pfr_ktable *kt; + + PF_RULES_WASSERT(); + KASSERT(dyn && dyn->pfid_kif && dyn->pfid_kt, + ("%s: bad argument", __func__)); + + kif = dyn->pfid_kif; + kt = dyn->pfid_kt; + + if (kt->pfrkt_larg != V_pfi_update) { + /* this table needs to be brought up-to-date */ + pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags); + kt->pfrkt_larg = V_pfi_update; + } + pfr_dynaddr_update(kt, dyn); +} + +static void +pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags) +{ + int e, size2 = 0; + struct ifg_member *ifgm; + + V_pfi_buffer_cnt = 0; + + if (kif->pfik_ifp != NULL) + pfi_instance_add(kif->pfik_ifp, net, flags); + else if (kif->pfik_group != NULL) { + IFNET_RLOCK(); + TAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next) + pfi_instance_add(ifgm->ifgm_ifp, net, flags); + IFNET_RUNLOCK(); + } + + if ((e = pfr_set_addrs(&kt->pfrkt_t, V_pfi_buffer, V_pfi_buffer_cnt, &size2, + NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK))) + printf("%s: cannot set %d new addresses into table %s: %d\n", + __func__, V_pfi_buffer_cnt, kt->pfrkt_name, e); +} + +static void +pfi_instance_add(struct ifnet *ifp, int net, int flags) +{ + struct ifaddr *ia; + int got4 = 0, got6 = 0; + int net2, af; + + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_list) { + if (ia->ifa_addr == NULL) + continue; + af = ia->ifa_addr->sa_family; + if (af != AF_INET && af != AF_INET6) + continue; + /* + * XXX: For point-to-point interfaces, (ifname:0) and IPv4, + * jump over addresses without a proper route to work + * around a problem with ppp not fully removing the + * address used during IPCP. + */ + if ((ifp->if_flags & IFF_POINTOPOINT) && + !(ia->ifa_flags & IFA_ROUTE) && + (flags & PFI_AFLAG_NOALIAS) && (af == AF_INET)) + continue; + if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6) + continue; + if ((flags & PFI_AFLAG_BROADCAST) && + !(ifp->if_flags & IFF_BROADCAST)) + continue; + if ((flags & PFI_AFLAG_PEER) && + !(ifp->if_flags & IFF_POINTOPOINT)) + continue; + if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL( + &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr)) + continue; + if (flags & PFI_AFLAG_NOALIAS) { + if (af == AF_INET && got4) + continue; + if (af == AF_INET6 && got6) + continue; + } + if (af == AF_INET) + got4 = 1; + else if (af == AF_INET6) + got6 = 1; + net2 = net; + if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) { + if (af == AF_INET) + net2 = pfi_unmask(&((struct sockaddr_in *) + ia->ifa_netmask)->sin_addr); + else if (af == AF_INET6) + net2 = pfi_unmask(&((struct sockaddr_in6 *) + ia->ifa_netmask)->sin6_addr); + } + if (af == AF_INET && net2 > 32) + net2 = 32; + if (flags & PFI_AFLAG_BROADCAST) + pfi_address_add(ia->ifa_broadaddr, af, net2); + else if (flags & PFI_AFLAG_PEER) + pfi_address_add(ia->ifa_dstaddr, af, net2); + else + pfi_address_add(ia->ifa_addr, af, net2); + } + IF_ADDR_RUNLOCK(ifp); +} + +static void +pfi_address_add(struct sockaddr *sa, int af, int net) +{ + struct pfr_addr *p; + int i; + + if (V_pfi_buffer_cnt >= V_pfi_buffer_max) { + int new_max = V_pfi_buffer_max * 2; + + if (new_max > PFI_BUFFER_MAX) { + printf("%s: address buffer full (%d/%d)\n", __func__, + V_pfi_buffer_cnt, PFI_BUFFER_MAX); + return; + } + p = malloc(new_max * sizeof(*V_pfi_buffer), PFI_MTYPE, + M_NOWAIT); + if (p == NULL) { + printf("%s: no memory to grow buffer (%d/%d)\n", + __func__, V_pfi_buffer_cnt, PFI_BUFFER_MAX); + return; + } + memcpy(V_pfi_buffer, p, V_pfi_buffer_cnt * sizeof(*V_pfi_buffer)); + /* no need to zero buffer */ + free(V_pfi_buffer, PFI_MTYPE); + V_pfi_buffer = p; + V_pfi_buffer_max = new_max; + } + if (af == AF_INET && net > 32) + net = 128; + p = V_pfi_buffer + V_pfi_buffer_cnt++; + bzero(p, sizeof(*p)); + p->pfra_af = af; + p->pfra_net = net; + if (af == AF_INET) + p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr; + else if (af == AF_INET6) { + p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr; + if (IN6_IS_SCOPE_EMBED(&p->pfra_ip6addr)) + p->pfra_ip6addr.s6_addr16[1] = 0; + } + /* mask network address bits */ + if (net < 128) + ((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8)); + for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++) + ((caddr_t)p)[i] = 0; +} + +void +pfi_dynaddr_remove(struct pfi_dynaddr *dyn) +{ + + KASSERT(dyn->pfid_kif != NULL, ("%s: null pfid_kif", __func__)); + KASSERT(dyn->pfid_kt != NULL, ("%s: null pfid_kt", __func__)); + + TAILQ_REMOVE(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); + pfi_kif_unref(dyn->pfid_kif); + pfr_detach_table(dyn->pfid_kt); + free(dyn, PFI_MTYPE); +} + +void +pfi_dynaddr_copyout(struct pf_addr_wrap *aw) +{ + + KASSERT(aw->type == PF_ADDR_DYNIFTL, + ("%s: type %u", __func__, aw->type)); + + if (aw->p.dyn == NULL || aw->p.dyn->pfid_kif == NULL) + return; + aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6; +} + +static int +pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q) +{ + return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ)); +} + +void +pfi_update_status(const char *name, struct pf_status *pfs) +{ + struct pfi_kif *p; + struct pfi_kif_cmp key; + struct ifg_member p_member, *ifgm; + TAILQ_HEAD(, ifg_member) ifg_members; + int i, j, k; + + strlcpy(key.pfik_name, name, sizeof(key.pfik_name)); + p = RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kif *)&key); + if (p == NULL) + return; + + if (p->pfik_group != NULL) { + bcopy(&p->pfik_group->ifg_members, &ifg_members, + sizeof(ifg_members)); + } else { + /* build a temporary list for p only */ + bzero(&p_member, sizeof(p_member)); + p_member.ifgm_ifp = p->pfik_ifp; + TAILQ_INIT(&ifg_members); + TAILQ_INSERT_TAIL(&ifg_members, &p_member, ifgm_next); + } + if (pfs) { + bzero(pfs->pcounters, sizeof(pfs->pcounters)); + bzero(pfs->bcounters, sizeof(pfs->bcounters)); + } + TAILQ_FOREACH(ifgm, &ifg_members, ifgm_next) { + if (ifgm->ifgm_ifp == NULL) + continue; + p = (struct pfi_kif *)ifgm->ifgm_ifp->if_pf_kif; + + /* just clear statistics */ + if (pfs == NULL) { + bzero(p->pfik_packets, sizeof(p->pfik_packets)); + bzero(p->pfik_bytes, sizeof(p->pfik_bytes)); + p->pfik_tzero = time_second; + continue; + } + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) { + pfs->pcounters[i][j][k] += + p->pfik_packets[i][j][k]; + pfs->bcounters[i][j] += + p->pfik_bytes[i][j][k]; + } + } +} + +void +pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size) +{ + struct pfi_kif *p, *nextp; + int n = 0; + + for (p = RB_MIN(pfi_ifhead, &V_pfi_ifs); p; p = nextp) { + nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); + if (pfi_skip_if(name, p)) + continue; + if (*size <= n++) + break; + if (!p->pfik_tzero) + p->pfik_tzero = time_second; + bcopy(p, buf++, sizeof(*buf)); + nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p); + } + *size = n; +} + +static int +pfi_skip_if(const char *filter, struct pfi_kif *p) +{ + int n; + + if (filter == NULL || !*filter) + return (0); + if (!strcmp(p->pfik_name, filter)) + return (0); /* exact match */ + n = strlen(filter); + if (n < 1 || n >= IFNAMSIZ) + return (1); /* sanity check */ + if (filter[n-1] >= '0' && filter[n-1] <= '9') + return (1); /* only do exact match in that case */ + if (strncmp(p->pfik_name, filter, n)) + return (1); /* prefix doesn't match */ + return (p->pfik_name[n] < '0' || p->pfik_name[n] > '9'); +} + +int +pfi_set_flags(const char *name, int flags) +{ + struct pfi_kif *p; + + RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) { + if (pfi_skip_if(name, p)) + continue; + p->pfik_flags |= flags; + } + return (0); +} + +int +pfi_clear_flags(const char *name, int flags) +{ + struct pfi_kif *p; + + RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) { + if (pfi_skip_if(name, p)) + continue; + p->pfik_flags &= ~flags; + } + return (0); +} + +/* from pf_print_state.c */ +static int +pfi_unmask(void *addr) +{ + struct pf_addr *m = addr; + int i = 31, j = 0, b = 0; + u_int32_t tmp; + + while (j < 4 && m->addr32[j] == 0xffffffff) { + b += 32; + j++; + } + if (j < 4) { + tmp = ntohl(m->addr32[j]); + for (i = 31; tmp & (1 << i); --i) + b++; + } + return (b); +} + +static void +pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + + CURVNET_SET(ifp->if_vnet); + pfi_attach_ifnet(ifp); +#ifdef ALTQ + PF_RULES_WLOCK(); + pf_altq_ifnet_event(ifp, 0); + PF_RULES_WUNLOCK(); +#endif + CURVNET_RESTORE(); +} + +static void +pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + struct pfi_kif *kif = (struct pfi_kif *)ifp->if_pf_kif; + + CURVNET_SET(ifp->if_vnet); + PF_RULES_WLOCK(); + V_pfi_update++; + pfi_kif_update(kif); + + kif->pfik_ifp = NULL; + ifp->if_pf_kif = NULL; +#ifdef ALTQ + pf_altq_ifnet_event(ifp, 1); +#endif + PF_RULES_WUNLOCK(); + CURVNET_RESTORE(); +} + +static void +pfi_attach_group_event(void *arg , struct ifg_group *ifg) +{ + + CURVNET_SET((struct vnet *)arg); + pfi_attach_ifgroup(ifg); + CURVNET_RESTORE(); +} + +static void +pfi_change_group_event(void *arg, char *gname) +{ + struct pfi_kif *kif; + + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + + CURVNET_SET((struct vnet *)arg); + PF_RULES_WLOCK(); + V_pfi_update++; + kif = pfi_kif_attach(kif, gname); + pfi_kif_update(kif); + PF_RULES_WUNLOCK(); + CURVNET_RESTORE(); +} + +static void +pfi_detach_group_event(void *arg, struct ifg_group *ifg) +{ + struct pfi_kif *kif = (struct pfi_kif *)ifg->ifg_pf_kif; + + CURVNET_SET((struct vnet *)arg); + PF_RULES_WLOCK(); + V_pfi_update++; + + kif->pfik_group = NULL; + ifg->ifg_pf_kif = NULL; + PF_RULES_WUNLOCK(); + CURVNET_RESTORE(); +} + +static void +pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp) +{ + + CURVNET_SET(ifp->if_vnet); + PF_RULES_WLOCK(); + if (ifp && ifp->if_pf_kif) { + V_pfi_update++; + pfi_kif_update(ifp->if_pf_kif); + } + PF_RULES_WUNLOCK(); + CURVNET_RESTORE(); +} diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c new file mode 100644 index 0000000000000..032f051277159 --- /dev/null +++ b/sys/netpfil/pf/pf_ioctl.c @@ -0,0 +1,3774 @@ +/* $OpenBSD: pf_ioctl.c,v 1.213 2009/02/15 21:46:12 mbalmer Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002,2003 Henning Brauer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_bpf.h" +#include "opt_pf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef INET6 +#include +#endif /* INET6 */ + +#ifdef ALTQ +#include +#endif + +static int pfattach(void); +static struct pf_pool *pf_get_pool(char *, u_int32_t, u_int8_t, u_int32_t, + u_int8_t, u_int8_t, u_int8_t); + +static void pf_mv_pool(struct pf_palist *, struct pf_palist *); +static void pf_empty_pool(struct pf_palist *); +static int pfioctl(struct cdev *, u_long, caddr_t, int, + struct thread *); +#ifdef ALTQ +static int pf_begin_altq(u_int32_t *); +static int pf_rollback_altq(u_int32_t); +static int pf_commit_altq(u_int32_t); +static int pf_enable_altq(struct pf_altq *); +static int pf_disable_altq(struct pf_altq *); +static u_int32_t pf_qname2qid(char *); +static void pf_qid_unref(u_int32_t); +#endif /* ALTQ */ +static int pf_begin_rules(u_int32_t *, int, const char *); +static int pf_rollback_rules(u_int32_t, int, char *); +static int pf_setup_pfsync_matching(struct pf_ruleset *); +static void pf_hash_rule(MD5_CTX *, struct pf_rule *); +static void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *); +static int pf_commit_rules(u_int32_t, int, char *); +static int pf_addr_setup(struct pf_ruleset *, + struct pf_addr_wrap *, sa_family_t); +static void pf_addr_copyout(struct pf_addr_wrap *); + +VNET_DEFINE(struct pf_rule, pf_default_rule); + +#ifdef ALTQ +static VNET_DEFINE(int, pf_altq_running); +#define V_pf_altq_running VNET(pf_altq_running) +#endif + +#define TAGID_MAX 50000 +struct pf_tagname { + TAILQ_ENTRY(pf_tagname) entries; + char name[PF_TAG_NAME_SIZE]; + uint16_t tag; + int ref; +}; + +TAILQ_HEAD(pf_tags, pf_tagname); +#define V_pf_tags VNET(pf_tags) +VNET_DEFINE(struct pf_tags, pf_tags); +#define V_pf_qids VNET(pf_qids) +VNET_DEFINE(struct pf_tags, pf_qids); +static MALLOC_DEFINE(M_PFTAG, "pf_tag", "pf(4) tag names"); +static MALLOC_DEFINE(M_PFALTQ, "pf_altq", "pf(4) altq configuration db"); +static MALLOC_DEFINE(M_PFRULE, "pf_rule", "pf(4) rules"); + +#if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE) +#error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE +#endif + +static u_int16_t tagname2tag(struct pf_tags *, char *); +static u_int16_t pf_tagname2tag(char *); +static void tag_unref(struct pf_tags *, u_int16_t); + +#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x + +struct cdev *pf_dev; + +/* + * XXX - These are new and need to be checked when moveing to a new version + */ +static void pf_clear_states(void); +static int pf_clear_tables(void); +static void pf_clear_srcnodes(struct pf_src_node *); +static void pf_tbladdr_copyout(struct pf_addr_wrap *); + +/* + * Wrapper functions for pfil(9) hooks + */ +#ifdef INET +static int pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, + int dir, struct inpcb *inp); +static int pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, + int dir, struct inpcb *inp); +#endif +#ifdef INET6 +static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, + int dir, struct inpcb *inp); +static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, + int dir, struct inpcb *inp); +#endif + +static int hook_pf(void); +static int dehook_pf(void); +static int shutdown_pf(void); +static int pf_load(void); +static int pf_unload(void); + +static struct cdevsw pf_cdevsw = { + .d_ioctl = pfioctl, + .d_name = PF_NAME, + .d_version = D_VERSION, +}; + +static volatile VNET_DEFINE(int, pf_pfil_hooked); +#define V_pf_pfil_hooked VNET(pf_pfil_hooked) +VNET_DEFINE(int, pf_end_threads); + +struct rwlock pf_rules_lock; + +/* pfsync */ +pfsync_state_import_t *pfsync_state_import_ptr = NULL; +pfsync_insert_state_t *pfsync_insert_state_ptr = NULL; +pfsync_update_state_t *pfsync_update_state_ptr = NULL; +pfsync_delete_state_t *pfsync_delete_state_ptr = NULL; +pfsync_clear_states_t *pfsync_clear_states_ptr = NULL; +pfsync_defer_t *pfsync_defer_ptr = NULL; +/* pflog */ +pflog_packet_t *pflog_packet_ptr = NULL; + +static int +pfattach(void) +{ + u_int32_t *my_timeout = V_pf_default_rule.timeout; + int error; + + pf_initialize(); + pfr_initialize(); + pfi_initialize(); + pf_normalize_init(); + + V_pf_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT; + V_pf_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT; + + RB_INIT(&V_pf_anchors); + pf_init_ruleset(&pf_main_ruleset); + + /* default rule should never be garbage collected */ + V_pf_default_rule.entries.tqe_prev = &V_pf_default_rule.entries.tqe_next; + V_pf_default_rule.action = PF_PASS; + V_pf_default_rule.nr = -1; + V_pf_default_rule.rtableid = -1; + + /* initialize default timeouts */ + my_timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; + my_timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; + my_timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; + my_timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; + my_timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; + my_timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; + my_timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; + my_timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; + my_timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; + my_timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; + my_timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; + my_timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; + my_timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; + my_timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; + my_timeout[PFTM_FRAG] = PFTM_FRAG_VAL; + my_timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL; + my_timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL; + my_timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL; + my_timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START; + my_timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END; + + bzero(&V_pf_status, sizeof(V_pf_status)); + V_pf_status.debug = PF_DEBUG_URGENT; + + V_pf_pfil_hooked = 0; + + /* XXX do our best to avoid a conflict */ + V_pf_status.hostid = arc4random(); + + if ((error = kproc_create(pf_purge_thread, curvnet, NULL, 0, 0, + "pf purge")) != 0) + /* XXXGL: leaked all above. */ + return (error); + if ((error = swi_add(NULL, "pf send", pf_intr, curvnet, SWI_NET, + INTR_MPSAFE, &V_pf_swi_cookie)) != 0) + /* XXXGL: leaked all above. */ + return (error); + + return (0); +} + +static struct pf_pool * +pf_get_pool(char *anchor, u_int32_t ticket, u_int8_t rule_action, + u_int32_t rule_number, u_int8_t r_last, u_int8_t active, + u_int8_t check_ticket) +{ + struct pf_ruleset *ruleset; + struct pf_rule *rule; + int rs_num; + + ruleset = pf_find_ruleset(anchor); + if (ruleset == NULL) + return (NULL); + rs_num = pf_get_ruleset_number(rule_action); + if (rs_num >= PF_RULESET_MAX) + return (NULL); + if (active) { + if (check_ticket && ticket != + ruleset->rules[rs_num].active.ticket) + return (NULL); + if (r_last) + rule = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, + pf_rulequeue); + else + rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + } else { + if (check_ticket && ticket != + ruleset->rules[rs_num].inactive.ticket) + return (NULL); + if (r_last) + rule = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, + pf_rulequeue); + else + rule = TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr); + } + if (!r_last) { + while ((rule != NULL) && (rule->nr != rule_number)) + rule = TAILQ_NEXT(rule, entries); + } + if (rule == NULL) + return (NULL); + + return (&rule->rpool); +} + +static void +pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb) +{ + struct pf_pooladdr *mv_pool_pa; + + while ((mv_pool_pa = TAILQ_FIRST(poola)) != NULL) { + TAILQ_REMOVE(poola, mv_pool_pa, entries); + TAILQ_INSERT_TAIL(poolb, mv_pool_pa, entries); + } +} + +static void +pf_empty_pool(struct pf_palist *poola) +{ + struct pf_pooladdr *pa; + + while ((pa = TAILQ_FIRST(poola)) != NULL) { + switch (pa->addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(pa->addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(pa->addr.p.tbl); + break; + } + if (pa->kif) + pfi_kif_unref(pa->kif); + TAILQ_REMOVE(poola, pa, entries); + free(pa, M_PFRULE); + } +} + +static void +pf_unlink_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) +{ + + PF_RULES_WASSERT(); + + TAILQ_REMOVE(rulequeue, rule, entries); + + PF_UNLNKDRULES_LOCK(); + rule->rule_flag |= PFRULE_REFS; + TAILQ_INSERT_TAIL(&V_pf_unlinked_rules, rule, entries); + PF_UNLNKDRULES_UNLOCK(); +} + +void +pf_free_rule(struct pf_rule *rule) +{ + + PF_RULES_WASSERT(); + + if (rule->tag) + tag_unref(&V_pf_tags, rule->tag); + if (rule->match_tag) + tag_unref(&V_pf_tags, rule->match_tag); +#ifdef ALTQ + if (rule->pqid != rule->qid) + pf_qid_unref(rule->pqid); + pf_qid_unref(rule->qid); +#endif + switch (rule->src.addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(rule->src.addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(rule->src.addr.p.tbl); + break; + } + switch (rule->dst.addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(rule->dst.addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(rule->dst.addr.p.tbl); + break; + } + if (rule->overload_tbl) + pfr_detach_table(rule->overload_tbl); + if (rule->kif) + pfi_kif_unref(rule->kif); + pf_anchor_remove(rule); + pf_empty_pool(&rule->rpool.list); + free(rule, M_PFRULE); +} + +static u_int16_t +tagname2tag(struct pf_tags *head, char *tagname) +{ + struct pf_tagname *tag, *p = NULL; + u_int16_t new_tagid = 1; + + PF_RULES_WASSERT(); + + TAILQ_FOREACH(tag, head, entries) + if (strcmp(tagname, tag->name) == 0) { + tag->ref++; + return (tag->tag); + } + + /* + * to avoid fragmentation, we do a linear search from the beginning + * and take the first free slot we find. if there is none or the list + * is empty, append a new entry at the end. + */ + + /* new entry */ + if (!TAILQ_EMPTY(head)) + for (p = TAILQ_FIRST(head); p != NULL && + p->tag == new_tagid; p = TAILQ_NEXT(p, entries)) + new_tagid = p->tag + 1; + + if (new_tagid > TAGID_MAX) + return (0); + + /* allocate and fill new struct pf_tagname */ + tag = malloc(sizeof(*tag), M_PFTAG, M_NOWAIT|M_ZERO); + if (tag == NULL) + return (0); + strlcpy(tag->name, tagname, sizeof(tag->name)); + tag->tag = new_tagid; + tag->ref++; + + if (p != NULL) /* insert new entry before p */ + TAILQ_INSERT_BEFORE(p, tag, entries); + else /* either list empty or no free slot in between */ + TAILQ_INSERT_TAIL(head, tag, entries); + + return (tag->tag); +} + +static void +tag_unref(struct pf_tags *head, u_int16_t tag) +{ + struct pf_tagname *p, *next; + + PF_RULES_WASSERT(); + + for (p = TAILQ_FIRST(head); p != NULL; p = next) { + next = TAILQ_NEXT(p, entries); + if (tag == p->tag) { + if (--p->ref == 0) { + TAILQ_REMOVE(head, p, entries); + free(p, M_PFTAG); + } + break; + } + } +} + +static u_int16_t +pf_tagname2tag(char *tagname) +{ + return (tagname2tag(&V_pf_tags, tagname)); +} + +#ifdef ALTQ +static u_int32_t +pf_qname2qid(char *qname) +{ + return ((u_int32_t)tagname2tag(&V_pf_qids, qname)); +} + +static void +pf_qid_unref(u_int32_t qid) +{ + tag_unref(&V_pf_qids, (u_int16_t)qid); +} + +static int +pf_begin_altq(u_int32_t *ticket) +{ + struct pf_altq *altq; + int error = 0; + + PF_RULES_WASSERT(); + + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { + /* detach and destroy the discipline */ + error = altq_remove(altq); + } else + pf_qid_unref(altq->qid); + free(altq, M_PFALTQ); + } + if (error) + return (error); + *ticket = ++V_ticket_altqs_inactive; + V_altqs_inactive_open = 1; + return (0); +} + +static int +pf_rollback_altq(u_int32_t ticket) +{ + struct pf_altq *altq; + int error = 0; + + PF_RULES_WASSERT(); + + if (!V_altqs_inactive_open || ticket != V_ticket_altqs_inactive) + return (0); + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { + /* detach and destroy the discipline */ + error = altq_remove(altq); + } else + pf_qid_unref(altq->qid); + free(altq, M_PFALTQ); + } + V_altqs_inactive_open = 0; + return (error); +} + +static int +pf_commit_altq(u_int32_t ticket) +{ + struct pf_altqqueue *old_altqs; + struct pf_altq *altq; + int err, error = 0; + + PF_RULES_WASSERT(); + + if (!V_altqs_inactive_open || ticket != V_ticket_altqs_inactive) + return (EBUSY); + + /* swap altqs, keep the old. */ + old_altqs = V_pf_altqs_active; + V_pf_altqs_active = V_pf_altqs_inactive; + V_pf_altqs_inactive = old_altqs; + V_ticket_altqs_active = V_ticket_altqs_inactive; + + /* Attach new disciplines */ + TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { + /* attach the discipline */ + error = altq_pfattach(altq); + if (error == 0 && V_pf_altq_running) + error = pf_enable_altq(altq); + if (error != 0) + return (error); + } + } + + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(V_pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(V_pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { + /* detach and destroy the discipline */ + if (V_pf_altq_running) + error = pf_disable_altq(altq); + err = altq_pfdetach(altq); + if (err != 0 && error == 0) + error = err; + err = altq_remove(altq); + if (err != 0 && error == 0) + error = err; + } else + pf_qid_unref(altq->qid); + free(altq, M_PFALTQ); + } + + V_altqs_inactive_open = 0; + return (error); +} + +static int +pf_enable_altq(struct pf_altq *altq) +{ + struct ifnet *ifp; + struct tb_profile tb; + int error = 0; + + if ((ifp = ifunit(altq->ifname)) == NULL) + return (EINVAL); + + if (ifp->if_snd.altq_type != ALTQT_NONE) + error = altq_enable(&ifp->if_snd); + + /* set tokenbucket regulator */ + if (error == 0 && ifp != NULL && ALTQ_IS_ENABLED(&ifp->if_snd)) { + tb.rate = altq->ifbandwidth; + tb.depth = altq->tbrsize; + error = tbr_set(&ifp->if_snd, &tb); + } + + return (error); +} + +static int +pf_disable_altq(struct pf_altq *altq) +{ + struct ifnet *ifp; + struct tb_profile tb; + int error; + + if ((ifp = ifunit(altq->ifname)) == NULL) + return (EINVAL); + + /* + * when the discipline is no longer referenced, it was overridden + * by a new one. if so, just return. + */ + if (altq->altq_disc != ifp->if_snd.altq_disc) + return (0); + + error = altq_disable(&ifp->if_snd); + + if (error == 0) { + /* clear tokenbucket regulator */ + tb.rate = 0; + error = tbr_set(&ifp->if_snd, &tb); + } + + return (error); +} + +void +pf_altq_ifnet_event(struct ifnet *ifp, int remove) +{ + struct ifnet *ifp1; + struct pf_altq *a1, *a2, *a3; + u_int32_t ticket; + int error = 0; + + /* Interrupt userland queue modifications */ + if (V_altqs_inactive_open) + pf_rollback_altq(V_ticket_altqs_inactive); + + /* Start new altq ruleset */ + if (pf_begin_altq(&ticket)) + return; + + /* Copy the current active set */ + TAILQ_FOREACH(a1, V_pf_altqs_active, entries) { + a2 = malloc(sizeof(*a2), M_PFALTQ, M_NOWAIT); + if (a2 == NULL) { + error = ENOMEM; + break; + } + bcopy(a1, a2, sizeof(struct pf_altq)); + + if (a2->qname[0] != 0) { + if ((a2->qid = pf_qname2qid(a2->qname)) == 0) { + error = EBUSY; + free(a2, M_PFALTQ); + break; + } + a2->altq_disc = NULL; + TAILQ_FOREACH(a3, V_pf_altqs_inactive, entries) { + if (strncmp(a3->ifname, a2->ifname, + IFNAMSIZ) == 0 && a3->qname[0] == 0) { + a2->altq_disc = a3->altq_disc; + break; + } + } + } + /* Deactivate the interface in question */ + a2->local_flags &= ~PFALTQ_FLAG_IF_REMOVED; + if ((ifp1 = ifunit(a2->ifname)) == NULL || + (remove && ifp1 == ifp)) { + a2->local_flags |= PFALTQ_FLAG_IF_REMOVED; + } else { + error = altq_add(a2); + + if (ticket != V_ticket_altqs_inactive) + error = EBUSY; + + if (error) { + free(a2, M_PFALTQ); + break; + } + } + + TAILQ_INSERT_TAIL(V_pf_altqs_inactive, a2, entries); + } + + if (error != 0) + pf_rollback_altq(ticket); + else + pf_commit_altq(ticket); +} +#endif /* ALTQ */ + +static int +pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor) +{ + struct pf_ruleset *rs; + struct pf_rule *rule; + + PF_RULES_WASSERT(); + + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) + return (EINVAL); + rs = pf_find_or_create_ruleset(anchor); + if (rs == NULL) + return (EINVAL); + while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) { + pf_unlink_rule(rs->rules[rs_num].inactive.ptr, rule); + rs->rules[rs_num].inactive.rcount--; + } + *ticket = ++rs->rules[rs_num].inactive.ticket; + rs->rules[rs_num].inactive.open = 1; + return (0); +} + +static int +pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor) +{ + struct pf_ruleset *rs; + struct pf_rule *rule; + + PF_RULES_WASSERT(); + + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) + return (EINVAL); + rs = pf_find_ruleset(anchor); + if (rs == NULL || !rs->rules[rs_num].inactive.open || + rs->rules[rs_num].inactive.ticket != ticket) + return (0); + while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) { + pf_unlink_rule(rs->rules[rs_num].inactive.ptr, rule); + rs->rules[rs_num].inactive.rcount--; + } + rs->rules[rs_num].inactive.open = 0; + return (0); +} + +#define PF_MD5_UPD(st, elm) \ + MD5Update(ctx, (u_int8_t *) &(st)->elm, sizeof((st)->elm)) + +#define PF_MD5_UPD_STR(st, elm) \ + MD5Update(ctx, (u_int8_t *) (st)->elm, strlen((st)->elm)) + +#define PF_MD5_UPD_HTONL(st, elm, stor) do { \ + (stor) = htonl((st)->elm); \ + MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int32_t));\ +} while (0) + +#define PF_MD5_UPD_HTONS(st, elm, stor) do { \ + (stor) = htons((st)->elm); \ + MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int16_t));\ +} while (0) + +static void +pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) +{ + PF_MD5_UPD(pfr, addr.type); + switch (pfr->addr.type) { + case PF_ADDR_DYNIFTL: + PF_MD5_UPD(pfr, addr.v.ifname); + PF_MD5_UPD(pfr, addr.iflags); + break; + case PF_ADDR_TABLE: + PF_MD5_UPD(pfr, addr.v.tblname); + break; + case PF_ADDR_ADDRMASK: + /* XXX ignore af? */ + PF_MD5_UPD(pfr, addr.v.a.addr.addr32); + PF_MD5_UPD(pfr, addr.v.a.mask.addr32); + break; + } + + PF_MD5_UPD(pfr, port[0]); + PF_MD5_UPD(pfr, port[1]); + PF_MD5_UPD(pfr, neg); + PF_MD5_UPD(pfr, port_op); +} + +static void +pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule) +{ + u_int16_t x; + u_int32_t y; + + pf_hash_rule_addr(ctx, &rule->src); + pf_hash_rule_addr(ctx, &rule->dst); + PF_MD5_UPD_STR(rule, label); + PF_MD5_UPD_STR(rule, ifname); + PF_MD5_UPD_STR(rule, match_tagname); + PF_MD5_UPD_HTONS(rule, match_tag, x); /* dup? */ + PF_MD5_UPD_HTONL(rule, os_fingerprint, y); + PF_MD5_UPD_HTONL(rule, prob, y); + PF_MD5_UPD_HTONL(rule, uid.uid[0], y); + PF_MD5_UPD_HTONL(rule, uid.uid[1], y); + PF_MD5_UPD(rule, uid.op); + PF_MD5_UPD_HTONL(rule, gid.gid[0], y); + PF_MD5_UPD_HTONL(rule, gid.gid[1], y); + PF_MD5_UPD(rule, gid.op); + PF_MD5_UPD_HTONL(rule, rule_flag, y); + PF_MD5_UPD(rule, action); + PF_MD5_UPD(rule, direction); + PF_MD5_UPD(rule, af); + PF_MD5_UPD(rule, quick); + PF_MD5_UPD(rule, ifnot); + PF_MD5_UPD(rule, match_tag_not); + PF_MD5_UPD(rule, natpass); + PF_MD5_UPD(rule, keep_state); + PF_MD5_UPD(rule, proto); + PF_MD5_UPD(rule, type); + PF_MD5_UPD(rule, code); + PF_MD5_UPD(rule, flags); + PF_MD5_UPD(rule, flagset); + PF_MD5_UPD(rule, allow_opts); + PF_MD5_UPD(rule, rt); + PF_MD5_UPD(rule, tos); +} + +static int +pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) +{ + struct pf_ruleset *rs; + struct pf_rule *rule, **old_array; + struct pf_rulequeue *old_rules; + int error; + u_int32_t old_rcount; + + PF_RULES_WASSERT(); + + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) + return (EINVAL); + rs = pf_find_ruleset(anchor); + if (rs == NULL || !rs->rules[rs_num].inactive.open || + ticket != rs->rules[rs_num].inactive.ticket) + return (EBUSY); + + /* Calculate checksum for the main ruleset */ + if (rs == &pf_main_ruleset) { + error = pf_setup_pfsync_matching(rs); + if (error != 0) + return (error); + } + + /* Swap rules, keep the old. */ + old_rules = rs->rules[rs_num].active.ptr; + old_rcount = rs->rules[rs_num].active.rcount; + old_array = rs->rules[rs_num].active.ptr_array; + + rs->rules[rs_num].active.ptr = + rs->rules[rs_num].inactive.ptr; + rs->rules[rs_num].active.ptr_array = + rs->rules[rs_num].inactive.ptr_array; + rs->rules[rs_num].active.rcount = + rs->rules[rs_num].inactive.rcount; + rs->rules[rs_num].inactive.ptr = old_rules; + rs->rules[rs_num].inactive.ptr_array = old_array; + rs->rules[rs_num].inactive.rcount = old_rcount; + + rs->rules[rs_num].active.ticket = + rs->rules[rs_num].inactive.ticket; + pf_calc_skip_steps(rs->rules[rs_num].active.ptr); + + + /* Purge the old rule list. */ + while ((rule = TAILQ_FIRST(old_rules)) != NULL) + pf_unlink_rule(old_rules, rule); + if (rs->rules[rs_num].inactive.ptr_array) + free(rs->rules[rs_num].inactive.ptr_array, M_TEMP); + rs->rules[rs_num].inactive.ptr_array = NULL; + rs->rules[rs_num].inactive.rcount = 0; + rs->rules[rs_num].inactive.open = 0; + pf_remove_if_empty_ruleset(rs); + + return (0); +} + +static int +pf_setup_pfsync_matching(struct pf_ruleset *rs) +{ + MD5_CTX ctx; + struct pf_rule *rule; + int rs_cnt; + u_int8_t digest[PF_MD5_DIGEST_LENGTH]; + + MD5Init(&ctx); + for (rs_cnt = 0; rs_cnt < PF_RULESET_MAX; rs_cnt++) { + /* XXX PF_RULESET_SCRUB as well? */ + if (rs_cnt == PF_RULESET_SCRUB) + continue; + + if (rs->rules[rs_cnt].inactive.ptr_array) + free(rs->rules[rs_cnt].inactive.ptr_array, M_TEMP); + rs->rules[rs_cnt].inactive.ptr_array = NULL; + + if (rs->rules[rs_cnt].inactive.rcount) { + rs->rules[rs_cnt].inactive.ptr_array = + malloc(sizeof(caddr_t) * + rs->rules[rs_cnt].inactive.rcount, + M_TEMP, M_NOWAIT); + + if (!rs->rules[rs_cnt].inactive.ptr_array) + return (ENOMEM); + } + + TAILQ_FOREACH(rule, rs->rules[rs_cnt].inactive.ptr, + entries) { + pf_hash_rule(&ctx, rule); + (rs->rules[rs_cnt].inactive.ptr_array)[rule->nr] = rule; + } + } + + MD5Final(digest, &ctx); + memcpy(V_pf_status.pf_chksum, digest, sizeof(V_pf_status.pf_chksum)); + return (0); +} + +static int +pf_addr_setup(struct pf_ruleset *ruleset, struct pf_addr_wrap *addr, + sa_family_t af) +{ + int error = 0; + + switch (addr->type) { + case PF_ADDR_TABLE: + addr->p.tbl = pfr_attach_table(ruleset, addr->v.tblname); + if (addr->p.tbl == NULL) + error = ENOMEM; + break; + case PF_ADDR_DYNIFTL: + error = pfi_dynaddr_setup(addr, af); + break; + } + + return (error); +} + +static void +pf_addr_copyout(struct pf_addr_wrap *addr) +{ + + switch (addr->type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_copyout(addr); + break; + case PF_ADDR_TABLE: + pf_tbladdr_copyout(addr); + break; + } +} + +static int +pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) +{ + int error = 0; + + CURVNET_SET(TD_TO_VNET(td)); + + /* XXX keep in sync with switch() below */ + if (securelevel_gt(td->td_ucred, 2)) + switch (cmd) { + case DIOCGETRULES: + case DIOCGETRULE: + case DIOCGETADDRS: + case DIOCGETADDR: + case DIOCGETSTATE: + case DIOCSETSTATUSIF: + case DIOCGETSTATUS: + case DIOCCLRSTATUS: + case DIOCNATLOOK: + case DIOCSETDEBUG: + case DIOCGETSTATES: + case DIOCGETTIMEOUT: + case DIOCCLRRULECTRS: + case DIOCGETLIMIT: + case DIOCGETALTQS: + case DIOCGETALTQ: + case DIOCGETQSTATS: + case DIOCGETRULESETS: + case DIOCGETRULESET: + case DIOCRGETTABLES: + case DIOCRGETTSTATS: + case DIOCRCLRTSTATS: + case DIOCRCLRADDRS: + case DIOCRADDADDRS: + case DIOCRDELADDRS: + case DIOCRSETADDRS: + case DIOCRGETADDRS: + case DIOCRGETASTATS: + case DIOCRCLRASTATS: + case DIOCRTSTADDRS: + case DIOCOSFPGET: + case DIOCGETSRCNODES: + case DIOCCLRSRCNODES: + case DIOCIGETIFACES: + case DIOCGIFSPEED: + case DIOCSETIFFLAG: + case DIOCCLRIFFLAG: + break; + case DIOCRCLRTABLES: + case DIOCRADDTABLES: + case DIOCRDELTABLES: + case DIOCRSETTFLAGS: + if (((struct pfioc_table *)addr)->pfrio_flags & + PFR_FLAG_DUMMY) + break; /* dummy operation ok */ + return (EPERM); + default: + return (EPERM); + } + + if (!(flags & FWRITE)) + switch (cmd) { + case DIOCGETRULES: + case DIOCGETADDRS: + case DIOCGETADDR: + case DIOCGETSTATE: + case DIOCGETSTATUS: + case DIOCGETSTATES: + case DIOCGETTIMEOUT: + case DIOCGETLIMIT: + case DIOCGETALTQS: + case DIOCGETALTQ: + case DIOCGETQSTATS: + case DIOCGETRULESETS: + case DIOCGETRULESET: + case DIOCNATLOOK: + case DIOCRGETTABLES: + case DIOCRGETTSTATS: + case DIOCRGETADDRS: + case DIOCRGETASTATS: + case DIOCRTSTADDRS: + case DIOCOSFPGET: + case DIOCGETSRCNODES: + case DIOCIGETIFACES: + case DIOCGIFSPEED: + break; + case DIOCRCLRTABLES: + case DIOCRADDTABLES: + case DIOCRDELTABLES: + case DIOCRCLRTSTATS: + case DIOCRCLRADDRS: + case DIOCRADDADDRS: + case DIOCRDELADDRS: + case DIOCRSETADDRS: + case DIOCRSETTFLAGS: + if (((struct pfioc_table *)addr)->pfrio_flags & + PFR_FLAG_DUMMY) { + flags |= FWRITE; /* need write lock for dummy */ + break; /* dummy operation ok */ + } + return (EACCES); + case DIOCGETRULE: + if (((struct pfioc_rule *)addr)->action == + PF_GET_CLR_CNTR) + return (EACCES); + break; + default: + return (EACCES); + } + + switch (cmd) { + case DIOCSTART: + PF_RULES_WLOCK(); + if (V_pf_status.running) + error = EEXIST; + else { + int cpu; + + PF_RULES_WUNLOCK(); + error = hook_pf(); + if (error) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: pfil registration failed\n")); + break; + } + PF_RULES_WLOCK(); + V_pf_status.running = 1; + V_pf_status.since = time_second; + + CPU_FOREACH(cpu) + V_pf_stateid[cpu] = time_second; + + DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); + } + PF_RULES_WUNLOCK(); + break; + + case DIOCSTOP: + PF_RULES_WLOCK(); + if (!V_pf_status.running) + error = ENOENT; + else { + V_pf_status.running = 0; + PF_RULES_WUNLOCK(); + error = dehook_pf(); + if (error) { + V_pf_status.running = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: pfil unregistration failed\n")); + } + PF_RULES_WLOCK(); + V_pf_status.since = time_second; + DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); + } + PF_RULES_WUNLOCK(); + break; + + case DIOCADDRULE: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *rule, *tail; + struct pf_pooladdr *pa; + struct pfi_kif *kif = NULL; + int rs_num; + + if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { + error = EINVAL; + break; + } +#ifndef INET + if (pr->rule.af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pr->rule.af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + + rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK); + bcopy(&pr->rule, rule, sizeof(struct pf_rule)); + if (rule->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + rule->cuid = td->td_ucred->cr_ruid; + rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; + TAILQ_INIT(&rule->rpool.list); + +#define ERROUT(x) { error = (x); goto DIOCADDRULE_error; } + + PF_RULES_WLOCK(); + pr->anchor[sizeof(pr->anchor) - 1] = 0; + ruleset = pf_find_ruleset(pr->anchor); + if (ruleset == NULL) + ERROUT(EINVAL); + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) + ERROUT(EINVAL); + if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { + DPFPRINTF(PF_DEBUG_MISC, + ("ticket: %d != [%d]%d\n", pr->ticket, rs_num, + ruleset->rules[rs_num].inactive.ticket)); + ERROUT(EBUSY); + } + if (pr->pool_ticket != V_ticket_pabuf) { + DPFPRINTF(PF_DEBUG_MISC, + ("pool_ticket: %d != %d\n", pr->pool_ticket, + V_ticket_pabuf)); + ERROUT(EBUSY); + } + + tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, + pf_rulequeue); + if (tail) + rule->nr = tail->nr + 1; + else + rule->nr = 0; + if (rule->ifname[0]) { + rule->kif = pfi_kif_attach(kif, rule->ifname); + pfi_kif_ref(rule->kif); + } else + rule->kif = NULL; + + if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) + error = EBUSY; + +#ifdef ALTQ + /* set queue IDs */ + if (rule->qname[0] != 0) { + if ((rule->qid = pf_qname2qid(rule->qname)) == 0) + error = EBUSY; + else if (rule->pqname[0] != 0) { + if ((rule->pqid = + pf_qname2qid(rule->pqname)) == 0) + error = EBUSY; + } else + rule->pqid = rule->qid; + } +#endif + if (rule->tagname[0]) + if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) + error = EBUSY; + if (rule->match_tagname[0]) + if ((rule->match_tag = + pf_tagname2tag(rule->match_tagname)) == 0) + error = EBUSY; + if (rule->rt && !rule->direction) + error = EINVAL; + if (!rule->log) + rule->logif = 0; + if (rule->logif >= PFLOGIFS_MAX) + error = EINVAL; + if (pf_addr_setup(ruleset, &rule->src.addr, rule->af)) + error = ENOMEM; + if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af)) + error = ENOMEM; + if (pf_anchor_setup(rule, ruleset, pr->anchor_call)) + error = EINVAL; + TAILQ_FOREACH(pa, &V_pf_pabuf, entries) + if (pa->addr.type == PF_ADDR_TABLE) { + pa->addr.p.tbl = pfr_attach_table(ruleset, + pa->addr.v.tblname); + if (pa->addr.p.tbl == NULL) + error = ENOMEM; + } + + if (rule->overload_tblname[0]) { + if ((rule->overload_tbl = pfr_attach_table(ruleset, + rule->overload_tblname)) == NULL) + error = EINVAL; + else + rule->overload_tbl->pfrkt_flags |= + PFR_TFLAG_ACTIVE; + } + + pf_mv_pool(&V_pf_pabuf, &rule->rpool.list); + if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || + (rule->action == PF_BINAT)) && rule->anchor == NULL) || + (rule->rt > PF_FASTROUTE)) && + (TAILQ_FIRST(&rule->rpool.list) == NULL)) + error = EINVAL; + + if (error) { + pf_free_rule(rule); + PF_RULES_WUNLOCK(); + break; + } + + rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); + rule->evaluations = rule->packets[0] = rule->packets[1] = + rule->bytes[0] = rule->bytes[1] = 0; + TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, + rule, entries); + ruleset->rules[rs_num].inactive.rcount++; + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCADDRULE_error: + PF_RULES_WUNLOCK(); + free(rule, M_PFRULE); + if (kif) + free(kif, PFI_MTYPE); + break; + } + + case DIOCGETRULES: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *tail; + int rs_num; + + PF_RULES_WLOCK(); + pr->anchor[sizeof(pr->anchor) - 1] = 0; + ruleset = pf_find_ruleset(pr->anchor); + if (ruleset == NULL) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + tail = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, + pf_rulequeue); + if (tail) + pr->nr = tail->nr + 1; + else + pr->nr = 0; + pr->ticket = ruleset->rules[rs_num].active.ticket; + PF_RULES_WUNLOCK(); + break; + } + + case DIOCGETRULE: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *rule; + int rs_num, i; + + PF_RULES_WLOCK(); + pr->anchor[sizeof(pr->anchor) - 1] = 0; + ruleset = pf_find_ruleset(pr->anchor); + if (ruleset == NULL) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + if (pr->ticket != ruleset->rules[rs_num].active.ticket) { + PF_RULES_WUNLOCK(); + error = EBUSY; + break; + } + rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + while ((rule != NULL) && (rule->nr != pr->nr)) + rule = TAILQ_NEXT(rule, entries); + if (rule == NULL) { + PF_RULES_WUNLOCK(); + error = EBUSY; + break; + } + bcopy(rule, &pr->rule, sizeof(struct pf_rule)); + if (pf_anchor_copyout(ruleset, rule, pr)) { + PF_RULES_WUNLOCK(); + error = EBUSY; + break; + } + pf_addr_copyout(&pr->rule.src.addr); + pf_addr_copyout(&pr->rule.dst.addr); + for (i = 0; i < PF_SKIP_COUNT; ++i) + if (rule->skip[i].ptr == NULL) + pr->rule.skip[i].nr = -1; + else + pr->rule.skip[i].nr = + rule->skip[i].ptr->nr; + + if (pr->action == PF_GET_CLR_CNTR) { + rule->evaluations = 0; + rule->packets[0] = rule->packets[1] = 0; + rule->bytes[0] = rule->bytes[1] = 0; + rule->states_tot = 0; + } + PF_RULES_WUNLOCK(); + break; + } + + case DIOCCHANGERULE: { + struct pfioc_rule *pcr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *oldrule = NULL, *newrule = NULL; + struct pfi_kif *kif = NULL; + struct pf_pooladdr *pa; + u_int32_t nr = 0; + int rs_num; + + if (pcr->action < PF_CHANGE_ADD_HEAD || + pcr->action > PF_CHANGE_GET_TICKET) { + error = EINVAL; + break; + } + if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { + error = EINVAL; + break; + } + + if (pcr->action != PF_CHANGE_REMOVE) { +#ifndef INET + if (pcr->rule.af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pcr->rule.af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + newrule = malloc(sizeof(*newrule), M_PFRULE, M_WAITOK); + bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); + newrule->cuid = td->td_ucred->cr_ruid; + newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0; + TAILQ_INIT(&newrule->rpool.list); + /* Initialize refcounting. */ + newrule->states_cur = 0; + newrule->entries.tqe_prev = NULL; + + if (newrule->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + } + +#define ERROUT(x) { error = (x); goto DIOCCHANGERULE_error; } + + PF_RULES_WLOCK(); + if (!(pcr->action == PF_CHANGE_REMOVE || + pcr->action == PF_CHANGE_GET_TICKET) && + pcr->pool_ticket != V_ticket_pabuf) + ERROUT(EBUSY); + + ruleset = pf_find_ruleset(pcr->anchor); + if (ruleset == NULL) + ERROUT(EINVAL); + + rs_num = pf_get_ruleset_number(pcr->rule.action); + if (rs_num >= PF_RULESET_MAX) + ERROUT(EINVAL); + + if (pcr->action == PF_CHANGE_GET_TICKET) { + pcr->ticket = ++ruleset->rules[rs_num].active.ticket; + ERROUT(0); + } else if (pcr->ticket != + ruleset->rules[rs_num].active.ticket) + ERROUT(EINVAL); + + if (pcr->action != PF_CHANGE_REMOVE) { + if (newrule->ifname[0]) { + newrule->kif = pfi_kif_attach(kif, + newrule->ifname); + pfi_kif_ref(newrule->kif); + } else + newrule->kif = NULL; + + if (newrule->rtableid > 0 && + newrule->rtableid >= rt_numfibs) + error = EBUSY; + +#ifdef ALTQ + /* set queue IDs */ + if (newrule->qname[0] != 0) { + if ((newrule->qid = + pf_qname2qid(newrule->qname)) == 0) + error = EBUSY; + else if (newrule->pqname[0] != 0) { + if ((newrule->pqid = + pf_qname2qid(newrule->pqname)) == 0) + error = EBUSY; + } else + newrule->pqid = newrule->qid; + } +#endif /* ALTQ */ + if (newrule->tagname[0]) + if ((newrule->tag = + pf_tagname2tag(newrule->tagname)) == 0) + error = EBUSY; + if (newrule->match_tagname[0]) + if ((newrule->match_tag = pf_tagname2tag( + newrule->match_tagname)) == 0) + error = EBUSY; + if (newrule->rt && !newrule->direction) + error = EINVAL; + if (!newrule->log) + newrule->logif = 0; + if (newrule->logif >= PFLOGIFS_MAX) + error = EINVAL; + if (pf_addr_setup(ruleset, &newrule->src.addr, newrule->af)) + error = ENOMEM; + if (pf_addr_setup(ruleset, &newrule->dst.addr, newrule->af)) + error = ENOMEM; + if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call)) + error = EINVAL; + TAILQ_FOREACH(pa, &V_pf_pabuf, entries) + if (pa->addr.type == PF_ADDR_TABLE) { + pa->addr.p.tbl = + pfr_attach_table(ruleset, + pa->addr.v.tblname); + if (pa->addr.p.tbl == NULL) + error = ENOMEM; + } + + if (newrule->overload_tblname[0]) { + if ((newrule->overload_tbl = pfr_attach_table( + ruleset, newrule->overload_tblname)) == + NULL) + error = EINVAL; + else + newrule->overload_tbl->pfrkt_flags |= + PFR_TFLAG_ACTIVE; + } + + pf_mv_pool(&V_pf_pabuf, &newrule->rpool.list); + if (((((newrule->action == PF_NAT) || + (newrule->action == PF_RDR) || + (newrule->action == PF_BINAT) || + (newrule->rt > PF_FASTROUTE)) && + !newrule->anchor)) && + (TAILQ_FIRST(&newrule->rpool.list) == NULL)) + error = EINVAL; + + if (error) { + pf_free_rule(newrule); + PF_RULES_WUNLOCK(); + break; + } + + newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list); + newrule->evaluations = 0; + newrule->packets[0] = newrule->packets[1] = 0; + newrule->bytes[0] = newrule->bytes[1] = 0; + } + pf_empty_pool(&V_pf_pabuf); + + if (pcr->action == PF_CHANGE_ADD_HEAD) + oldrule = TAILQ_FIRST( + ruleset->rules[rs_num].active.ptr); + else if (pcr->action == PF_CHANGE_ADD_TAIL) + oldrule = TAILQ_LAST( + ruleset->rules[rs_num].active.ptr, pf_rulequeue); + else { + oldrule = TAILQ_FIRST( + ruleset->rules[rs_num].active.ptr); + while ((oldrule != NULL) && (oldrule->nr != pcr->nr)) + oldrule = TAILQ_NEXT(oldrule, entries); + if (oldrule == NULL) { + if (newrule != NULL) + pf_free_rule(newrule); + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + } + + if (pcr->action == PF_CHANGE_REMOVE) { + pf_unlink_rule(ruleset->rules[rs_num].active.ptr, + oldrule); + ruleset->rules[rs_num].active.rcount--; + } else { + if (oldrule == NULL) + TAILQ_INSERT_TAIL( + ruleset->rules[rs_num].active.ptr, + newrule, entries); + else if (pcr->action == PF_CHANGE_ADD_HEAD || + pcr->action == PF_CHANGE_ADD_BEFORE) + TAILQ_INSERT_BEFORE(oldrule, newrule, entries); + else + TAILQ_INSERT_AFTER( + ruleset->rules[rs_num].active.ptr, + oldrule, newrule, entries); + ruleset->rules[rs_num].active.rcount++; + } + + nr = 0; + TAILQ_FOREACH(oldrule, + ruleset->rules[rs_num].active.ptr, entries) + oldrule->nr = nr++; + + ruleset->rules[rs_num].active.ticket++; + + pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); + pf_remove_if_empty_ruleset(ruleset); + + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCCHANGERULE_error: + PF_RULES_WUNLOCK(); + if (newrule != NULL) + free(newrule, M_PFRULE); + if (kif != NULL) + free(kif, PFI_MTYPE); + break; + } + + case DIOCCLRSTATES: { + struct pf_state *s; + struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; + u_int i, killed = 0; + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + +relock_DIOCCLRSTATES: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) + if (!psk->psk_ifname[0] || + !strcmp(psk->psk_ifname, + s->kif->pfik_name)) { + /* + * Don't send out individual + * delete messages. + */ + s->state_flags |= PFSTATE_NOSYNC; + pf_unlink_state(s, PF_ENTER_LOCKED); + killed++; + goto relock_DIOCCLRSTATES; + } + PF_HASHROW_UNLOCK(ih); + } + psk->psk_killed = killed; + if (pfsync_clear_states_ptr != NULL) + pfsync_clear_states_ptr(V_pf_status.hostid, psk->psk_ifname); + break; + } + + case DIOCKILLSTATES: { + struct pf_state *s; + struct pf_state_key *sk; + struct pf_addr *srcaddr, *dstaddr; + u_int16_t srcport, dstport; + struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; + u_int i, killed = 0; + + if (psk->psk_pfcmp.id) { + if (psk->psk_pfcmp.creatorid == 0) + psk->psk_pfcmp.creatorid = V_pf_status.hostid; + if ((s = pf_find_state_byid(psk->psk_pfcmp.id, + psk->psk_pfcmp.creatorid))) { + pf_unlink_state(s, PF_ENTER_LOCKED); + psk->psk_killed = 1; + } + break; + } + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + +relock_DIOCKILLSTATES: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + sk = s->key[PF_SK_WIRE]; + if (s->direction == PF_OUT) { + srcaddr = &sk->addr[1]; + dstaddr = &sk->addr[0]; + srcport = sk->port[0]; + dstport = sk->port[0]; + } else { + srcaddr = &sk->addr[0]; + dstaddr = &sk->addr[1]; + srcport = sk->port[0]; + dstport = sk->port[0]; + } + + if ((!psk->psk_af || sk->af == psk->psk_af) + && (!psk->psk_proto || psk->psk_proto == + sk->proto) && + PF_MATCHA(psk->psk_src.neg, + &psk->psk_src.addr.v.a.addr, + &psk->psk_src.addr.v.a.mask, + srcaddr, sk->af) && + PF_MATCHA(psk->psk_dst.neg, + &psk->psk_dst.addr.v.a.addr, + &psk->psk_dst.addr.v.a.mask, + dstaddr, sk->af) && + (psk->psk_src.port_op == 0 || + pf_match_port(psk->psk_src.port_op, + psk->psk_src.port[0], psk->psk_src.port[1], + srcport)) && + (psk->psk_dst.port_op == 0 || + pf_match_port(psk->psk_dst.port_op, + psk->psk_dst.port[0], psk->psk_dst.port[1], + dstport)) && + (!psk->psk_label[0] || + (s->rule.ptr->label[0] && + !strcmp(psk->psk_label, + s->rule.ptr->label))) && + (!psk->psk_ifname[0] || + !strcmp(psk->psk_ifname, + s->kif->pfik_name))) { + pf_unlink_state(s, PF_ENTER_LOCKED); + killed++; + goto relock_DIOCKILLSTATES; + } + } + PF_HASHROW_UNLOCK(ih); + } + psk->psk_killed = killed; + break; + } + + case DIOCADDSTATE: { + struct pfioc_state *ps = (struct pfioc_state *)addr; + struct pfsync_state *sp = &ps->state; + + if (sp->timeout >= PFTM_MAX && + sp->timeout != PFTM_UNTIL_PACKET) { + error = EINVAL; + break; + } + if (pfsync_state_import_ptr != NULL) { + PF_RULES_RLOCK(); + error = pfsync_state_import_ptr(sp, PFSYNC_SI_IOCTL); + PF_RULES_RUNLOCK(); + } + error = EOPNOTSUPP; + break; + } + + case DIOCGETSTATE: { + struct pfioc_state *ps = (struct pfioc_state *)addr; + struct pf_state *s; + + s = pf_find_state_byid(ps->state.id, ps->state.creatorid); + if (s == NULL) { + error = ENOENT; + break; + } + + pfsync_state_export(&ps->state, s); + PF_STATE_UNLOCK(s); + break; + } + + case DIOCGETSTATES: { + struct pfioc_states *ps = (struct pfioc_states *)addr; + struct pf_state *s; + struct pfsync_state *pstore, *p; + int i, nr; + + if (ps->ps_len == 0) { + nr = uma_zone_get_cur(V_pf_state_z); + ps->ps_len = sizeof(struct pfsync_state) * nr; + break; + } + + p = pstore = malloc(ps->ps_len, M_TEMP, M_WAITOK); + nr = 0; + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + + if (s->timeout == PFTM_UNLINKED) + continue; + + if ((nr+1) * sizeof(*p) > ps->ps_len) { + PF_HASHROW_UNLOCK(ih); + goto DIOCGETSTATES_full; + } + pfsync_state_export(p, s); + p++; + nr++; + } + PF_HASHROW_UNLOCK(ih); + } +DIOCGETSTATES_full: + error = copyout(pstore, ps->ps_states, + sizeof(struct pfsync_state) * nr); + if (error) { + free(pstore, M_TEMP); + break; + } + ps->ps_len = sizeof(struct pfsync_state) * nr; + free(pstore, M_TEMP); + + break; + } + + case DIOCGETSTATUS: { + struct pf_status *s = (struct pf_status *)addr; + PF_RULES_RLOCK(); + bcopy(&V_pf_status, s, sizeof(struct pf_status)); + pfi_update_status(s->ifname, s); + PF_RULES_RUNLOCK(); + break; + } + + case DIOCSETSTATUSIF: { + struct pfioc_if *pi = (struct pfioc_if *)addr; + + if (pi->ifname[0] == 0) { + bzero(V_pf_status.ifname, IFNAMSIZ); + break; + } + PF_RULES_WLOCK(); + strlcpy(V_pf_status.ifname, pi->ifname, IFNAMSIZ); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCCLRSTATUS: { + PF_RULES_WLOCK(); + bzero(V_pf_status.counters, sizeof(V_pf_status.counters)); + bzero(V_pf_status.fcounters, sizeof(V_pf_status.fcounters)); + bzero(V_pf_status.scounters, sizeof(V_pf_status.scounters)); + V_pf_status.since = time_second; + if (*V_pf_status.ifname) + pfi_update_status(V_pf_status.ifname, NULL); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCNATLOOK: { + struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; + struct pf_state_key *sk; + struct pf_state *state; + struct pf_state_key_cmp key; + int m = 0, direction = pnl->direction; + int sidx, didx; + + /* NATLOOK src and dst are reversed, so reverse sidx/didx */ + sidx = (direction == PF_IN) ? 1 : 0; + didx = (direction == PF_IN) ? 0 : 1; + + if (!pnl->proto || + PF_AZERO(&pnl->saddr, pnl->af) || + PF_AZERO(&pnl->daddr, pnl->af) || + ((pnl->proto == IPPROTO_TCP || + pnl->proto == IPPROTO_UDP) && + (!pnl->dport || !pnl->sport))) + error = EINVAL; + else { + key.af = pnl->af; + key.proto = pnl->proto; + PF_ACPY(&key.addr[sidx], &pnl->saddr, pnl->af); + key.port[sidx] = pnl->sport; + PF_ACPY(&key.addr[didx], &pnl->daddr, pnl->af); + key.port[didx] = pnl->dport; + + state = pf_find_state_all(&key, direction, &m); + + if (m > 1) + error = E2BIG; /* more than one state */ + else if (state != NULL) { + /* XXXGL: not locked read */ + sk = state->key[sidx]; + PF_ACPY(&pnl->rsaddr, &sk->addr[sidx], sk->af); + pnl->rsport = sk->port[sidx]; + PF_ACPY(&pnl->rdaddr, &sk->addr[didx], sk->af); + pnl->rdport = sk->port[didx]; + } else + error = ENOENT; + } + break; + } + + case DIOCSETTIMEOUT: { + struct pfioc_tm *pt = (struct pfioc_tm *)addr; + int old; + + if (pt->timeout < 0 || pt->timeout >= PFTM_MAX || + pt->seconds < 0) { + error = EINVAL; + break; + } + PF_RULES_WLOCK(); + old = V_pf_default_rule.timeout[pt->timeout]; + if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0) + pt->seconds = 1; + V_pf_default_rule.timeout[pt->timeout] = pt->seconds; + if (pt->timeout == PFTM_INTERVAL && pt->seconds < old) + wakeup(pf_purge_thread); + pt->seconds = old; + PF_RULES_WUNLOCK(); + break; + } + + case DIOCGETTIMEOUT: { + struct pfioc_tm *pt = (struct pfioc_tm *)addr; + + if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) { + error = EINVAL; + break; + } + PF_RULES_RLOCK(); + pt->seconds = V_pf_default_rule.timeout[pt->timeout]; + PF_RULES_RUNLOCK(); + break; + } + + case DIOCGETLIMIT: { + struct pfioc_limit *pl = (struct pfioc_limit *)addr; + + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { + error = EINVAL; + break; + } + PF_RULES_RLOCK(); + pl->limit = V_pf_limits[pl->index].limit; + PF_RULES_RUNLOCK(); + break; + } + + case DIOCSETLIMIT: { + struct pfioc_limit *pl = (struct pfioc_limit *)addr; + int old_limit; + + PF_RULES_WLOCK(); + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX || + V_pf_limits[pl->index].zone == NULL) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + uma_zone_set_max(V_pf_limits[pl->index].zone, pl->limit); + old_limit = V_pf_limits[pl->index].limit; + V_pf_limits[pl->index].limit = pl->limit; + pl->limit = old_limit; + PF_RULES_WUNLOCK(); + break; + } + + case DIOCSETDEBUG: { + u_int32_t *level = (u_int32_t *)addr; + + PF_RULES_WLOCK(); + V_pf_status.debug = *level; + PF_RULES_WUNLOCK(); + break; + } + + case DIOCCLRRULECTRS: { + /* obsoleted by DIOCGETRULE with action=PF_GET_CLR_CNTR */ + struct pf_ruleset *ruleset = &pf_main_ruleset; + struct pf_rule *rule; + + PF_RULES_WLOCK(); + TAILQ_FOREACH(rule, + ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) { + rule->evaluations = 0; + rule->packets[0] = rule->packets[1] = 0; + rule->bytes[0] = rule->bytes[1] = 0; + } + PF_RULES_WUNLOCK(); + break; + } + + case DIOCGIFSPEED: { + struct pf_ifspeed *psp = (struct pf_ifspeed *)addr; + struct pf_ifspeed ps; + struct ifnet *ifp; + + if (psp->ifname[0] != 0) { + /* Can we completely trust user-land? */ + strlcpy(ps.ifname, psp->ifname, IFNAMSIZ); + ifp = ifunit(ps.ifname); + if (ifp != NULL) + psp->baudrate = ifp->if_baudrate; + else + error = EINVAL; + } else + error = EINVAL; + break; + } + +#ifdef ALTQ + case DIOCSTARTALTQ: { + struct pf_altq *altq; + + PF_RULES_WLOCK(); + /* enable all altq interfaces on active list */ + TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { + if (altq->qname[0] == 0 && (altq->local_flags & + PFALTQ_FLAG_IF_REMOVED) == 0) { + error = pf_enable_altq(altq); + if (error != 0) + break; + } + } + if (error == 0) + V_pf_altq_running = 1; + PF_RULES_WUNLOCK(); + DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n")); + break; + } + + case DIOCSTOPALTQ: { + struct pf_altq *altq; + + PF_RULES_WLOCK(); + /* disable all altq interfaces on active list */ + TAILQ_FOREACH(altq, V_pf_altqs_active, entries) { + if (altq->qname[0] == 0 && (altq->local_flags & + PFALTQ_FLAG_IF_REMOVED) == 0) { + error = pf_disable_altq(altq); + if (error != 0) + break; + } + } + if (error == 0) + V_pf_altq_running = 0; + PF_RULES_WUNLOCK(); + DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); + break; + } + + case DIOCADDALTQ: { + struct pfioc_altq *pa = (struct pfioc_altq *)addr; + struct pf_altq *altq, *a; + struct ifnet *ifp; + + altq = malloc(sizeof(*altq), M_PFALTQ, M_WAITOK); + bcopy(&pa->altq, altq, sizeof(struct pf_altq)); + altq->local_flags = 0; + + PF_RULES_WLOCK(); + if (pa->ticket != V_ticket_altqs_inactive) { + PF_RULES_WUNLOCK(); + free(altq, M_PFALTQ); + error = EBUSY; + break; + } + + /* + * if this is for a queue, find the discipline and + * copy the necessary fields + */ + if (altq->qname[0] != 0) { + if ((altq->qid = pf_qname2qid(altq->qname)) == 0) { + PF_RULES_WUNLOCK(); + error = EBUSY; + free(altq, M_PFALTQ); + break; + } + altq->altq_disc = NULL; + TAILQ_FOREACH(a, V_pf_altqs_inactive, entries) { + if (strncmp(a->ifname, altq->ifname, + IFNAMSIZ) == 0 && a->qname[0] == 0) { + altq->altq_disc = a->altq_disc; + break; + } + } + } + + if ((ifp = ifunit(altq->ifname)) == NULL) + altq->local_flags |= PFALTQ_FLAG_IF_REMOVED; + else + error = altq_add(altq); + + if (error) { + PF_RULES_WUNLOCK(); + free(altq, M_PFALTQ); + break; + } + + TAILQ_INSERT_TAIL(V_pf_altqs_inactive, altq, entries); + bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCGETALTQS: { + struct pfioc_altq *pa = (struct pfioc_altq *)addr; + struct pf_altq *altq; + + PF_RULES_RLOCK(); + pa->nr = 0; + TAILQ_FOREACH(altq, V_pf_altqs_active, entries) + pa->nr++; + pa->ticket = V_ticket_altqs_active; + PF_RULES_RUNLOCK(); + break; + } + + case DIOCGETALTQ: { + struct pfioc_altq *pa = (struct pfioc_altq *)addr; + struct pf_altq *altq; + u_int32_t nr; + + PF_RULES_RLOCK(); + if (pa->ticket != V_ticket_altqs_active) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + nr = 0; + altq = TAILQ_FIRST(V_pf_altqs_active); + while ((altq != NULL) && (nr < pa->nr)) { + altq = TAILQ_NEXT(altq, entries); + nr++; + } + if (altq == NULL) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + PF_RULES_RUNLOCK(); + break; + } + + case DIOCCHANGEALTQ: + /* CHANGEALTQ not supported yet! */ + error = ENODEV; + break; + + case DIOCGETQSTATS: { + struct pfioc_qstats *pq = (struct pfioc_qstats *)addr; + struct pf_altq *altq; + u_int32_t nr; + int nbytes; + + PF_RULES_RLOCK(); + if (pq->ticket != V_ticket_altqs_active) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + nbytes = pq->nbytes; + nr = 0; + altq = TAILQ_FIRST(V_pf_altqs_active); + while ((altq != NULL) && (nr < pq->nr)) { + altq = TAILQ_NEXT(altq, entries); + nr++; + } + if (altq == NULL) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + + if ((altq->local_flags & PFALTQ_FLAG_IF_REMOVED) != 0) { + PF_RULES_RUNLOCK(); + error = ENXIO; + break; + } + PF_RULES_RUNLOCK(); + error = altq_getqstats(altq, pq->buf, &nbytes); + if (error == 0) { + pq->scheduler = altq->scheduler; + pq->nbytes = nbytes; + } + break; + } +#endif /* ALTQ */ + + case DIOCBEGINADDRS: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + + PF_RULES_WLOCK(); + pf_empty_pool(&V_pf_pabuf); + pp->ticket = ++V_ticket_pabuf; + PF_RULES_WUNLOCK(); + break; + } + + case DIOCADDADDR: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + struct pf_pooladdr *pa; + struct pfi_kif *kif = NULL; + +#ifndef INET + if (pp->af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pp->af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + if (pp->addr.addr.type != PF_ADDR_ADDRMASK && + pp->addr.addr.type != PF_ADDR_DYNIFTL && + pp->addr.addr.type != PF_ADDR_TABLE) { + error = EINVAL; + break; + } + pa = malloc(sizeof(*pa), M_PFRULE, M_WAITOK); + bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); + if (pa->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + PF_RULES_WLOCK(); + if (pp->ticket != V_ticket_pabuf) { + PF_RULES_WUNLOCK(); + if (pa->ifname[0]) + free(kif, PFI_MTYPE); + free(pa, M_PFRULE); + error = EBUSY; + break; + } + if (pa->ifname[0]) { + pa->kif = pfi_kif_attach(kif, pa->ifname); + pfi_kif_ref(pa->kif); + } else + pa->kif = NULL; + if (pa->addr.type == PF_ADDR_DYNIFTL && ((error = + pfi_dynaddr_setup(&pa->addr, pp->af)) != 0)) { + if (pa->ifname[0]) + pfi_kif_unref(pa->kif); + PF_RULES_WUNLOCK(); + free(pa, M_PFRULE); + break; + } + TAILQ_INSERT_TAIL(&V_pf_pabuf, pa, entries); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCGETADDRS: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; + struct pf_pooladdr *pa; + + PF_RULES_RLOCK(); + pp->nr = 0; + pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, + pp->r_num, 0, 1, 0); + if (pool == NULL) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + TAILQ_FOREACH(pa, &pool->list, entries) + pp->nr++; + PF_RULES_RUNLOCK(); + break; + } + + case DIOCGETADDR: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; + struct pf_pooladdr *pa; + u_int32_t nr = 0; + + PF_RULES_RLOCK(); + pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, + pp->r_num, 0, 1, 1); + if (pool == NULL) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + pa = TAILQ_FIRST(&pool->list); + while ((pa != NULL) && (nr < pp->nr)) { + pa = TAILQ_NEXT(pa, entries); + nr++; + } + if (pa == NULL) { + PF_RULES_RUNLOCK(); + error = EBUSY; + break; + } + bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); + pf_addr_copyout(&pp->addr.addr); + PF_RULES_RUNLOCK(); + break; + } + + case DIOCCHANGEADDR: { + struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; + struct pf_pooladdr *oldpa = NULL, *newpa = NULL; + struct pf_ruleset *ruleset; + struct pfi_kif *kif = NULL; + + if (pca->action < PF_CHANGE_ADD_HEAD || + pca->action > PF_CHANGE_REMOVE) { + error = EINVAL; + break; + } + if (pca->addr.addr.type != PF_ADDR_ADDRMASK && + pca->addr.addr.type != PF_ADDR_DYNIFTL && + pca->addr.addr.type != PF_ADDR_TABLE) { + error = EINVAL; + break; + } + + if (pca->action != PF_CHANGE_REMOVE) { +#ifndef INET + if (pca->af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pca->af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + newpa = malloc(sizeof(*newpa), M_PFRULE, M_WAITOK); + bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); + if (newpa->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + } + +#define ERROUT(x) { error = (x); goto DIOCCHANGEADDR_error; } + PF_RULES_WLOCK(); + ruleset = pf_find_ruleset(pca->anchor); + if (ruleset == NULL) + ERROUT(EBUSY); + + pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action, + pca->r_num, pca->r_last, 1, 1); + if (pool == NULL) + ERROUT(EBUSY); + + if (pca->action != PF_CHANGE_REMOVE) { + if (newpa->ifname[0]) { + newpa->kif = pfi_kif_attach(kif, newpa->ifname); + pfi_kif_ref(newpa->kif); + } else + newpa->kif = NULL; + + switch (newpa->addr.type) { + case PF_ADDR_DYNIFTL: + error = pfi_dynaddr_setup(&newpa->addr, + pca->af); + break; + case PF_ADDR_TABLE: + newpa->addr.p.tbl = pfr_attach_table(ruleset, + newpa->addr.v.tblname); + if (newpa->addr.p.tbl == NULL) + error = ENOMEM; + break; + } + if (error) { + if (newpa->kif) + pfi_kif_unref(newpa->kif); + PF_RULES_WUNLOCK(); + free(newpa, M_PFRULE); + break; + } + } + + if (pca->action == PF_CHANGE_ADD_HEAD) + oldpa = TAILQ_FIRST(&pool->list); + else if (pca->action == PF_CHANGE_ADD_TAIL) + oldpa = TAILQ_LAST(&pool->list, pf_palist); + else { + int i = 0; + + oldpa = TAILQ_FIRST(&pool->list); + while ((oldpa != NULL) && (i < pca->nr)) { + oldpa = TAILQ_NEXT(oldpa, entries); + i++; + } + if (oldpa == NULL) { + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + } + + if (pca->action == PF_CHANGE_REMOVE) { + TAILQ_REMOVE(&pool->list, oldpa, entries); + switch (oldpa->addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(oldpa->addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(oldpa->addr.p.tbl); + break; + } + if (oldpa->kif) + pfi_kif_unref(oldpa->kif); + free(oldpa, M_PFRULE); + } else { + if (oldpa == NULL) + TAILQ_INSERT_TAIL(&pool->list, newpa, entries); + else if (pca->action == PF_CHANGE_ADD_HEAD || + pca->action == PF_CHANGE_ADD_BEFORE) + TAILQ_INSERT_BEFORE(oldpa, newpa, entries); + else + TAILQ_INSERT_AFTER(&pool->list, oldpa, + newpa, entries); + } + + pool->cur = TAILQ_FIRST(&pool->list); + PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, + pca->af); + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCCHANGEADDR_error: + PF_RULES_WUNLOCK(); + if (newpa != NULL) + free(newpa, M_PFRULE); + if (kif != NULL) + free(kif, PFI_MTYPE); + break; + } + + case DIOCGETRULESETS: { + struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; + struct pf_ruleset *ruleset; + struct pf_anchor *anchor; + + PF_RULES_RLOCK(); + pr->path[sizeof(pr->path) - 1] = 0; + if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { + PF_RULES_RUNLOCK(); + error = ENOENT; + break; + } + pr->nr = 0; + if (ruleset->anchor == NULL) { + /* XXX kludge for pf_main_ruleset */ + RB_FOREACH(anchor, pf_anchor_global, &V_pf_anchors) + if (anchor->parent == NULL) + pr->nr++; + } else { + RB_FOREACH(anchor, pf_anchor_node, + &ruleset->anchor->children) + pr->nr++; + } + PF_RULES_RUNLOCK(); + break; + } + + case DIOCGETRULESET: { + struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; + struct pf_ruleset *ruleset; + struct pf_anchor *anchor; + u_int32_t nr = 0; + + PF_RULES_RLOCK(); + pr->path[sizeof(pr->path) - 1] = 0; + if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { + PF_RULES_RUNLOCK(); + error = ENOENT; + break; + } + pr->name[0] = 0; + if (ruleset->anchor == NULL) { + /* XXX kludge for pf_main_ruleset */ + RB_FOREACH(anchor, pf_anchor_global, &V_pf_anchors) + if (anchor->parent == NULL && nr++ == pr->nr) { + strlcpy(pr->name, anchor->name, + sizeof(pr->name)); + break; + } + } else { + RB_FOREACH(anchor, pf_anchor_node, + &ruleset->anchor->children) + if (nr++ == pr->nr) { + strlcpy(pr->name, anchor->name, + sizeof(pr->name)); + break; + } + } + if (!pr->name[0]) + error = EBUSY; + PF_RULES_RUNLOCK(); + break; + } + + case DIOCRCLRTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != 0) { + error = ENODEV; + break; + } + PF_RULES_WLOCK(); + error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, + io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCRADDTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_add_tables(pfrts, io->pfrio_size, + &io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); + break; + } + + case DIOCRDELTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_del_tables(pfrts, io->pfrio_size, + &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); + break; + } + + case DIOCRGETTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + error = pfr_get_tables(&io->pfrio_table, pfrts, + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfrts, io->pfrio_buffer, totlen); + free(pfrts, M_TEMP); + break; + } + + case DIOCRGETTSTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_tstats *pfrtstats; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_tstats)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_tstats); + pfrtstats = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_WLOCK(); + error = pfr_get_tstats(&io->pfrio_table, pfrtstats, + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0) + error = copyout(pfrtstats, io->pfrio_buffer, totlen); + free(pfrtstats, M_TEMP); + break; + } + + case DIOCRCLRTSTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_clr_tstats(pfrts, io->pfrio_size, + &io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); + break; + } + + case DIOCRSETTFLAGS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_table *pfrts; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfrts, totlen); + if (error) { + free(pfrts, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_set_tflags(pfrts, io->pfrio_size, + io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, + &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfrts, M_TEMP); + break; + } + + case DIOCRCLRADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != 0) { + error = ENODEV; + break; + } + PF_RULES_WLOCK(); + error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel, + io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCRADDADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_add_addrs(&io->pfrio_table, pfras, + io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags | + PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); + break; + } + + case DIOCRDELADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_del_addrs(&io->pfrio_table, pfras, + io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags | + PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); + break; + } + + case DIOCRSETADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = (io->pfrio_size + io->pfrio_size2) * + sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_set_addrs(&io->pfrio_table, pfras, + io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, + &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags | + PFR_FLAG_USERIOCTL, 0); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); + break; + } + + case DIOCRGETADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + error = pfr_get_addrs(&io->pfrio_table, pfras, + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); + break; + } + + case DIOCRGETASTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_astats *pfrastats; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_astats)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_astats); + pfrastats = malloc(totlen, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + error = pfr_get_astats(&io->pfrio_table, pfrastats, + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfrastats, io->pfrio_buffer, totlen); + free(pfrastats, M_TEMP); + break; + } + + case DIOCRCLRASTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_clr_astats(&io->pfrio_table, pfras, + io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags | + PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); + break; + } + + case DIOCRTSTADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_RLOCK(); + error = pfr_tst_addrs(&io->pfrio_table, pfras, + io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags | + PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error == 0) + error = copyout(pfras, io->pfrio_buffer, totlen); + free(pfras, M_TEMP); + break; + } + + case DIOCRINADEFINE: { + struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfr_addr *pfras; + size_t totlen; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + totlen = io->pfrio_size * sizeof(struct pfr_addr); + pfras = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->pfrio_buffer, pfras, totlen); + if (error) { + free(pfras, M_TEMP); + break; + } + PF_RULES_WLOCK(); + error = pfr_ina_define(&io->pfrio_table, pfras, + io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr, + io->pfrio_ticket, io->pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_WUNLOCK(); + free(pfras, M_TEMP); + break; + } + + case DIOCOSFPADD: { + struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; + PF_RULES_WLOCK(); + error = pf_osfp_add(io); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCOSFPGET: { + struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; + PF_RULES_RLOCK(); + error = pf_osfp_get(io); + PF_RULES_RUNLOCK(); + break; + } + + case DIOCXBEGIN: { + struct pfioc_trans *io = (struct pfioc_trans *)addr; + struct pfioc_trans_e *ioes, *ioe; + size_t totlen; + int i; + + if (io->esize != sizeof(*ioe)) { + error = ENODEV; + break; + } + totlen = sizeof(struct pfioc_trans_e) * io->size; + ioes = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->array, ioes, totlen); + if (error) { + free(ioes, M_TEMP); + break; + } + PF_RULES_WLOCK(); + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { + switch (ioe->rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (ioe->anchor[0]) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EINVAL; + goto fail; + } + if ((error = pf_begin_altq(&ioe->ticket))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_begin(&table, + &ioe->ticket, NULL, 0))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; + } + break; + } + default: + if ((error = pf_begin_rules(&ioe->ticket, + ioe->rs_num, ioe->anchor))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; + } + break; + } + } + PF_RULES_WUNLOCK(); + error = copyout(ioes, io->array, totlen); + free(ioes, M_TEMP); + break; + } + + case DIOCXROLLBACK: { + struct pfioc_trans *io = (struct pfioc_trans *)addr; + struct pfioc_trans_e *ioe, *ioes; + size_t totlen; + int i; + + if (io->esize != sizeof(*ioe)) { + error = ENODEV; + break; + } + totlen = sizeof(struct pfioc_trans_e) * io->size; + ioes = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->array, ioes, totlen); + if (error) { + free(ioes, M_TEMP); + break; + } + PF_RULES_WLOCK(); + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { + switch (ioe->rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (ioe->anchor[0]) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EINVAL; + goto fail; + } + if ((error = pf_rollback_altq(ioe->ticket))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; /* really bad */ + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_rollback(&table, + ioe->ticket, NULL, 0))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; /* really bad */ + } + break; + } + default: + if ((error = pf_rollback_rules(ioe->ticket, + ioe->rs_num, ioe->anchor))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; /* really bad */ + } + break; + } + } + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + break; + } + + case DIOCXCOMMIT: { + struct pfioc_trans *io = (struct pfioc_trans *)addr; + struct pfioc_trans_e *ioe, *ioes; + struct pf_ruleset *rs; + size_t totlen; + int i; + + if (io->esize != sizeof(*ioe)) { + error = ENODEV; + break; + } + totlen = sizeof(struct pfioc_trans_e) * io->size; + ioes = malloc(totlen, M_TEMP, M_WAITOK); + error = copyin(io->array, ioes, totlen); + if (error) { + free(ioes, M_TEMP); + break; + } + PF_RULES_WLOCK(); + /* First makes sure everything will succeed. */ + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { + switch (ioe->rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (ioe->anchor[0]) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EINVAL; + goto fail; + } + if (!V_altqs_inactive_open || ioe->ticket != + V_ticket_altqs_inactive) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EBUSY; + goto fail; + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + rs = pf_find_ruleset(ioe->anchor); + if (rs == NULL || !rs->topen || ioe->ticket != + rs->tticket) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EBUSY; + goto fail; + } + break; + default: + if (ioe->rs_num < 0 || ioe->rs_num >= + PF_RULESET_MAX) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EINVAL; + goto fail; + } + rs = pf_find_ruleset(ioe->anchor); + if (rs == NULL || + !rs->rules[ioe->rs_num].inactive.open || + rs->rules[ioe->rs_num].inactive.ticket != + ioe->ticket) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + error = EBUSY; + goto fail; + } + break; + } + } + /* Now do the commit - no errors should happen here. */ + for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { + switch (ioe->rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if ((error = pf_commit_altq(ioe->ticket))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; /* really bad */ + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_commit(&table, + ioe->ticket, NULL, NULL, 0))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; /* really bad */ + } + break; + } + default: + if ((error = pf_commit_rules(ioe->ticket, + ioe->rs_num, ioe->anchor))) { + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + goto fail; /* really bad */ + } + break; + } + } + PF_RULES_WUNLOCK(); + free(ioes, M_TEMP); + break; + } + + case DIOCGETSRCNODES: { + struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr; + struct pf_srchash *sh; + struct pf_src_node *n, *p, *pstore; + uint32_t i, nr = 0; + + if (psn->psn_len == 0) { + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) + nr++; + PF_HASHROW_UNLOCK(sh); + } + psn->psn_len = sizeof(struct pf_src_node) * nr; + break; + } + + p = pstore = malloc(psn->psn_len, M_TEMP, M_WAITOK); + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) { + int secs = time_uptime, diff; + + if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len) + break; + + bcopy(n, p, sizeof(struct pf_src_node)); + if (n->rule.ptr != NULL) + p->rule.nr = n->rule.ptr->nr; + p->creation = secs - p->creation; + if (p->expire > secs) + p->expire -= secs; + else + p->expire = 0; + + /* Adjust the connection rate estimate. */ + diff = secs - n->conn_rate.last; + if (diff >= n->conn_rate.seconds) + p->conn_rate.count = 0; + else + p->conn_rate.count -= + n->conn_rate.count * diff / + n->conn_rate.seconds; + p++; + nr++; + } + PF_HASHROW_UNLOCK(sh); + } + error = copyout(pstore, psn->psn_src_nodes, + sizeof(struct pf_src_node) * nr); + if (error) { + free(pstore, M_TEMP); + break; + } + psn->psn_len = sizeof(struct pf_src_node) * nr; + free(pstore, M_TEMP); + break; + } + + case DIOCCLRSRCNODES: { + + pf_clear_srcnodes(NULL); + pf_purge_expired_src_nodes(); + V_pf_status.src_nodes = 0; + break; + } + + case DIOCKILLSRCNODES: { + struct pfioc_src_node_kill *psnk = + (struct pfioc_src_node_kill *)addr; + struct pf_srchash *sh; + struct pf_src_node *sn; + u_int i, killed = 0; + + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + /* + * XXXGL: we don't ever acquire sources hash lock + * but if we ever do, the below call to pf_clear_srcnodes() + * would lead to a LOR. + */ + PF_HASHROW_LOCK(sh); + LIST_FOREACH(sn, &sh->nodes, entry) + if (PF_MATCHA(psnk->psnk_src.neg, + &psnk->psnk_src.addr.v.a.addr, + &psnk->psnk_src.addr.v.a.mask, + &sn->addr, sn->af) && + PF_MATCHA(psnk->psnk_dst.neg, + &psnk->psnk_dst.addr.v.a.addr, + &psnk->psnk_dst.addr.v.a.mask, + &sn->raddr, sn->af)) { + /* Handle state to src_node linkage */ + if (sn->states != 0) + pf_clear_srcnodes(sn); + sn->expire = 1; + killed++; + } + PF_HASHROW_UNLOCK(sh); + } + + if (killed > 0) + pf_purge_expired_src_nodes(); + + psnk->psnk_killed = killed; + break; + } + + case DIOCSETHOSTID: { + u_int32_t *hostid = (u_int32_t *)addr; + + PF_RULES_WLOCK(); + if (*hostid == 0) + V_pf_status.hostid = arc4random(); + else + V_pf_status.hostid = *hostid; + PF_RULES_WUNLOCK(); + break; + } + + case DIOCOSFPFLUSH: + PF_RULES_WLOCK(); + pf_osfp_flush(); + PF_RULES_WUNLOCK(); + break; + + case DIOCIGETIFACES: { + struct pfioc_iface *io = (struct pfioc_iface *)addr; + struct pfi_kif *ifstore; + size_t bufsiz; + + if (io->pfiio_esize != sizeof(struct pfi_kif)) { + error = ENODEV; + break; + } + + bufsiz = io->pfiio_size * sizeof(struct pfi_kif); + ifstore = malloc(bufsiz, M_TEMP, M_WAITOK); + PF_RULES_RLOCK(); + pfi_get_ifaces(io->pfiio_name, ifstore, &io->pfiio_size); + PF_RULES_RUNLOCK(); + error = copyout(ifstore, io->pfiio_buffer, bufsiz); + free(ifstore, M_TEMP); + break; + } + + case DIOCSETIFFLAG: { + struct pfioc_iface *io = (struct pfioc_iface *)addr; + + PF_RULES_WLOCK(); + error = pfi_set_flags(io->pfiio_name, io->pfiio_flags); + PF_RULES_WUNLOCK(); + break; + } + + case DIOCCLRIFFLAG: { + struct pfioc_iface *io = (struct pfioc_iface *)addr; + + PF_RULES_WLOCK(); + error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags); + PF_RULES_WUNLOCK(); + break; + } + + default: + error = ENODEV; + break; + } +fail: + CURVNET_RESTORE(); + + return (error); +} + +void +pfsync_state_export(struct pfsync_state *sp, struct pf_state *st) +{ + bzero(sp, sizeof(struct pfsync_state)); + + /* copy from state key */ + sp->key[PF_SK_WIRE].addr[0] = st->key[PF_SK_WIRE]->addr[0]; + sp->key[PF_SK_WIRE].addr[1] = st->key[PF_SK_WIRE]->addr[1]; + sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0]; + sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1]; + sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0]; + sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1]; + sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0]; + sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1]; + sp->proto = st->key[PF_SK_WIRE]->proto; + sp->af = st->key[PF_SK_WIRE]->af; + + /* copy from state */ + strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname)); + bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); + sp->creation = htonl(time_uptime - st->creation); + sp->expire = pf_state_expires(st); + if (sp->expire <= time_uptime) + sp->expire = htonl(0); + else + sp->expire = htonl(sp->expire - time_uptime); + + sp->direction = st->direction; + sp->log = st->log; + sp->timeout = st->timeout; + sp->state_flags = st->state_flags; + if (st->src_node) + sp->sync_flags |= PFSYNC_FLAG_SRCNODE; + if (st->nat_src_node) + sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE; + + sp->id = st->id; + sp->creatorid = st->creatorid; + pf_state_peer_hton(&st->src, &sp->src); + pf_state_peer_hton(&st->dst, &sp->dst); + + if (st->rule.ptr == NULL) + sp->rule = htonl(-1); + else + sp->rule = htonl(st->rule.ptr->nr); + if (st->anchor.ptr == NULL) + sp->anchor = htonl(-1); + else + sp->anchor = htonl(st->anchor.ptr->nr); + if (st->nat_rule.ptr == NULL) + sp->nat_rule = htonl(-1); + else + sp->nat_rule = htonl(st->nat_rule.ptr->nr); + + pf_state_counter_hton(st->packets[0], sp->packets[0]); + pf_state_counter_hton(st->packets[1], sp->packets[1]); + pf_state_counter_hton(st->bytes[0], sp->bytes[0]); + pf_state_counter_hton(st->bytes[1], sp->bytes[1]); + +} + +static void +pf_tbladdr_copyout(struct pf_addr_wrap *aw) +{ + struct pfr_ktable *kt; + + KASSERT(aw->type == PF_ADDR_TABLE, ("%s: type %u", __func__, aw->type)); + + kt = aw->p.tbl; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + aw->p.tbl = NULL; + aw->p.tblcnt = (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) ? + kt->pfrkt_cnt : -1; +} + +/* + * XXX - Check for version missmatch!!! + */ +static void +pf_clear_states(void) +{ + struct pf_state *s; + u_int i; + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; +relock: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + s->timeout = PFTM_PURGE; + /* Don't send out individual delete messages. */ + s->sync_state = PFSTATE_NOSYNC; + pf_unlink_state(s, PF_ENTER_LOCKED); + goto relock; + } + PF_HASHROW_UNLOCK(ih); + } +} + +static int +pf_clear_tables(void) +{ + struct pfioc_table io; + int error; + + bzero(&io, sizeof(io)); + + error = pfr_clr_tables(&io.pfrio_table, &io.pfrio_ndel, + io.pfrio_flags); + + return (error); +} + +static void +pf_clear_srcnodes(struct pf_src_node *n) +{ + struct pf_state *s; + int i; + + for (i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (n == NULL || n == s->src_node) + s->src_node = NULL; + if (n == NULL || n == s->nat_src_node) + s->nat_src_node = NULL; + } + PF_HASHROW_UNLOCK(ih); + } + + if (n == NULL) { + struct pf_srchash *sh; + + for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask; + i++, sh++) { + PF_HASHROW_LOCK(sh); + LIST_FOREACH(n, &sh->nodes, entry) { + n->expire = 1; + n->states = 0; + } + PF_HASHROW_UNLOCK(sh); + } + } else { + /* XXX: hash slot should already be locked here. */ + n->expire = 1; + n->states = 0; + } +} +/* + * XXX - Check for version missmatch!!! + */ + +/* + * Duplicate pfctl -Fa operation to get rid of as much as we can. + */ +static int +shutdown_pf(void) +{ + int error = 0; + u_int32_t t[5]; + char nn = '\0'; + + V_pf_status.running = 0; + do { + if ((error = pf_begin_rules(&t[0], PF_RULESET_SCRUB, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: SCRUB\n")); + break; + } + if ((error = pf_begin_rules(&t[1], PF_RULESET_FILTER, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: FILTER\n")); + break; /* XXX: rollback? */ + } + if ((error = pf_begin_rules(&t[2], PF_RULESET_NAT, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: NAT\n")); + break; /* XXX: rollback? */ + } + if ((error = pf_begin_rules(&t[3], PF_RULESET_BINAT, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: BINAT\n")); + break; /* XXX: rollback? */ + } + if ((error = pf_begin_rules(&t[4], PF_RULESET_RDR, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: RDR\n")); + break; /* XXX: rollback? */ + } + + /* XXX: these should always succeed here */ + pf_commit_rules(t[0], PF_RULESET_SCRUB, &nn); + pf_commit_rules(t[1], PF_RULESET_FILTER, &nn); + pf_commit_rules(t[2], PF_RULESET_NAT, &nn); + pf_commit_rules(t[3], PF_RULESET_BINAT, &nn); + pf_commit_rules(t[4], PF_RULESET_RDR, &nn); + + if ((error = pf_clear_tables()) != 0) + break; + +#ifdef ALTQ + if ((error = pf_begin_altq(&t[0])) != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: ALTQ\n")); + break; + } + pf_commit_altq(t[0]); +#endif + + pf_clear_states(); + + pf_clear_srcnodes(NULL); + + /* status does not use malloced mem so no need to cleanup */ + /* fingerprints and interfaces have thier own cleanup code */ + } while(0); + + return (error); +} + +#ifdef INET +static int +pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, + struct inpcb *inp) +{ + /* + * XXX Wed Jul 9 22:03:16 2003 UTC + * OpenBSD has changed its byte ordering convention on ip_len/ip_off + * in network stack. OpenBSD's network stack have converted + * ip_len/ip_off to host byte order frist as FreeBSD. + * Now this is not true anymore , so we should convert back to network + * byte order. + */ + struct ip *h = NULL; + int chk; + + if ((*m)->m_pkthdr.len >= (int)sizeof(struct ip)) { + /* if m_pkthdr.len is less than ip header, pf will handle. */ + h = mtod(*m, struct ip *); + HTONS(h->ip_len); + HTONS(h->ip_off); + } + CURVNET_SET(ifp->if_vnet); + chk = pf_test(PF_IN, ifp, m, inp); + CURVNET_RESTORE(); + if (chk && *m) { + m_freem(*m); + *m = NULL; + } + if (*m != NULL) { + /* pf_test can change ip header location */ + h = mtod(*m, struct ip *); + NTOHS(h->ip_len); + NTOHS(h->ip_off); + } + return chk; +} + +static int +pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, + struct inpcb *inp) +{ + /* + * XXX Wed Jul 9 22:03:16 2003 UTC + * OpenBSD has changed its byte ordering convention on ip_len/ip_off + * in network stack. OpenBSD's network stack have converted + * ip_len/ip_off to host byte order frist as FreeBSD. + * Now this is not true anymore , so we should convert back to network + * byte order. + */ + struct ip *h = NULL; + int chk; + + /* We need a proper CSUM befor we start (s. OpenBSD ip_output) */ + if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + in_delayed_cksum(*m); + (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + } + if ((*m)->m_pkthdr.len >= (int)sizeof(*h)) { + /* if m_pkthdr.len is less than ip header, pf will handle. */ + h = mtod(*m, struct ip *); + HTONS(h->ip_len); + HTONS(h->ip_off); + } + CURVNET_SET(ifp->if_vnet); + chk = pf_test(PF_OUT, ifp, m, inp); + CURVNET_RESTORE(); + if (chk && *m) { + m_freem(*m); + *m = NULL; + } + if (*m != NULL) { + /* pf_test can change ip header location */ + h = mtod(*m, struct ip *); + NTOHS(h->ip_len); + NTOHS(h->ip_off); + } + return chk; +} +#endif + +#ifdef INET6 +static int +pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, + struct inpcb *inp) +{ + + /* + * IPv6 is not affected by ip_len/ip_off byte order changes. + */ + int chk; + + /* + * In case of loopback traffic IPv6 uses the real interface in + * order to support scoped addresses. In order to support stateful + * filtering we have change this to lo0 as it is the case in IPv4. + */ + CURVNET_SET(ifp->if_vnet); + chk = pf_test6(PF_IN, (*m)->m_flags & M_LOOP ? V_loif : ifp, m, inp); + CURVNET_RESTORE(); + if (chk && *m) { + m_freem(*m); + *m = NULL; + } + return chk; +} + +static int +pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, + struct inpcb *inp) +{ + /* + * IPv6 does not affected ip_len/ip_off byte order changes. + */ + int chk; + + /* We need a proper CSUM before we start (s. OpenBSD ip_output) */ + if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { +#ifdef INET + /* XXX-BZ copy&paste error from r126261? */ + in_delayed_cksum(*m); +#endif + (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + } + CURVNET_SET(ifp->if_vnet); + chk = pf_test6(PF_OUT, ifp, m, inp); + CURVNET_RESTORE(); + if (chk && *m) { + m_freem(*m); + *m = NULL; + } + return chk; +} +#endif /* INET6 */ + +static int +hook_pf(void) +{ +#ifdef INET + struct pfil_head *pfh_inet; +#endif +#ifdef INET6 + struct pfil_head *pfh_inet6; +#endif + + if (V_pf_pfil_hooked) + return (0); + +#ifdef INET + pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); + if (pfh_inet == NULL) + return (ESRCH); /* XXX */ + pfil_add_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); + pfil_add_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); +#endif +#ifdef INET6 + pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (pfh_inet6 == NULL) { +#ifdef INET + pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, + pfh_inet); + pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, + pfh_inet); +#endif + return (ESRCH); /* XXX */ + } + pfil_add_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); + pfil_add_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); +#endif + + V_pf_pfil_hooked = 1; + return (0); +} + +static int +dehook_pf(void) +{ +#ifdef INET + struct pfil_head *pfh_inet; +#endif +#ifdef INET6 + struct pfil_head *pfh_inet6; +#endif + + if (V_pf_pfil_hooked == 0) + return (0); + +#ifdef INET + pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); + if (pfh_inet == NULL) + return (ESRCH); /* XXX */ + pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, + pfh_inet); + pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, + pfh_inet); +#endif +#ifdef INET6 + pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (pfh_inet6 == NULL) + return (ESRCH); /* XXX */ + pfil_remove_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, + pfh_inet6); + pfil_remove_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, + pfh_inet6); +#endif + + V_pf_pfil_hooked = 0; + return (0); +} + +static int +pf_load(void) +{ + int error; + + VNET_ITERATOR_DECL(vnet_iter); + + VNET_LIST_RLOCK(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + V_pf_pfil_hooked = 0; + V_pf_end_threads = 0; + TAILQ_INIT(&V_pf_tags); + TAILQ_INIT(&V_pf_qids); + CURVNET_RESTORE(); + } + VNET_LIST_RUNLOCK(); + + rw_init(&pf_rules_lock, "pf rulesets"); + + pf_dev = make_dev(&pf_cdevsw, 0, 0, 0, 0600, PF_NAME); + if ((error = pfattach()) != 0) + return (error); + + return (0); +} + +static int +pf_unload(void) +{ + int error = 0; + + PF_RULES_WLOCK(); + V_pf_status.running = 0; + PF_RULES_WUNLOCK(); + swi_remove(V_pf_swi_cookie); + error = dehook_pf(); + if (error) { + /* + * Should not happen! + * XXX Due to error code ESRCH, kldunload will show + * a message like 'No such process'. + */ + printf("%s : pfil unregisteration fail\n", __FUNCTION__); + return error; + } + PF_RULES_WLOCK(); + shutdown_pf(); + V_pf_end_threads = 1; + while (V_pf_end_threads < 2) { + wakeup_one(pf_purge_thread); + rw_sleep(pf_purge_thread, &pf_rules_lock, 0, "pftmo", 0); + } + pf_normalize_cleanup(); + pfi_cleanup(); + pfr_cleanup(); + pf_osfp_flush(); + pf_cleanup(); + PF_RULES_WUNLOCK(); + destroy_dev(pf_dev); + rw_destroy(&pf_rules_lock); + + return (error); +} + +static int +pf_modevent(module_t mod, int type, void *data) +{ + int error = 0; + + switch(type) { + case MOD_LOAD: + error = pf_load(); + break; + case MOD_QUIESCE: + /* + * Module should not be unloaded due to race conditions. + */ + error = EPERM; + break; + case MOD_UNLOAD: + error = pf_unload(); + break; + default: + error = EINVAL; + break; + } + + return (error); +} + +static moduledata_t pf_mod = { + "pf", + pf_modevent, + 0 +}; + +DECLARE_MODULE(pf, pf_mod, SI_SUB_PSEUDO, SI_ORDER_FIRST); +MODULE_VERSION(pf, PF_MODVER); diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c new file mode 100644 index 0000000000000..5b47852a5d1b4 --- /dev/null +++ b/sys/netpfil/pf/pf_lb.c @@ -0,0 +1,663 @@ +/* $OpenBSD: pf_lb.c,v 1.2 2009/02/12 02:13:15 sthen Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002 - 2008 Henning Brauer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_pf.h" +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include + +#include +#include +#include +#include + +#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x + +static void pf_hash(struct pf_addr *, struct pf_addr *, + struct pf_poolhashkey *, sa_family_t); +static struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, + int, int, struct pfi_kif *, + struct pf_addr *, u_int16_t, struct pf_addr *, + u_int16_t, int); +static int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, + struct pf_addr *, struct pf_addr *, u_int16_t, + struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, + struct pf_src_node **); + +#define mix(a,b,c) \ + do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ + } while (0) + +/* + * hash function based on bridge_hash in if_bridge.c + */ +static void +pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, + struct pf_poolhashkey *key, sa_family_t af) +{ + u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0]; + + switch (af) { +#ifdef INET + case AF_INET: + a += inaddr->addr32[0]; + b += key->key32[1]; + mix(a, b, c); + hash->addr32[0] = c + key->key32[2]; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + a += inaddr->addr32[0]; + b += inaddr->addr32[2]; + mix(a, b, c); + hash->addr32[0] = c; + a += inaddr->addr32[1]; + b += inaddr->addr32[3]; + c += key->key32[1]; + mix(a, b, c); + hash->addr32[1] = c; + a += inaddr->addr32[2]; + b += inaddr->addr32[1]; + c += key->key32[2]; + mix(a, b, c); + hash->addr32[2] = c; + a += inaddr->addr32[3]; + b += inaddr->addr32[0]; + c += key->key32[3]; + mix(a, b, c); + hash->addr32[3] = c; + break; +#endif /* INET6 */ + } +} + +static struct pf_rule * +pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, + int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, + struct pf_addr *daddr, u_int16_t dport, int rs_num) +{ + struct pf_rule *r, *rm = NULL; + struct pf_ruleset *ruleset = NULL; + int tag = -1; + int rtableid = -1; + int asd = 0; + + r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr); + while (r && rm == NULL) { + struct pf_rule_addr *src = NULL, *dst = NULL; + struct pf_addr_wrap *xdst = NULL; + + if (r->action == PF_BINAT && direction == PF_IN) { + src = &r->dst; + if (r->rpool.cur != NULL) + xdst = &r->rpool.cur->addr; + } else { + src = &r->src; + dst = &r->dst; + } + + r->evaluations++; + if (pfi_kif_match(r->kif, kif) == r->ifnot) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != pd->af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&src->addr, saddr, pd->af, + src->neg, kif, M_GETFIB(m))) + r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR : + PF_SKIP_DST_ADDR].ptr; + else if (src->port_op && !pf_match_port(src->port_op, + src->port[0], src->port[1], sport)) + r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT : + PF_SKIP_DST_PORT].ptr; + else if (dst != NULL && + PF_MISMATCHAW(&dst->addr, daddr, pd->af, dst->neg, NULL, + M_GETFIB(m))) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, pd->af, + 0, NULL, M_GETFIB(m))) + r = TAILQ_NEXT(r, entries); + else if (dst != NULL && dst->port_op && + !pf_match_port(dst->port_op, dst->port[0], + dst->port[1], dport)) + r = r->skip[PF_SKIP_DST_PORT].ptr; + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) + r = TAILQ_NEXT(r, entries); + else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != + IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m, + off, pd->hdr.tcp), r->os_fingerprint))) + r = TAILQ_NEXT(r, entries); + else { + if (r->tag) + tag = r->tag; + if (r->rtableid >= 0) + rtableid = r->rtableid; + if (r->anchor == NULL) { + rm = r; + } else + pf_step_into_anchor(&asd, &ruleset, rs_num, + &r, NULL, NULL); + } + if (r == NULL) + pf_step_out_of_anchor(&asd, &ruleset, rs_num, &r, + NULL, NULL); + } + + if (tag > 0 && pf_tag_packet(m, pd, tag)) + return (NULL); + if (rtableid >= 0) + M_SETFIB(m, rtableid); + + if (rm != NULL && (rm->action == PF_NONAT || + rm->action == PF_NORDR || rm->action == PF_NOBINAT)) + return (NULL); + return (rm); +} + +static int +pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, + struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, + struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, + struct pf_src_node **sn) +{ + struct pf_state_key_cmp key; + struct pf_addr init_addr; + u_int16_t cut; + + bzero(&init_addr, sizeof(init_addr)); + if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) + return (1); + + if (proto == IPPROTO_ICMP) { + low = 1; + high = 65535; + } + + do { + key.af = af; + key.proto = proto; + PF_ACPY(&key.addr[1], daddr, key.af); + PF_ACPY(&key.addr[0], naddr, key.af); + key.port[1] = dport; + + /* + * port search; start random, step; + * similar 2 portloop in in_pcbbind + */ + if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP || + proto == IPPROTO_ICMP)) { + key.port[0] = dport; + if (pf_find_state_all(&key, PF_IN, NULL) == NULL) + return (0); + } else if (low == 0 && high == 0) { + key.port[0] = *nport; + if (pf_find_state_all(&key, PF_IN, NULL) == NULL) + return (0); + } else if (low == high) { + key.port[0] = htons(low); + if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { + *nport = htons(low); + return (0); + } + } else { + u_int16_t tmp; + + if (low > high) { + tmp = low; + low = high; + high = tmp; + } + /* low < high */ + cut = htonl(arc4random()) % (1 + high - low) + low; + /* low <= cut <= high */ + for (tmp = cut; tmp <= high; ++(tmp)) { + key.port[0] = htons(tmp); + if (pf_find_state_all(&key, PF_IN, NULL) == + NULL) { + *nport = htons(tmp); + return (0); + } + } + for (tmp = cut - 1; tmp >= low; --(tmp)) { + key.port[0] = htons(tmp); + if (pf_find_state_all(&key, PF_IN, NULL) == + NULL) { + *nport = htons(tmp); + return (0); + } + } + } + + switch (r->rpool.opts & PF_POOL_TYPEMASK) { + case PF_POOL_RANDOM: + case PF_POOL_ROUNDROBIN: + if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) + return (1); + break; + case PF_POOL_NONE: + case PF_POOL_SRCHASH: + case PF_POOL_BITMASK: + default: + return (1); + } + } while (! PF_AEQ(&init_addr, naddr, af) ); + return (1); /* none available */ +} + +int +pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, + struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) +{ + struct pf_pool *rpool = &r->rpool; + struct pf_addr *raddr = NULL, *rmask = NULL; + + if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && + (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { + *sn = pf_find_src_node(saddr, r, af, 0); + if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { + PF_ACPY(naddr, &(*sn)->raddr, af); + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf_map_addr: src tracking maps "); + pf_print_host(saddr, 0, af); + printf(" to "); + pf_print_host(naddr, 0, af); + printf("\n"); + } + return (0); + } + } + + if (rpool->cur->addr.type == PF_ADDR_NOROUTE) + return (1); + if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { + switch (af) { +#ifdef INET + case AF_INET: + if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && + (rpool->opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) + return (1); + raddr = &rpool->cur->addr.p.dyn->pfid_addr4; + rmask = &rpool->cur->addr.p.dyn->pfid_mask4; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 && + (rpool->opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) + return (1); + raddr = &rpool->cur->addr.p.dyn->pfid_addr6; + rmask = &rpool->cur->addr.p.dyn->pfid_mask6; + break; +#endif /* INET6 */ + } + } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { + if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) + return (1); /* unsupported */ + } else { + raddr = &rpool->cur->addr.v.a.addr; + rmask = &rpool->cur->addr.v.a.mask; + } + + switch (rpool->opts & PF_POOL_TYPEMASK) { + case PF_POOL_NONE: + PF_ACPY(naddr, raddr, af); + break; + case PF_POOL_BITMASK: + PF_POOLMASK(naddr, raddr, rmask, saddr, af); + break; + case PF_POOL_RANDOM: + if (init_addr != NULL && PF_AZERO(init_addr, af)) { + switch (af) { +#ifdef INET + case AF_INET: + rpool->counter.addr32[0] = htonl(arc4random()); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (rmask->addr32[3] != 0xffffffff) + rpool->counter.addr32[3] = + htonl(arc4random()); + else + break; + if (rmask->addr32[2] != 0xffffffff) + rpool->counter.addr32[2] = + htonl(arc4random()); + else + break; + if (rmask->addr32[1] != 0xffffffff) + rpool->counter.addr32[1] = + htonl(arc4random()); + else + break; + if (rmask->addr32[0] != 0xffffffff) + rpool->counter.addr32[0] = + htonl(arc4random()); + break; +#endif /* INET6 */ + } + PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); + PF_ACPY(init_addr, naddr, af); + + } else { + PF_AINC(&rpool->counter, af); + PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); + } + break; + case PF_POOL_SRCHASH: + { + unsigned char hash[16]; + + pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); + PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); + break; + } + case PF_POOL_ROUNDROBIN: + { + struct pf_pooladdr *acur = rpool->cur; + + /* + * XXXGL: in the round-robin case we need to store + * the round-robin machine state in the rule, thus + * forwarding thread needs to modify rule. + * + * This is done w/o locking, because performance is assumed + * more important than round-robin precision. + * + * In the simpliest case we just update the "rpool->cur" + * pointer. However, if pool contains tables or dynamic + * addresses, then "tblidx" is also used to store machine + * state. Since "tblidx" is int, concurrent access to it can't + * lead to inconsistence, only to lost of precision. + * + * Things get worse, if table contains not hosts, but + * prefixes. In this case counter also stores machine state, + * and for IPv6 address, counter can't be updated atomically. + * Probably, using round-robin on a table containing IPv6 + * prefixes (or even IPv4) would cause a panic. + */ + + if (rpool->cur->addr.type == PF_ADDR_TABLE) { + if (!pfr_pool_get(rpool->cur->addr.p.tbl, + &rpool->tblidx, &rpool->counter, af)) + goto get_addr; + } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { + if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, + &rpool->tblidx, &rpool->counter, af)) + goto get_addr; + } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) + goto get_addr; + + try_next: + if (TAILQ_NEXT(rpool->cur, entries) == NULL) + rpool->cur = TAILQ_FIRST(&rpool->list); + else + rpool->cur = TAILQ_NEXT(rpool->cur, entries); + if (rpool->cur->addr.type == PF_ADDR_TABLE) { + rpool->tblidx = -1; + if (pfr_pool_get(rpool->cur->addr.p.tbl, + &rpool->tblidx, &rpool->counter, af)) { + /* table contains no address of type 'af' */ + if (rpool->cur != acur) + goto try_next; + return (1); + } + } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { + rpool->tblidx = -1; + if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, + &rpool->tblidx, &rpool->counter, af)) { + /* table contains no address of type 'af' */ + if (rpool->cur != acur) + goto try_next; + return (1); + } + } else { + raddr = &rpool->cur->addr.v.a.addr; + rmask = &rpool->cur->addr.v.a.mask; + PF_ACPY(&rpool->counter, raddr, af); + } + + get_addr: + PF_ACPY(naddr, &rpool->counter, af); + if (init_addr != NULL && PF_AZERO(init_addr, af)) + PF_ACPY(init_addr, naddr, af); + PF_AINC(&rpool->counter, af); + break; + } + } + if (*sn != NULL) + PF_ACPY(&(*sn)->raddr, naddr, af); + + if (V_pf_status.debug >= PF_DEBUG_MISC && + (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { + printf("pf_map_addr: selected address "); + pf_print_host(naddr, 0, af); + printf("\n"); + } + + return (0); +} + +struct pf_rule * +pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, + struct pfi_kif *kif, struct pf_src_node **sn, + struct pf_state_key **skp, struct pf_state_key **nkp, + struct pf_addr *saddr, struct pf_addr *daddr, + u_int16_t sport, u_int16_t dport) +{ + struct pf_rule *r = NULL; + struct pf_addr *naddr; + uint16_t *nport; + + PF_RULES_RASSERT(); + KASSERT(*skp == NULL, ("*skp not NULL")); + KASSERT(*nkp == NULL, ("*nkp not NULL")); + + if (direction == PF_OUT) { + r = pf_match_translation(pd, m, off, direction, kif, saddr, + sport, daddr, dport, PF_RULESET_BINAT); + if (r == NULL) + r = pf_match_translation(pd, m, off, direction, kif, + saddr, sport, daddr, dport, PF_RULESET_NAT); + } else { + r = pf_match_translation(pd, m, off, direction, kif, saddr, + sport, daddr, dport, PF_RULESET_RDR); + if (r == NULL) + r = pf_match_translation(pd, m, off, direction, kif, + saddr, sport, daddr, dport, PF_RULESET_BINAT); + } + + if (r == NULL) + return (NULL); + + switch (r->action) { + case PF_NONAT: + case PF_NOBINAT: + case PF_NORDR: + return (NULL); + } + + *skp = pf_state_key_setup(pd, saddr, daddr, sport, dport); + if (*skp == NULL) + return (NULL); + *nkp = pf_state_key_clone(*skp); + if (*nkp == NULL) { + uma_zfree(V_pf_state_key_z, skp); + *skp = NULL; + return (NULL); + } + + /* XXX We only modify one side for now. */ + naddr = &(*nkp)->addr[1]; + nport = &(*nkp)->port[1]; + + switch (r->action) { + case PF_NAT: + if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, + naddr, nport, r->rpool.proxy_port[0], + r->rpool.proxy_port[1], sn)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: NAT proxy port allocation (%u-%u) failed\n", + r->rpool.proxy_port[0], r->rpool.proxy_port[1])); + goto notrans; + } + break; + case PF_BINAT: + switch (direction) { + case PF_OUT: + if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ + switch (pd->af) { +#ifdef INET + case AF_INET: + if (r->rpool.cur->addr.p.dyn-> + pfid_acnt4 < 1) + goto notrans; + PF_POOLMASK(naddr, + &r->rpool.cur->addr.p.dyn-> + pfid_addr4, + &r->rpool.cur->addr.p.dyn-> + pfid_mask4, saddr, AF_INET); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (r->rpool.cur->addr.p.dyn-> + pfid_acnt6 < 1) + goto notrans; + PF_POOLMASK(naddr, + &r->rpool.cur->addr.p.dyn-> + pfid_addr6, + &r->rpool.cur->addr.p.dyn-> + pfid_mask6, saddr, AF_INET6); + break; +#endif /* INET6 */ + } + } else + PF_POOLMASK(naddr, + &r->rpool.cur->addr.v.a.addr, + &r->rpool.cur->addr.v.a.mask, saddr, + pd->af); + break; + case PF_IN: + if (r->src.addr.type == PF_ADDR_DYNIFTL) { + switch (pd->af) { +#ifdef INET + case AF_INET: + if (r->src.addr.p.dyn-> pfid_acnt4 < 1) + goto notrans; + PF_POOLMASK(naddr, + &r->src.addr.p.dyn->pfid_addr4, + &r->src.addr.p.dyn->pfid_mask4, + daddr, AF_INET); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (r->src.addr.p.dyn->pfid_acnt6 < 1) + goto notrans; + PF_POOLMASK(naddr, + &r->src.addr.p.dyn->pfid_addr6, + &r->src.addr.p.dyn->pfid_mask6, + daddr, AF_INET6); + break; +#endif /* INET6 */ + } + } else + PF_POOLMASK(naddr, &r->src.addr.v.a.addr, + &r->src.addr.v.a.mask, daddr, pd->af); + break; + } + break; + case PF_RDR: { + if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) + goto notrans; + if ((r->rpool.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) + PF_POOLMASK(naddr, naddr, &r->rpool.cur->addr.v.a.mask, + daddr, pd->af); + + if (r->rpool.proxy_port[1]) { + uint32_t tmp_nport; + + tmp_nport = ((ntohs(dport) - ntohs(r->dst.port[0])) % + (r->rpool.proxy_port[1] - r->rpool.proxy_port[0] + + 1)) + r->rpool.proxy_port[0]; + + /* Wrap around if necessary. */ + if (tmp_nport > 65535) + tmp_nport -= 65535; + *nport = htons((uint16_t)tmp_nport); + } else if (r->rpool.proxy_port[0]) + *nport = htons(r->rpool.proxy_port[0]); + break; + } + default: + panic("%s: unknown action %u", __func__, r->action); + } + + /* Return success only if translation really happened. */ + if (bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) + return (r); + +notrans: + uma_zfree(V_pf_state_key_z, *nkp); + uma_zfree(V_pf_state_key_z, *skp); + *skp = *nkp = NULL; + + return (NULL); +} diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c new file mode 100644 index 0000000000000..9063fe8917c03 --- /dev/null +++ b/sys/netpfil/pf/pf_norm.c @@ -0,0 +1,1999 @@ +/* $OpenBSD: pf_norm.c,v 1.114 2009/01/29 14:11:45 henning Exp $ */ + +/* + * Copyright 2001 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_pf.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#endif /* INET6 */ + +struct pf_frent { + LIST_ENTRY(pf_frent) fr_next; + union { + struct { + struct ip *_fr_ip; + struct mbuf *_fr_m; + } _frag; + struct { + uint16_t _fr_off; + uint16_t _fr_end; + } _cache; + } _u; +}; +#define fr_ip _u._frag._fr_ip +#define fr_m _u._frag._fr_m +#define fr_off _u._cache._fr_off +#define fr_end _u._cache._fr_end + +struct pf_fragment { + RB_ENTRY(pf_fragment) fr_entry; + TAILQ_ENTRY(pf_fragment) frag_next; + struct in_addr fr_src; + struct in_addr fr_dst; + u_int8_t fr_p; /* protocol of this fragment */ + u_int8_t fr_flags; /* status flags */ +#define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ +#define PFFRAG_NOBUFFER 0x0002 /* Non-buffering fragment cache */ +#define PFFRAG_DROP 0x0004 /* Drop all fragments */ +#define BUFFER_FRAGMENTS(fr) (!((fr)->fr_flags & PFFRAG_NOBUFFER)) + u_int16_t fr_id; /* fragment id for reassemble */ + u_int16_t fr_max; /* fragment data max */ + u_int32_t fr_timeout; + LIST_HEAD(, pf_frent) fr_queue; +}; + +static struct mtx pf_frag_mtx; +#define PF_FRAG_LOCK() mtx_lock(&pf_frag_mtx) +#define PF_FRAG_UNLOCK() mtx_unlock(&pf_frag_mtx) +#define PF_FRAG_ASSERT() mtx_assert(&pf_frag_mtx, MA_OWNED) + +VNET_DEFINE(uma_zone_t, pf_state_scrub_z); /* XXX: shared with pfsync */ + +static VNET_DEFINE(uma_zone_t, pf_frent_z); +#define V_pf_frent_z VNET(pf_frent_z) +static VNET_DEFINE(uma_zone_t, pf_frag_z); +#define V_pf_frag_z VNET(pf_frag_z) + +TAILQ_HEAD(pf_fragqueue, pf_fragment); +TAILQ_HEAD(pf_cachequeue, pf_fragment); +static VNET_DEFINE(struct pf_fragqueue, pf_fragqueue); +#define V_pf_fragqueue VNET(pf_fragqueue) +static VNET_DEFINE(struct pf_cachequeue, pf_cachequeue); +#define V_pf_cachequeue VNET(pf_cachequeue) +RB_HEAD(pf_frag_tree, pf_fragment); +static VNET_DEFINE(struct pf_frag_tree, pf_frag_tree); +#define V_pf_frag_tree VNET(pf_frag_tree) +static VNET_DEFINE(struct pf_frag_tree, pf_cache_tree); +#define V_pf_cache_tree VNET(pf_cache_tree) +static int pf_frag_compare(struct pf_fragment *, + struct pf_fragment *); +static RB_PROTOTYPE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); +static RB_GENERATE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); + +/* Private prototypes */ +static void pf_free_fragment(struct pf_fragment *); +static void pf_remove_fragment(struct pf_fragment *); +static int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *, + struct tcphdr *, int, sa_family_t); +#ifdef INET +static void pf_ip2key(struct pf_fragment *, struct ip *); +static void pf_scrub_ip(struct mbuf **, u_int32_t, u_int8_t, + u_int8_t); +static void pf_flush_fragments(void); +static struct pf_fragment *pf_find_fragment(struct ip *, struct pf_frag_tree *); +static struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment **, + struct pf_frent *, int); +static struct mbuf *pf_fragcache(struct mbuf **, struct ip*, + struct pf_fragment **, int, int, int *); +#endif /* INET */ +#ifdef INET6 +static void pf_scrub_ip6(struct mbuf **, u_int8_t); +#endif +#define DPFPRINTF(x) do { \ + if (V_pf_status.debug >= PF_DEBUG_MISC) { \ + printf("%s: ", __func__); \ + printf x ; \ + } \ +} while(0) + +void +pf_normalize_init(void) +{ + + V_pf_frag_z = uma_zcreate("pf frags", sizeof(struct pf_fragment), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + V_pf_frent_z = uma_zcreate("pf frag entries", sizeof(struct pf_frent), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + V_pf_state_scrub_z = uma_zcreate("pf state scrubs", + sizeof(struct pf_state_scrub), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + + V_pf_limits[PF_LIMIT_FRAGS].zone = V_pf_frent_z; + V_pf_limits[PF_LIMIT_FRAGS].limit = PFFRAG_FRENT_HIWAT; + uma_zone_set_max(V_pf_frent_z, PFFRAG_FRENT_HIWAT); + + mtx_init(&pf_frag_mtx, "pf fragments", NULL, MTX_DEF); + + TAILQ_INIT(&V_pf_fragqueue); + TAILQ_INIT(&V_pf_cachequeue); +} + +void +pf_normalize_cleanup(void) +{ + + uma_zdestroy(V_pf_state_scrub_z); + uma_zdestroy(V_pf_frent_z); + uma_zdestroy(V_pf_frag_z); + + mtx_destroy(&pf_frag_mtx); +} + +static int +pf_frag_compare(struct pf_fragment *a, struct pf_fragment *b) +{ + int diff; + + if ((diff = a->fr_id - b->fr_id)) + return (diff); + else if ((diff = a->fr_p - b->fr_p)) + return (diff); + else if (a->fr_src.s_addr < b->fr_src.s_addr) + return (-1); + else if (a->fr_src.s_addr > b->fr_src.s_addr) + return (1); + else if (a->fr_dst.s_addr < b->fr_dst.s_addr) + return (-1); + else if (a->fr_dst.s_addr > b->fr_dst.s_addr) + return (1); + return (0); +} + +void +pf_purge_expired_fragments(void) +{ + struct pf_fragment *frag; + u_int32_t expire = time_uptime - + V_pf_default_rule.timeout[PFTM_FRAG]; + + PF_FRAG_LOCK(); + while ((frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue)) != NULL) { + KASSERT((BUFFER_FRAGMENTS(frag)), + ("BUFFER_FRAGMENTS(frag) == 0: %s", __FUNCTION__)); + if (frag->fr_timeout > expire) + break; + + DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); + pf_free_fragment(frag); + } + + while ((frag = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue)) != NULL) { + KASSERT((!BUFFER_FRAGMENTS(frag)), + ("BUFFER_FRAGMENTS(frag) != 0: %s", __FUNCTION__)); + if (frag->fr_timeout > expire) + break; + + DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); + pf_free_fragment(frag); + KASSERT((TAILQ_EMPTY(&V_pf_cachequeue) || + TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue) != frag), + ("!(TAILQ_EMPTY() || TAILQ_LAST() == farg): %s", + __FUNCTION__)); + } + PF_FRAG_UNLOCK(); +} + +#ifdef INET +/* + * Try to flush old fragments to make space for new ones + */ +static void +pf_flush_fragments(void) +{ + struct pf_fragment *frag, *cache; + int goal; + + PF_FRAG_ASSERT(); + + goal = uma_zone_get_cur(V_pf_frent_z) * 9 / 10; + DPFPRINTF(("trying to free %d frag entriess\n", goal)); + while (goal < uma_zone_get_cur(V_pf_frent_z)) { + frag = TAILQ_LAST(&V_pf_fragqueue, pf_fragqueue); + if (frag) + pf_free_fragment(frag); + cache = TAILQ_LAST(&V_pf_cachequeue, pf_cachequeue); + if (cache) + pf_free_fragment(cache); + if (frag == NULL && cache == NULL) + break; + } +} +#endif /* INET */ + +/* Frees the fragments and all associated entries */ +static void +pf_free_fragment(struct pf_fragment *frag) +{ + struct pf_frent *frent; + + PF_FRAG_ASSERT(); + + /* Free all fragments */ + if (BUFFER_FRAGMENTS(frag)) { + for (frent = LIST_FIRST(&frag->fr_queue); frent; + frent = LIST_FIRST(&frag->fr_queue)) { + LIST_REMOVE(frent, fr_next); + + m_freem(frent->fr_m); + uma_zfree(V_pf_frent_z, frent); + } + } else { + for (frent = LIST_FIRST(&frag->fr_queue); frent; + frent = LIST_FIRST(&frag->fr_queue)) { + LIST_REMOVE(frent, fr_next); + + KASSERT((LIST_EMPTY(&frag->fr_queue) || + LIST_FIRST(&frag->fr_queue)->fr_off > + frent->fr_end), + ("! (LIST_EMPTY() || LIST_FIRST()->fr_off >" + " frent->fr_end): %s", __func__)); + + uma_zfree(V_pf_frent_z, frent); + } + } + + pf_remove_fragment(frag); +} + +#ifdef INET +static void +pf_ip2key(struct pf_fragment *key, struct ip *ip) +{ + key->fr_p = ip->ip_p; + key->fr_id = ip->ip_id; + key->fr_src.s_addr = ip->ip_src.s_addr; + key->fr_dst.s_addr = ip->ip_dst.s_addr; +} + +static struct pf_fragment * +pf_find_fragment(struct ip *ip, struct pf_frag_tree *tree) +{ + struct pf_fragment key; + struct pf_fragment *frag; + + PF_FRAG_ASSERT(); + + pf_ip2key(&key, ip); + + frag = RB_FIND(pf_frag_tree, tree, &key); + if (frag != NULL) { + /* XXX Are we sure we want to update the timeout? */ + frag->fr_timeout = time_uptime; + if (BUFFER_FRAGMENTS(frag)) { + TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next); + TAILQ_INSERT_HEAD(&V_pf_fragqueue, frag, frag_next); + } else { + TAILQ_REMOVE(&V_pf_cachequeue, frag, frag_next); + TAILQ_INSERT_HEAD(&V_pf_cachequeue, frag, frag_next); + } + } + + return (frag); +} +#endif /* INET */ + +/* Removes a fragment from the fragment queue and frees the fragment */ + +static void +pf_remove_fragment(struct pf_fragment *frag) +{ + + PF_FRAG_ASSERT(); + + if (BUFFER_FRAGMENTS(frag)) { + RB_REMOVE(pf_frag_tree, &V_pf_frag_tree, frag); + TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next); + uma_zfree(V_pf_frag_z, frag); + } else { + RB_REMOVE(pf_frag_tree, &V_pf_cache_tree, frag); + TAILQ_REMOVE(&V_pf_cachequeue, frag, frag_next); + uma_zfree(V_pf_frag_z, frag); + } +} + +#ifdef INET +#define FR_IP_OFF(fr) ((ntohs((fr)->fr_ip->ip_off) & IP_OFFMASK) << 3) +static struct mbuf * +pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, + struct pf_frent *frent, int mff) +{ + struct mbuf *m = *m0, *m2; + struct pf_frent *frea, *next; + struct pf_frent *frep = NULL; + struct ip *ip = frent->fr_ip; + int hlen = ip->ip_hl << 2; + u_int16_t off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3; + u_int16_t ip_len = ntohs(ip->ip_len) - ip->ip_hl * 4; + u_int16_t max = ip_len + off; + + PF_FRAG_ASSERT(); + KASSERT((*frag == NULL || BUFFER_FRAGMENTS(*frag)), + ("! (*frag == NULL || BUFFER_FRAGMENTS(*frag)): %s", __FUNCTION__)); + + /* Strip off ip header */ + m->m_data += hlen; + m->m_len -= hlen; + + /* Create a new reassembly queue for this packet */ + if (*frag == NULL) { + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); + if (*frag == NULL) { + pf_flush_fragments(); + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); + if (*frag == NULL) + goto drop_fragment; + } + + (*frag)->fr_flags = 0; + (*frag)->fr_max = 0; + (*frag)->fr_src = frent->fr_ip->ip_src; + (*frag)->fr_dst = frent->fr_ip->ip_dst; + (*frag)->fr_p = frent->fr_ip->ip_p; + (*frag)->fr_id = frent->fr_ip->ip_id; + (*frag)->fr_timeout = time_uptime; + LIST_INIT(&(*frag)->fr_queue); + + RB_INSERT(pf_frag_tree, &V_pf_frag_tree, *frag); + TAILQ_INSERT_HEAD(&V_pf_fragqueue, *frag, frag_next); + + /* We do not have a previous fragment */ + frep = NULL; + goto insert; + } + + /* + * Find a fragment after the current one: + * - off contains the real shifted offset. + */ + LIST_FOREACH(frea, &(*frag)->fr_queue, fr_next) { + if (FR_IP_OFF(frea) > off) + break; + frep = frea; + } + + KASSERT((frep != NULL || frea != NULL), + ("!(frep != NULL || frea != NULL): %s", __FUNCTION__));; + + if (frep != NULL && + FR_IP_OFF(frep) + ntohs(frep->fr_ip->ip_len) - frep->fr_ip->ip_hl * + 4 > off) + { + u_int16_t precut; + + precut = FR_IP_OFF(frep) + ntohs(frep->fr_ip->ip_len) - + frep->fr_ip->ip_hl * 4 - off; + if (precut >= ip_len) + goto drop_fragment; + m_adj(frent->fr_m, precut); + DPFPRINTF(("overlap -%d\n", precut)); + /* Enforce 8 byte boundaries */ + ip->ip_off = htons(ntohs(ip->ip_off) + (precut >> 3)); + off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3; + ip_len -= precut; + ip->ip_len = htons(ip_len); + } + + for (; frea != NULL && ip_len + off > FR_IP_OFF(frea); + frea = next) + { + u_int16_t aftercut; + + aftercut = ip_len + off - FR_IP_OFF(frea); + DPFPRINTF(("adjust overlap %d\n", aftercut)); + if (aftercut < ntohs(frea->fr_ip->ip_len) - frea->fr_ip->ip_hl + * 4) + { + frea->fr_ip->ip_len = + htons(ntohs(frea->fr_ip->ip_len) - aftercut); + frea->fr_ip->ip_off = htons(ntohs(frea->fr_ip->ip_off) + + (aftercut >> 3)); + m_adj(frea->fr_m, aftercut); + break; + } + + /* This fragment is completely overlapped, lose it */ + next = LIST_NEXT(frea, fr_next); + m_freem(frea->fr_m); + LIST_REMOVE(frea, fr_next); + uma_zfree(V_pf_frent_z, frea); + } + + insert: + /* Update maximum data size */ + if ((*frag)->fr_max < max) + (*frag)->fr_max = max; + /* This is the last segment */ + if (!mff) + (*frag)->fr_flags |= PFFRAG_SEENLAST; + + if (frep == NULL) + LIST_INSERT_HEAD(&(*frag)->fr_queue, frent, fr_next); + else + LIST_INSERT_AFTER(frep, frent, fr_next); + + /* Check if we are completely reassembled */ + if (!((*frag)->fr_flags & PFFRAG_SEENLAST)) + return (NULL); + + /* Check if we have all the data */ + off = 0; + for (frep = LIST_FIRST(&(*frag)->fr_queue); frep; frep = next) { + next = LIST_NEXT(frep, fr_next); + + off += ntohs(frep->fr_ip->ip_len) - frep->fr_ip->ip_hl * 4; + if (off < (*frag)->fr_max && + (next == NULL || FR_IP_OFF(next) != off)) + { + DPFPRINTF(("missing fragment at %d, next %d, max %d\n", + off, next == NULL ? -1 : FR_IP_OFF(next), + (*frag)->fr_max)); + return (NULL); + } + } + DPFPRINTF(("%d < %d?\n", off, (*frag)->fr_max)); + if (off < (*frag)->fr_max) + return (NULL); + + /* We have all the data */ + frent = LIST_FIRST(&(*frag)->fr_queue); + KASSERT((frent != NULL), ("frent == NULL: %s", __FUNCTION__)); + if ((frent->fr_ip->ip_hl << 2) + off > IP_MAXPACKET) { + DPFPRINTF(("drop: too big: %d\n", off)); + pf_free_fragment(*frag); + *frag = NULL; + return (NULL); + } + next = LIST_NEXT(frent, fr_next); + + /* Magic from ip_input */ + ip = frent->fr_ip; + m = frent->fr_m; + m2 = m->m_next; + m->m_next = NULL; + m_cat(m, m2); + uma_zfree(V_pf_frent_z, frent); + for (frent = next; frent != NULL; frent = next) { + next = LIST_NEXT(frent, fr_next); + + m2 = frent->fr_m; + uma_zfree(V_pf_frent_z, frent); + m->m_pkthdr.csum_flags &= m2->m_pkthdr.csum_flags; + m->m_pkthdr.csum_data += m2->m_pkthdr.csum_data; + m_cat(m, m2); + } + + while (m->m_pkthdr.csum_data & 0xffff0000) + m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data & 0xffff) + + (m->m_pkthdr.csum_data >> 16); + ip->ip_src = (*frag)->fr_src; + ip->ip_dst = (*frag)->fr_dst; + + /* Remove from fragment queue */ + pf_remove_fragment(*frag); + *frag = NULL; + + hlen = ip->ip_hl << 2; + ip->ip_len = htons(off + hlen); + m->m_len += hlen; + m->m_data -= hlen; + + /* some debugging cruft by sklower, below, will go away soon */ + /* XXX this should be done elsewhere */ + if (m->m_flags & M_PKTHDR) { + int plen = 0; + for (m2 = m; m2; m2 = m2->m_next) + plen += m2->m_len; + m->m_pkthdr.len = plen; + } + + DPFPRINTF(("complete: %p(%d)\n", m, ntohs(ip->ip_len))); + return (m); + + drop_fragment: + /* Oops - fail safe - drop packet */ + uma_zfree(V_pf_frent_z, frent); + m_freem(m); + return (NULL); +} + +static struct mbuf * +pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, + int drop, int *nomem) +{ + struct mbuf *m = *m0; + struct pf_frent *frp, *fra, *cur = NULL; + int ip_len = ntohs(h->ip_len) - (h->ip_hl << 2); + u_int16_t off = ntohs(h->ip_off) << 3; + u_int16_t max = ip_len + off; + int hosed = 0; + + PF_FRAG_ASSERT(); + KASSERT((*frag == NULL || !BUFFER_FRAGMENTS(*frag)), + ("!(*frag == NULL || !BUFFER_FRAGMENTS(*frag)): %s", __FUNCTION__)); + + /* Create a new range queue for this packet */ + if (*frag == NULL) { + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); + if (*frag == NULL) { + pf_flush_fragments(); + *frag = uma_zalloc(V_pf_frag_z, M_NOWAIT); + if (*frag == NULL) + goto no_mem; + } + + /* Get an entry for the queue */ + cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); + if (cur == NULL) { + uma_zfree(V_pf_frag_z, *frag); + *frag = NULL; + goto no_mem; + } + + (*frag)->fr_flags = PFFRAG_NOBUFFER; + (*frag)->fr_max = 0; + (*frag)->fr_src = h->ip_src; + (*frag)->fr_dst = h->ip_dst; + (*frag)->fr_p = h->ip_p; + (*frag)->fr_id = h->ip_id; + (*frag)->fr_timeout = time_uptime; + + cur->fr_off = off; + cur->fr_end = max; + LIST_INIT(&(*frag)->fr_queue); + LIST_INSERT_HEAD(&(*frag)->fr_queue, cur, fr_next); + + RB_INSERT(pf_frag_tree, &V_pf_cache_tree, *frag); + TAILQ_INSERT_HEAD(&V_pf_cachequeue, *frag, frag_next); + + DPFPRINTF(("fragcache[%d]: new %d-%d\n", h->ip_id, off, max)); + + goto pass; + } + + /* + * Find a fragment after the current one: + * - off contains the real shifted offset. + */ + frp = NULL; + LIST_FOREACH(fra, &(*frag)->fr_queue, fr_next) { + if (fra->fr_off > off) + break; + frp = fra; + } + + KASSERT((frp != NULL || fra != NULL), + ("!(frp != NULL || fra != NULL): %s", __FUNCTION__)); + + if (frp != NULL) { + int precut; + + precut = frp->fr_end - off; + if (precut >= ip_len) { + /* Fragment is entirely a duplicate */ + DPFPRINTF(("fragcache[%d]: dead (%d-%d) %d-%d\n", + h->ip_id, frp->fr_off, frp->fr_end, off, max)); + goto drop_fragment; + } + if (precut == 0) { + /* They are adjacent. Fixup cache entry */ + DPFPRINTF(("fragcache[%d]: adjacent (%d-%d) %d-%d\n", + h->ip_id, frp->fr_off, frp->fr_end, off, max)); + frp->fr_end = max; + } else if (precut > 0) { + /* The first part of this payload overlaps with a + * fragment that has already been passed. + * Need to trim off the first part of the payload. + * But to do so easily, we need to create another + * mbuf to throw the original header into. + */ + + DPFPRINTF(("fragcache[%d]: chop %d (%d-%d) %d-%d\n", + h->ip_id, precut, frp->fr_off, frp->fr_end, off, + max)); + + off += precut; + max -= precut; + /* Update the previous frag to encompass this one */ + frp->fr_end = max; + + if (!drop) { + /* XXX Optimization opportunity + * This is a very heavy way to trim the payload. + * we could do it much faster by diddling mbuf + * internals but that would be even less legible + * than this mbuf magic. For my next trick, + * I'll pull a rabbit out of my laptop. + */ + *m0 = m_dup(m, M_NOWAIT); + if (*m0 == NULL) + goto no_mem; + /* From KAME Project : We have missed this! */ + m_adj(*m0, (h->ip_hl << 2) - + (*m0)->m_pkthdr.len); + + KASSERT(((*m0)->m_next == NULL), + ("(*m0)->m_next != NULL: %s", + __FUNCTION__)); + m_adj(m, precut + (h->ip_hl << 2)); + m_cat(*m0, m); + m = *m0; + if (m->m_flags & M_PKTHDR) { + int plen = 0; + struct mbuf *t; + for (t = m; t; t = t->m_next) + plen += t->m_len; + m->m_pkthdr.len = plen; + } + + + h = mtod(m, struct ip *); + + KASSERT(((int)m->m_len == + ntohs(h->ip_len) - precut), + ("m->m_len != ntohs(h->ip_len) - precut: %s", + __FUNCTION__)); + h->ip_off = htons(ntohs(h->ip_off) + + (precut >> 3)); + h->ip_len = htons(ntohs(h->ip_len) - precut); + } else { + hosed++; + } + } else { + /* There is a gap between fragments */ + + DPFPRINTF(("fragcache[%d]: gap %d (%d-%d) %d-%d\n", + h->ip_id, -precut, frp->fr_off, frp->fr_end, off, + max)); + + cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); + if (cur == NULL) + goto no_mem; + + cur->fr_off = off; + cur->fr_end = max; + LIST_INSERT_AFTER(frp, cur, fr_next); + } + } + + if (fra != NULL) { + int aftercut; + int merge = 0; + + aftercut = max - fra->fr_off; + if (aftercut == 0) { + /* Adjacent fragments */ + DPFPRINTF(("fragcache[%d]: adjacent %d-%d (%d-%d)\n", + h->ip_id, off, max, fra->fr_off, fra->fr_end)); + fra->fr_off = off; + merge = 1; + } else if (aftercut > 0) { + /* Need to chop off the tail of this fragment */ + DPFPRINTF(("fragcache[%d]: chop %d %d-%d (%d-%d)\n", + h->ip_id, aftercut, off, max, fra->fr_off, + fra->fr_end)); + fra->fr_off = off; + max -= aftercut; + + merge = 1; + + if (!drop) { + m_adj(m, -aftercut); + if (m->m_flags & M_PKTHDR) { + int plen = 0; + struct mbuf *t; + for (t = m; t; t = t->m_next) + plen += t->m_len; + m->m_pkthdr.len = plen; + } + h = mtod(m, struct ip *); + KASSERT(((int)m->m_len == ntohs(h->ip_len) - aftercut), + ("m->m_len != ntohs(h->ip_len) - aftercut: %s", + __FUNCTION__)); + h->ip_len = htons(ntohs(h->ip_len) - aftercut); + } else { + hosed++; + } + } else if (frp == NULL) { + /* There is a gap between fragments */ + DPFPRINTF(("fragcache[%d]: gap %d %d-%d (%d-%d)\n", + h->ip_id, -aftercut, off, max, fra->fr_off, + fra->fr_end)); + + cur = uma_zalloc(V_pf_frent_z, M_NOWAIT); + if (cur == NULL) + goto no_mem; + + cur->fr_off = off; + cur->fr_end = max; + LIST_INSERT_BEFORE(fra, cur, fr_next); + } + + + /* Need to glue together two separate fragment descriptors */ + if (merge) { + if (cur && fra->fr_off <= cur->fr_end) { + /* Need to merge in a previous 'cur' */ + DPFPRINTF(("fragcache[%d]: adjacent(merge " + "%d-%d) %d-%d (%d-%d)\n", + h->ip_id, cur->fr_off, cur->fr_end, off, + max, fra->fr_off, fra->fr_end)); + fra->fr_off = cur->fr_off; + LIST_REMOVE(cur, fr_next); + uma_zfree(V_pf_frent_z, cur); + cur = NULL; + + } else if (frp && fra->fr_off <= frp->fr_end) { + /* Need to merge in a modified 'frp' */ + KASSERT((cur == NULL), ("cur != NULL: %s", + __FUNCTION__)); + DPFPRINTF(("fragcache[%d]: adjacent(merge " + "%d-%d) %d-%d (%d-%d)\n", + h->ip_id, frp->fr_off, frp->fr_end, off, + max, fra->fr_off, fra->fr_end)); + fra->fr_off = frp->fr_off; + LIST_REMOVE(frp, fr_next); + uma_zfree(V_pf_frent_z, frp); + frp = NULL; + + } + } + } + + if (hosed) { + /* + * We must keep tracking the overall fragment even when + * we're going to drop it anyway so that we know when to + * free the overall descriptor. Thus we drop the frag late. + */ + goto drop_fragment; + } + + + pass: + /* Update maximum data size */ + if ((*frag)->fr_max < max) + (*frag)->fr_max = max; + + /* This is the last segment */ + if (!mff) + (*frag)->fr_flags |= PFFRAG_SEENLAST; + + /* Check if we are completely reassembled */ + if (((*frag)->fr_flags & PFFRAG_SEENLAST) && + LIST_FIRST(&(*frag)->fr_queue)->fr_off == 0 && + LIST_FIRST(&(*frag)->fr_queue)->fr_end == (*frag)->fr_max) { + /* Remove from fragment queue */ + DPFPRINTF(("fragcache[%d]: done 0-%d\n", h->ip_id, + (*frag)->fr_max)); + pf_free_fragment(*frag); + *frag = NULL; + } + + return (m); + + no_mem: + *nomem = 1; + + /* Still need to pay attention to !IP_MF */ + if (!mff && *frag != NULL) + (*frag)->fr_flags |= PFFRAG_SEENLAST; + + m_freem(m); + return (NULL); + + drop_fragment: + + /* Still need to pay attention to !IP_MF */ + if (!mff && *frag != NULL) + (*frag)->fr_flags |= PFFRAG_SEENLAST; + + if (drop) { + /* This fragment has been deemed bad. Don't reass */ + if (((*frag)->fr_flags & PFFRAG_DROP) == 0) + DPFPRINTF(("fragcache[%d]: dropping overall fragment\n", + h->ip_id)); + (*frag)->fr_flags |= PFFRAG_DROP; + } + + m_freem(m); + return (NULL); +} + +int +pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, + struct pf_pdesc *pd) +{ + struct mbuf *m = *m0; + struct pf_rule *r; + struct pf_frent *frent; + struct pf_fragment *frag = NULL; + struct ip *h = mtod(m, struct ip *); + int mff = (ntohs(h->ip_off) & IP_MF); + int hlen = h->ip_hl << 2; + u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; + u_int16_t max; + int ip_len; + int ip_off; + int tag = -1; + + PF_RULES_RASSERT(); + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); + while (r != NULL) { + r->evaluations++; + if (pfi_kif_match(r->kif, kif) == r->ifnot) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != dir) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != AF_INET) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != h->ip_p) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, + (struct pf_addr *)&h->ip_src.s_addr, AF_INET, + r->src.neg, kif, M_GETFIB(m))) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, + (struct pf_addr *)&h->ip_dst.s_addr, AF_INET, + r->dst.neg, NULL, M_GETFIB(m))) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->match_tag && !pf_match_tag(m, r, &tag, + pd->pf_mtag ? pd->pf_mtag->tag : 0)) + r = TAILQ_NEXT(r, entries); + else + break; + } + + if (r == NULL || r->action == PF_NOSCRUB) + return (PF_PASS); + else { + r->packets[dir == PF_OUT]++; + r->bytes[dir == PF_OUT] += pd->tot_len; + } + + /* Check for illegal packets */ + if (hlen < (int)sizeof(struct ip)) + goto drop; + + if (hlen > ntohs(h->ip_len)) + goto drop; + + /* Clear IP_DF if the rule uses the no-df option */ + if (r->rule_flag & PFRULE_NODF && h->ip_off & htons(IP_DF)) { + u_int16_t ip_off = h->ip_off; + + h->ip_off &= htons(~IP_DF); + h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); + } + + /* We will need other tests here */ + if (!fragoff && !mff) + goto no_fragment; + + /* We're dealing with a fragment now. Don't allow fragments + * with IP_DF to enter the cache. If the flag was cleared by + * no-df above, fine. Otherwise drop it. + */ + if (h->ip_off & htons(IP_DF)) { + DPFPRINTF(("IP_DF\n")); + goto bad; + } + + ip_len = ntohs(h->ip_len) - hlen; + ip_off = (ntohs(h->ip_off) & IP_OFFMASK) << 3; + + /* All fragments are 8 byte aligned */ + if (mff && (ip_len & 0x7)) { + DPFPRINTF(("mff and %d\n", ip_len)); + goto bad; + } + + /* Respect maximum length */ + if (fragoff + ip_len > IP_MAXPACKET) { + DPFPRINTF(("max packet %d\n", fragoff + ip_len)); + goto bad; + } + max = fragoff + ip_len; + + if ((r->rule_flag & (PFRULE_FRAGCROP|PFRULE_FRAGDROP)) == 0) { + + /* Fully buffer all of the fragments */ + PF_FRAG_LOCK(); + frag = pf_find_fragment(h, &V_pf_frag_tree); + + /* Check if we saw the last fragment already */ + if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && + max > frag->fr_max) + goto bad; + + /* Get an entry for the fragment queue */ + frent = uma_zalloc(V_pf_frent_z, M_NOWAIT); + if (frent == NULL) { + PF_FRAG_UNLOCK(); + REASON_SET(reason, PFRES_MEMORY); + return (PF_DROP); + } + frent->fr_ip = h; + frent->fr_m = m; + + /* Might return a completely reassembled mbuf, or NULL */ + DPFPRINTF(("reass frag %d @ %d-%d\n", h->ip_id, fragoff, max)); + *m0 = m = pf_reassemble(m0, &frag, frent, mff); + PF_FRAG_UNLOCK(); + + if (m == NULL) + return (PF_DROP); + + /* use mtag from concatenated mbuf chain */ + pd->pf_mtag = pf_find_mtag(m); +#ifdef DIAGNOSTIC + if (pd->pf_mtag == NULL) { + printf("%s: pf_find_mtag returned NULL(1)\n", __func__); + if ((pd->pf_mtag = pf_get_mtag(m)) == NULL) { + m_freem(m); + *m0 = NULL; + goto no_mem; + } + } +#endif + if (frag != NULL && (frag->fr_flags & PFFRAG_DROP)) + goto drop; + + h = mtod(m, struct ip *); + } else { + /* non-buffering fragment cache (drops or masks overlaps) */ + int nomem = 0; + + if (dir == PF_OUT && pd->pf_mtag->flags & PF_TAG_FRAGCACHE) { + /* + * Already passed the fragment cache in the + * input direction. If we continued, it would + * appear to be a dup and would be dropped. + */ + goto fragment_pass; + } + + PF_FRAG_LOCK(); + frag = pf_find_fragment(h, &V_pf_cache_tree); + + /* Check if we saw the last fragment already */ + if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && + max > frag->fr_max) { + if (r->rule_flag & PFRULE_FRAGDROP) + frag->fr_flags |= PFFRAG_DROP; + goto bad; + } + + *m0 = m = pf_fragcache(m0, h, &frag, mff, + (r->rule_flag & PFRULE_FRAGDROP) ? 1 : 0, &nomem); + PF_FRAG_UNLOCK(); + if (m == NULL) { + if (nomem) + goto no_mem; + goto drop; + } + + /* use mtag from copied and trimmed mbuf chain */ + pd->pf_mtag = pf_find_mtag(m); +#ifdef DIAGNOSTIC + if (pd->pf_mtag == NULL) { + printf("%s: pf_find_mtag returned NULL(2)\n", __func__); + if ((pd->pf_mtag = pf_get_mtag(m)) == NULL) { + m_freem(m); + *m0 = NULL; + goto no_mem; + } + } +#endif + if (dir == PF_IN) + pd->pf_mtag->flags |= PF_TAG_FRAGCACHE; + + if (frag != NULL && (frag->fr_flags & PFFRAG_DROP)) + goto drop; + goto fragment_pass; + } + + no_fragment: + /* At this point, only IP_DF is allowed in ip_off */ + if (h->ip_off & ~htons(IP_DF)) { + u_int16_t ip_off = h->ip_off; + + h->ip_off &= htons(IP_DF); + h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); + } + + /* not missing a return here */ + + fragment_pass: + pf_scrub_ip(&m, r->rule_flag, r->min_ttl, r->set_tos); + + if ((r->rule_flag & (PFRULE_FRAGCROP|PFRULE_FRAGDROP)) == 0) + pd->flags |= PFDESC_IP_REAS; + return (PF_PASS); + + no_mem: + REASON_SET(reason, PFRES_MEMORY); + if (r != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, + 1); + return (PF_DROP); + + drop: + REASON_SET(reason, PFRES_NORM); + if (r != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, + 1); + return (PF_DROP); + + bad: + DPFPRINTF(("dropping bad fragment\n")); + + /* Free associated fragments */ + if (frag != NULL) { + pf_free_fragment(frag); + PF_FRAG_UNLOCK(); + } + + REASON_SET(reason, PFRES_FRAG); + if (r != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET, dir, *reason, r, NULL, NULL, pd, + 1); + + return (PF_DROP); +} +#endif + +#ifdef INET6 +int +pf_normalize_ip6(struct mbuf **m0, int dir, struct pfi_kif *kif, + u_short *reason, struct pf_pdesc *pd) +{ + struct mbuf *m = *m0; + struct pf_rule *r; + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + int off; + struct ip6_ext ext; + struct ip6_opt opt; + struct ip6_opt_jumbo jumbo; + struct ip6_frag frag; + u_int32_t jumbolen = 0, plen; + u_int16_t fragoff = 0; + int optend; + int ooff; + u_int8_t proto; + int terminal; + + PF_RULES_RASSERT(); + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); + while (r != NULL) { + r->evaluations++; + if (pfi_kif_match(r->kif, kif) == r->ifnot) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != dir) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != AF_INET6) + r = r->skip[PF_SKIP_AF].ptr; +#if 0 /* header chain! */ + else if (r->proto && r->proto != h->ip6_nxt) + r = r->skip[PF_SKIP_PROTO].ptr; +#endif + else if (PF_MISMATCHAW(&r->src.addr, + (struct pf_addr *)&h->ip6_src, AF_INET6, + r->src.neg, kif, M_GETFIB(m))) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, + (struct pf_addr *)&h->ip6_dst, AF_INET6, + r->dst.neg, NULL, M_GETFIB(m))) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else + break; + } + + if (r == NULL || r->action == PF_NOSCRUB) + return (PF_PASS); + else { + r->packets[dir == PF_OUT]++; + r->bytes[dir == PF_OUT] += pd->tot_len; + } + + /* Check for illegal packets */ + if (sizeof(struct ip6_hdr) + IPV6_MAXPACKET < m->m_pkthdr.len) + goto drop; + + off = sizeof(struct ip6_hdr); + proto = h->ip6_nxt; + terminal = 0; + do { + switch (proto) { + case IPPROTO_FRAGMENT: + goto fragment; + break; + case IPPROTO_AH: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + if (!pf_pull_hdr(m, off, &ext, sizeof(ext), NULL, + NULL, AF_INET6)) + goto shortpkt; + if (proto == IPPROTO_AH) + off += (ext.ip6e_len + 2) * 4; + else + off += (ext.ip6e_len + 1) * 8; + proto = ext.ip6e_nxt; + break; + case IPPROTO_HOPOPTS: + if (!pf_pull_hdr(m, off, &ext, sizeof(ext), NULL, + NULL, AF_INET6)) + goto shortpkt; + optend = off + (ext.ip6e_len + 1) * 8; + ooff = off + sizeof(ext); + do { + if (!pf_pull_hdr(m, ooff, &opt.ip6o_type, + sizeof(opt.ip6o_type), NULL, NULL, + AF_INET6)) + goto shortpkt; + if (opt.ip6o_type == IP6OPT_PAD1) { + ooff++; + continue; + } + if (!pf_pull_hdr(m, ooff, &opt, sizeof(opt), + NULL, NULL, AF_INET6)) + goto shortpkt; + if (ooff + sizeof(opt) + opt.ip6o_len > optend) + goto drop; + switch (opt.ip6o_type) { + case IP6OPT_JUMBO: + if (h->ip6_plen != 0) + goto drop; + if (!pf_pull_hdr(m, ooff, &jumbo, + sizeof(jumbo), NULL, NULL, + AF_INET6)) + goto shortpkt; + memcpy(&jumbolen, jumbo.ip6oj_jumbo_len, + sizeof(jumbolen)); + jumbolen = ntohl(jumbolen); + if (jumbolen <= IPV6_MAXPACKET) + goto drop; + if (sizeof(struct ip6_hdr) + jumbolen != + m->m_pkthdr.len) + goto drop; + break; + default: + break; + } + ooff += sizeof(opt) + opt.ip6o_len; + } while (ooff < optend); + + off = optend; + proto = ext.ip6e_nxt; + break; + default: + terminal = 1; + break; + } + } while (!terminal); + + /* jumbo payload option must be present, or plen > 0 */ + if (ntohs(h->ip6_plen) == 0) + plen = jumbolen; + else + plen = ntohs(h->ip6_plen); + if (plen == 0) + goto drop; + if (sizeof(struct ip6_hdr) + plen > m->m_pkthdr.len) + goto shortpkt; + + pf_scrub_ip6(&m, r->min_ttl); + + return (PF_PASS); + + fragment: + if (ntohs(h->ip6_plen) == 0 || jumbolen) + goto drop; + plen = ntohs(h->ip6_plen); + + if (!pf_pull_hdr(m, off, &frag, sizeof(frag), NULL, NULL, AF_INET6)) + goto shortpkt; + fragoff = ntohs(frag.ip6f_offlg & IP6F_OFF_MASK); + if (fragoff + (plen - off - sizeof(frag)) > IPV6_MAXPACKET) + goto badfrag; + + /* do something about it */ + /* remember to set pd->flags |= PFDESC_IP_REAS */ + return (PF_PASS); + + shortpkt: + REASON_SET(reason, PFRES_SHORT); + if (r != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, + 1); + return (PF_DROP); + + drop: + REASON_SET(reason, PFRES_NORM); + if (r != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, + 1); + return (PF_DROP); + + badfrag: + REASON_SET(reason, PFRES_FRAG); + if (r != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET6, dir, *reason, r, NULL, NULL, pd, + 1); + return (PF_DROP); +} +#endif /* INET6 */ + +int +pf_normalize_tcp(int dir, struct pfi_kif *kif, struct mbuf *m, int ipoff, + int off, void *h, struct pf_pdesc *pd) +{ + struct pf_rule *r, *rm = NULL; + struct tcphdr *th = pd->hdr.tcp; + int rewrite = 0; + u_short reason; + u_int8_t flags; + sa_family_t af = pd->af; + + PF_RULES_RASSERT(); + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); + while (r != NULL) { + r->evaluations++; + if (pfi_kif_match(r->kif, kif) == r->ifnot) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != dir) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, + r->src.neg, kif, M_GETFIB(m))) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (r->src.port_op && !pf_match_port(r->src.port_op, + r->src.port[0], r->src.port[1], th->th_sport)) + r = r->skip[PF_SKIP_SRC_PORT].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, + r->dst.neg, NULL, M_GETFIB(m))) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->dst.port_op && !pf_match_port(r->dst.port_op, + r->dst.port[0], r->dst.port[1], th->th_dport)) + r = r->skip[PF_SKIP_DST_PORT].ptr; + else if (r->os_fingerprint != PF_OSFP_ANY && !pf_osfp_match( + pf_osfp_fingerprint(pd, m, off, th), + r->os_fingerprint)) + r = TAILQ_NEXT(r, entries); + else { + rm = r; + break; + } + } + + if (rm == NULL || rm->action == PF_NOSCRUB) + return (PF_PASS); + else { + r->packets[dir == PF_OUT]++; + r->bytes[dir == PF_OUT] += pd->tot_len; + } + + if (rm->rule_flag & PFRULE_REASSEMBLE_TCP) + pd->flags |= PFDESC_TCP_NORM; + + flags = th->th_flags; + if (flags & TH_SYN) { + /* Illegal packet */ + if (flags & TH_RST) + goto tcp_drop; + + if (flags & TH_FIN) + flags &= ~TH_FIN; + } else { + /* Illegal packet */ + if (!(flags & (TH_ACK|TH_RST))) + goto tcp_drop; + } + + if (!(flags & TH_ACK)) { + /* These flags are only valid if ACK is set */ + if ((flags & TH_FIN) || (flags & TH_PUSH) || (flags & TH_URG)) + goto tcp_drop; + } + + /* Check for illegal header length */ + if (th->th_off < (sizeof(struct tcphdr) >> 2)) + goto tcp_drop; + + /* If flags changed, or reserved data set, then adjust */ + if (flags != th->th_flags || th->th_x2 != 0) { + u_int16_t ov, nv; + + ov = *(u_int16_t *)(&th->th_ack + 1); + th->th_flags = flags; + th->th_x2 = 0; + nv = *(u_int16_t *)(&th->th_ack + 1); + + th->th_sum = pf_cksum_fixup(th->th_sum, ov, nv, 0); + rewrite = 1; + } + + /* Remove urgent pointer, if TH_URG is not set */ + if (!(flags & TH_URG) && th->th_urp) { + th->th_sum = pf_cksum_fixup(th->th_sum, th->th_urp, 0, 0); + th->th_urp = 0; + rewrite = 1; + } + + /* Process options */ + if (r->max_mss && pf_normalize_tcpopt(r, m, th, off, pd->af)) + rewrite = 1; + + /* copy back packet headers if we sanitized */ + if (rewrite) + m_copyback(m, off, sizeof(*th), (caddr_t)th); + + return (PF_PASS); + + tcp_drop: + REASON_SET(&reason, PFRES_NORM); + if (rm != NULL && r->log) + PFLOG_PACKET(kif, m, AF_INET, dir, reason, r, NULL, NULL, pd, + 1); + return (PF_DROP); +} + +int +pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, + struct tcphdr *th, struct pf_state_peer *src, struct pf_state_peer *dst) +{ + u_int32_t tsval, tsecr; + u_int8_t hdr[60]; + u_int8_t *opt; + + KASSERT((src->scrub == NULL), + ("pf_normalize_tcp_init: src->scrub != NULL")); + + src->scrub = uma_zalloc(V_pf_state_scrub_z, M_ZERO | M_NOWAIT); + if (src->scrub == NULL) + return (1); + + switch (pd->af) { +#ifdef INET + case AF_INET: { + struct ip *h = mtod(m, struct ip *); + src->scrub->pfss_ttl = h->ip_ttl; + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + src->scrub->pfss_ttl = h->ip6_hlim; + break; + } +#endif /* INET6 */ + } + + + /* + * All normalizations below are only begun if we see the start of + * the connections. They must all set an enabled bit in pfss_flags + */ + if ((th->th_flags & TH_SYN) == 0) + return (0); + + + if (th->th_off > (sizeof(struct tcphdr) >> 2) && src->scrub && + pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { + /* Diddle with TCP options */ + int hlen; + opt = hdr + sizeof(struct tcphdr); + hlen = (th->th_off << 2) - sizeof(struct tcphdr); + while (hlen >= TCPOLEN_TIMESTAMP) { + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUGH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_TIMESTAMP: + if (opt[1] >= TCPOLEN_TIMESTAMP) { + src->scrub->pfss_flags |= + PFSS_TIMESTAMP; + src->scrub->pfss_ts_mod = + htonl(arc4random()); + + /* note PFSS_PAWS not set yet */ + memcpy(&tsval, &opt[2], + sizeof(u_int32_t)); + memcpy(&tsecr, &opt[6], + sizeof(u_int32_t)); + src->scrub->pfss_tsval0 = ntohl(tsval); + src->scrub->pfss_tsval = ntohl(tsval); + src->scrub->pfss_tsecr = ntohl(tsecr); + getmicrouptime(&src->scrub->pfss_last); + } + /* FALLTHROUGH */ + default: + hlen -= MAX(opt[1], 2); + opt += MAX(opt[1], 2); + break; + } + } + } + + return (0); +} + +void +pf_normalize_tcp_cleanup(struct pf_state *state) +{ + if (state->src.scrub) + uma_zfree(V_pf_state_scrub_z, state->src.scrub); + if (state->dst.scrub) + uma_zfree(V_pf_state_scrub_z, state->dst.scrub); + + /* Someday... flush the TCP segment reassembly descriptors. */ +} + +int +pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, + u_short *reason, struct tcphdr *th, struct pf_state *state, + struct pf_state_peer *src, struct pf_state_peer *dst, int *writeback) +{ + struct timeval uptime; + u_int32_t tsval, tsecr; + u_int tsval_from_last; + u_int8_t hdr[60]; + u_int8_t *opt; + int copyback = 0; + int got_ts = 0; + + KASSERT((src->scrub || dst->scrub), + ("%s: src->scrub && dst->scrub!", __func__)); + + /* + * Enforce the minimum TTL seen for this connection. Negate a common + * technique to evade an intrusion detection system and confuse + * firewall state code. + */ + switch (pd->af) { +#ifdef INET + case AF_INET: { + if (src->scrub) { + struct ip *h = mtod(m, struct ip *); + if (h->ip_ttl > src->scrub->pfss_ttl) + src->scrub->pfss_ttl = h->ip_ttl; + h->ip_ttl = src->scrub->pfss_ttl; + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + if (src->scrub) { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + if (h->ip6_hlim > src->scrub->pfss_ttl) + src->scrub->pfss_ttl = h->ip6_hlim; + h->ip6_hlim = src->scrub->pfss_ttl; + } + break; + } +#endif /* INET6 */ + } + + if (th->th_off > (sizeof(struct tcphdr) >> 2) && + ((src->scrub && (src->scrub->pfss_flags & PFSS_TIMESTAMP)) || + (dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP))) && + pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { + /* Diddle with TCP options */ + int hlen; + opt = hdr + sizeof(struct tcphdr); + hlen = (th->th_off << 2) - sizeof(struct tcphdr); + while (hlen >= TCPOLEN_TIMESTAMP) { + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUGH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_TIMESTAMP: + /* Modulate the timestamps. Can be used for + * NAT detection, OS uptime determination or + * reboot detection. + */ + + if (got_ts) { + /* Huh? Multiple timestamps!? */ + if (V_pf_status.debug >= PF_DEBUG_MISC) { + DPFPRINTF(("multiple TS??")); + pf_print_state(state); + printf("\n"); + } + REASON_SET(reason, PFRES_TS); + return (PF_DROP); + } + if (opt[1] >= TCPOLEN_TIMESTAMP) { + memcpy(&tsval, &opt[2], + sizeof(u_int32_t)); + if (tsval && src->scrub && + (src->scrub->pfss_flags & + PFSS_TIMESTAMP)) { + tsval = ntohl(tsval); + pf_change_a(&opt[2], + &th->th_sum, + htonl(tsval + + src->scrub->pfss_ts_mod), + 0); + copyback = 1; + } + + /* Modulate TS reply iff valid (!0) */ + memcpy(&tsecr, &opt[6], + sizeof(u_int32_t)); + if (tsecr && dst->scrub && + (dst->scrub->pfss_flags & + PFSS_TIMESTAMP)) { + tsecr = ntohl(tsecr) + - dst->scrub->pfss_ts_mod; + pf_change_a(&opt[6], + &th->th_sum, htonl(tsecr), + 0); + copyback = 1; + } + got_ts = 1; + } + /* FALLTHROUGH */ + default: + hlen -= MAX(opt[1], 2); + opt += MAX(opt[1], 2); + break; + } + } + if (copyback) { + /* Copyback the options, caller copys back header */ + *writeback = 1; + m_copyback(m, off + sizeof(struct tcphdr), + (th->th_off << 2) - sizeof(struct tcphdr), hdr + + sizeof(struct tcphdr)); + } + } + + + /* + * Must invalidate PAWS checks on connections idle for too long. + * The fastest allowed timestamp clock is 1ms. That turns out to + * be about 24 days before it wraps. XXX Right now our lowerbound + * TS echo check only works for the first 12 days of a connection + * when the TS has exhausted half its 32bit space + */ +#define TS_MAX_IDLE (24*24*60*60) +#define TS_MAX_CONN (12*24*60*60) /* XXX remove when better tsecr check */ + + getmicrouptime(&uptime); + if (src->scrub && (src->scrub->pfss_flags & PFSS_PAWS) && + (uptime.tv_sec - src->scrub->pfss_last.tv_sec > TS_MAX_IDLE || + time_uptime - state->creation > TS_MAX_CONN)) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + DPFPRINTF(("src idled out of PAWS\n")); + pf_print_state(state); + printf("\n"); + } + src->scrub->pfss_flags = (src->scrub->pfss_flags & ~PFSS_PAWS) + | PFSS_PAWS_IDLED; + } + if (dst->scrub && (dst->scrub->pfss_flags & PFSS_PAWS) && + uptime.tv_sec - dst->scrub->pfss_last.tv_sec > TS_MAX_IDLE) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + DPFPRINTF(("dst idled out of PAWS\n")); + pf_print_state(state); + printf("\n"); + } + dst->scrub->pfss_flags = (dst->scrub->pfss_flags & ~PFSS_PAWS) + | PFSS_PAWS_IDLED; + } + + if (got_ts && src->scrub && dst->scrub && + (src->scrub->pfss_flags & PFSS_PAWS) && + (dst->scrub->pfss_flags & PFSS_PAWS)) { + /* Validate that the timestamps are "in-window". + * RFC1323 describes TCP Timestamp options that allow + * measurement of RTT (round trip time) and PAWS + * (protection against wrapped sequence numbers). PAWS + * gives us a set of rules for rejecting packets on + * long fat pipes (packets that were somehow delayed + * in transit longer than the time it took to send the + * full TCP sequence space of 4Gb). We can use these + * rules and infer a few others that will let us treat + * the 32bit timestamp and the 32bit echoed timestamp + * as sequence numbers to prevent a blind attacker from + * inserting packets into a connection. + * + * RFC1323 tells us: + * - The timestamp on this packet must be greater than + * or equal to the last value echoed by the other + * endpoint. The RFC says those will be discarded + * since it is a dup that has already been acked. + * This gives us a lowerbound on the timestamp. + * timestamp >= other last echoed timestamp + * - The timestamp will be less than or equal to + * the last timestamp plus the time between the + * last packet and now. The RFC defines the max + * clock rate as 1ms. We will allow clocks to be + * up to 10% fast and will allow a total difference + * or 30 seconds due to a route change. And this + * gives us an upperbound on the timestamp. + * timestamp <= last timestamp + max ticks + * We have to be careful here. Windows will send an + * initial timestamp of zero and then initialize it + * to a random value after the 3whs; presumably to + * avoid a DoS by having to call an expensive RNG + * during a SYN flood. Proof MS has at least one + * good security geek. + * + * - The TCP timestamp option must also echo the other + * endpoints timestamp. The timestamp echoed is the + * one carried on the earliest unacknowledged segment + * on the left edge of the sequence window. The RFC + * states that the host will reject any echoed + * timestamps that were larger than any ever sent. + * This gives us an upperbound on the TS echo. + * tescr <= largest_tsval + * - The lowerbound on the TS echo is a little more + * tricky to determine. The other endpoint's echoed + * values will not decrease. But there may be + * network conditions that re-order packets and + * cause our view of them to decrease. For now the + * only lowerbound we can safely determine is that + * the TS echo will never be less than the original + * TS. XXX There is probably a better lowerbound. + * Remove TS_MAX_CONN with better lowerbound check. + * tescr >= other original TS + * + * It is also important to note that the fastest + * timestamp clock of 1ms will wrap its 32bit space in + * 24 days. So we just disable TS checking after 24 + * days of idle time. We actually must use a 12d + * connection limit until we can come up with a better + * lowerbound to the TS echo check. + */ + struct timeval delta_ts; + int ts_fudge; + + + /* + * PFTM_TS_DIFF is how many seconds of leeway to allow + * a host's timestamp. This can happen if the previous + * packet got delayed in transit for much longer than + * this packet. + */ + if ((ts_fudge = state->rule.ptr->timeout[PFTM_TS_DIFF]) == 0) + ts_fudge = V_pf_default_rule.timeout[PFTM_TS_DIFF]; + + /* Calculate max ticks since the last timestamp */ +#define TS_MAXFREQ 1100 /* RFC max TS freq of 1Khz + 10% skew */ +#define TS_MICROSECS 1000000 /* microseconds per second */ + delta_ts = uptime; + timevalsub(&delta_ts, &src->scrub->pfss_last); + tsval_from_last = (delta_ts.tv_sec + ts_fudge) * TS_MAXFREQ; + tsval_from_last += delta_ts.tv_usec / (TS_MICROSECS/TS_MAXFREQ); + + if ((src->state >= TCPS_ESTABLISHED && + dst->state >= TCPS_ESTABLISHED) && + (SEQ_LT(tsval, dst->scrub->pfss_tsecr) || + SEQ_GT(tsval, src->scrub->pfss_tsval + tsval_from_last) || + (tsecr && (SEQ_GT(tsecr, dst->scrub->pfss_tsval) || + SEQ_LT(tsecr, dst->scrub->pfss_tsval0))))) { + /* Bad RFC1323 implementation or an insertion attack. + * + * - Solaris 2.6 and 2.7 are known to send another ACK + * after the FIN,FIN|ACK,ACK closing that carries + * an old timestamp. + */ + + DPFPRINTF(("Timestamp failed %c%c%c%c\n", + SEQ_LT(tsval, dst->scrub->pfss_tsecr) ? '0' : ' ', + SEQ_GT(tsval, src->scrub->pfss_tsval + + tsval_from_last) ? '1' : ' ', + SEQ_GT(tsecr, dst->scrub->pfss_tsval) ? '2' : ' ', + SEQ_LT(tsecr, dst->scrub->pfss_tsval0)? '3' : ' ')); + DPFPRINTF((" tsval: %u tsecr: %u +ticks: %u " + "idle: %jus %lums\n", + tsval, tsecr, tsval_from_last, + (uintmax_t)delta_ts.tv_sec, + delta_ts.tv_usec / 1000)); + DPFPRINTF((" src->tsval: %u tsecr: %u\n", + src->scrub->pfss_tsval, src->scrub->pfss_tsecr)); + DPFPRINTF((" dst->tsval: %u tsecr: %u tsval0: %u" + "\n", dst->scrub->pfss_tsval, + dst->scrub->pfss_tsecr, dst->scrub->pfss_tsval0)); + if (V_pf_status.debug >= PF_DEBUG_MISC) { + pf_print_state(state); + pf_print_flags(th->th_flags); + printf("\n"); + } + REASON_SET(reason, PFRES_TS); + return (PF_DROP); + } + + /* XXX I'd really like to require tsecr but it's optional */ + + } else if (!got_ts && (th->th_flags & TH_RST) == 0 && + ((src->state == TCPS_ESTABLISHED && dst->state == TCPS_ESTABLISHED) + || pd->p_len > 0 || (th->th_flags & TH_SYN)) && + src->scrub && dst->scrub && + (src->scrub->pfss_flags & PFSS_PAWS) && + (dst->scrub->pfss_flags & PFSS_PAWS)) { + /* Didn't send a timestamp. Timestamps aren't really useful + * when: + * - connection opening or closing (often not even sent). + * but we must not let an attacker to put a FIN on a + * data packet to sneak it through our ESTABLISHED check. + * - on a TCP reset. RFC suggests not even looking at TS. + * - on an empty ACK. The TS will not be echoed so it will + * probably not help keep the RTT calculation in sync and + * there isn't as much danger when the sequence numbers + * got wrapped. So some stacks don't include TS on empty + * ACKs :-( + * + * To minimize the disruption to mostly RFC1323 conformant + * stacks, we will only require timestamps on data packets. + * + * And what do ya know, we cannot require timestamps on data + * packets. There appear to be devices that do legitimate + * TCP connection hijacking. There are HTTP devices that allow + * a 3whs (with timestamps) and then buffer the HTTP request. + * If the intermediate device has the HTTP response cache, it + * will spoof the response but not bother timestamping its + * packets. So we can look for the presence of a timestamp in + * the first data packet and if there, require it in all future + * packets. + */ + + if (pd->p_len > 0 && (src->scrub->pfss_flags & PFSS_DATA_TS)) { + /* + * Hey! Someone tried to sneak a packet in. Or the + * stack changed its RFC1323 behavior?!?! + */ + if (V_pf_status.debug >= PF_DEBUG_MISC) { + DPFPRINTF(("Did not receive expected RFC1323 " + "timestamp\n")); + pf_print_state(state); + pf_print_flags(th->th_flags); + printf("\n"); + } + REASON_SET(reason, PFRES_TS); + return (PF_DROP); + } + } + + + /* + * We will note if a host sends his data packets with or without + * timestamps. And require all data packets to contain a timestamp + * if the first does. PAWS implicitly requires that all data packets be + * timestamped. But I think there are middle-man devices that hijack + * TCP streams immediately after the 3whs and don't timestamp their + * packets (seen in a WWW accelerator or cache). + */ + if (pd->p_len > 0 && src->scrub && (src->scrub->pfss_flags & + (PFSS_TIMESTAMP|PFSS_DATA_TS|PFSS_DATA_NOTS)) == PFSS_TIMESTAMP) { + if (got_ts) + src->scrub->pfss_flags |= PFSS_DATA_TS; + else { + src->scrub->pfss_flags |= PFSS_DATA_NOTS; + if (V_pf_status.debug >= PF_DEBUG_MISC && dst->scrub && + (dst->scrub->pfss_flags & PFSS_TIMESTAMP)) { + /* Don't warn if other host rejected RFC1323 */ + DPFPRINTF(("Broken RFC1323 stack did not " + "timestamp data packet. Disabled PAWS " + "security.\n")); + pf_print_state(state); + pf_print_flags(th->th_flags); + printf("\n"); + } + } + } + + + /* + * Update PAWS values + */ + if (got_ts && src->scrub && PFSS_TIMESTAMP == (src->scrub->pfss_flags & + (PFSS_PAWS_IDLED|PFSS_TIMESTAMP))) { + getmicrouptime(&src->scrub->pfss_last); + if (SEQ_GEQ(tsval, src->scrub->pfss_tsval) || + (src->scrub->pfss_flags & PFSS_PAWS) == 0) + src->scrub->pfss_tsval = tsval; + + if (tsecr) { + if (SEQ_GEQ(tsecr, src->scrub->pfss_tsecr) || + (src->scrub->pfss_flags & PFSS_PAWS) == 0) + src->scrub->pfss_tsecr = tsecr; + + if ((src->scrub->pfss_flags & PFSS_PAWS) == 0 && + (SEQ_LT(tsval, src->scrub->pfss_tsval0) || + src->scrub->pfss_tsval0 == 0)) { + /* tsval0 MUST be the lowest timestamp */ + src->scrub->pfss_tsval0 = tsval; + } + + /* Only fully initialized after a TS gets echoed */ + if ((src->scrub->pfss_flags & PFSS_PAWS) == 0) + src->scrub->pfss_flags |= PFSS_PAWS; + } + } + + /* I have a dream.... TCP segment reassembly.... */ + return (0); +} + +static int +pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, + int off, sa_family_t af) +{ + u_int16_t *mss; + int thoff; + int opt, cnt, optlen = 0; + int rewrite = 0; + u_char opts[TCP_MAXOLEN]; + u_char *optp = opts; + + thoff = th->th_off << 2; + cnt = thoff - sizeof(struct tcphdr); + + if (cnt > 0 && !pf_pull_hdr(m, off + sizeof(*th), opts, cnt, + NULL, NULL, af)) + return (rewrite); + + for (; cnt > 0; cnt -= optlen, optp += optlen) { + opt = optp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + if (cnt < 2) + break; + optlen = optp[1]; + if (optlen < 2 || optlen > cnt) + break; + } + switch (opt) { + case TCPOPT_MAXSEG: + mss = (u_int16_t *)(optp + 2); + if ((ntohs(*mss)) > r->max_mss) { + th->th_sum = pf_cksum_fixup(th->th_sum, + *mss, htons(r->max_mss), 0); + *mss = htons(r->max_mss); + rewrite = 1; + } + break; + default: + break; + } + } + + if (rewrite) + m_copyback(m, off + sizeof(*th), thoff - sizeof(*th), opts); + + return (rewrite); +} + +#ifdef INET +static void +pf_scrub_ip(struct mbuf **m0, u_int32_t flags, u_int8_t min_ttl, u_int8_t tos) +{ + struct mbuf *m = *m0; + struct ip *h = mtod(m, struct ip *); + + /* Clear IP_DF if no-df was requested */ + if (flags & PFRULE_NODF && h->ip_off & htons(IP_DF)) { + u_int16_t ip_off = h->ip_off; + + h->ip_off &= htons(~IP_DF); + h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); + } + + /* Enforce a minimum ttl, may cause endless packet loops */ + if (min_ttl && h->ip_ttl < min_ttl) { + u_int16_t ip_ttl = h->ip_ttl; + + h->ip_ttl = min_ttl; + h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_ttl, h->ip_ttl, 0); + } + + /* Enforce tos */ + if (flags & PFRULE_SET_TOS) { + u_int16_t ov, nv; + + ov = *(u_int16_t *)h; + h->ip_tos = tos; + nv = *(u_int16_t *)h; + + h->ip_sum = pf_cksum_fixup(h->ip_sum, ov, nv, 0); + } + + /* random-id, but not for fragments */ + if (flags & PFRULE_RANDOMID && !(h->ip_off & ~htons(IP_DF))) { + u_int16_t ip_id = h->ip_id; + + h->ip_id = ip_randomid(); + h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_id, h->ip_id, 0); + } +} +#endif /* INET */ + +#ifdef INET6 +static void +pf_scrub_ip6(struct mbuf **m0, u_int8_t min_ttl) +{ + struct mbuf *m = *m0; + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + + /* Enforce a minimum ttl, may cause endless packet loops */ + if (min_ttl && h->ip6_hlim < min_ttl) + h->ip6_hlim = min_ttl; +} +#endif diff --git a/sys/netpfil/pf/pf_osfp.c b/sys/netpfil/pf/pf_osfp.c new file mode 100644 index 0000000000000..29d4a4030b51b --- /dev/null +++ b/sys/netpfil/pf/pf_osfp.c @@ -0,0 +1,526 @@ +/* $OpenBSD: pf_osfp.c,v 1.14 2008/06/12 18:17:01 henning Exp $ */ + +/* + * Copyright (c) 2003 Mike Frantzen + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +static MALLOC_DEFINE(M_PFOSFP, "pf_osfp", "pf(4) operating system fingerprints"); +#define DPFPRINTF(format, x...) \ + if (V_pf_status.debug >= PF_DEBUG_NOISY) \ + printf(format , ##x) + +SLIST_HEAD(pf_osfp_list, pf_os_fingerprint); +static VNET_DEFINE(struct pf_osfp_list, pf_osfp_list) = + SLIST_HEAD_INITIALIZER(); +#define V_pf_osfp_list VNET(pf_osfp_list) + +static struct pf_osfp_enlist *pf_osfp_fingerprint_hdr(const struct ip *, + const struct ip6_hdr *, + const struct tcphdr *); +static struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *, + struct pf_os_fingerprint *, u_int8_t); +static struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *, + struct pf_os_fingerprint *); +static void pf_osfp_insert(struct pf_osfp_list *, + struct pf_os_fingerprint *); +#ifdef PFDEBUG +static struct pf_os_fingerprint *pf_osfp_validate(void); +#endif + +/* + * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only) + * Returns the list of possible OSes. + */ +struct pf_osfp_enlist * +pf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off, + const struct tcphdr *tcp) +{ + struct ip *ip; + struct ip6_hdr *ip6; + char hdr[60]; + + if ((pd->af != PF_INET && pd->af != PF_INET6) || + pd->proto != IPPROTO_TCP || (tcp->th_off << 2) < sizeof(*tcp)) + return (NULL); + + if (pd->af == PF_INET) { + ip = mtod(m, struct ip *); + ip6 = (struct ip6_hdr *)NULL; + } else { + ip = (struct ip *)NULL; + ip6 = mtod(m, struct ip6_hdr *); + } + if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL, + pd->af)) return (NULL); + + return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr)); +} + +static struct pf_osfp_enlist * +pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcp) +{ + struct pf_os_fingerprint fp, *fpresult; + int cnt, optlen = 0; + const u_int8_t *optp; + char srcname[128]; + + if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN) + return (NULL); + if (ip) { + if ((ip->ip_off & htons(IP_OFFMASK)) != 0) + return (NULL); + } + + memset(&fp, 0, sizeof(fp)); + + if (ip) { + fp.fp_psize = ntohs(ip->ip_len); + fp.fp_ttl = ip->ip_ttl; + if (ip->ip_off & htons(IP_DF)) + fp.fp_flags |= PF_OSFP_DF; + strlcpy(srcname, inet_ntoa(ip->ip_src), sizeof(srcname)); + } +#ifdef INET6 + else if (ip6) { + /* jumbo payload? */ + fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); + fp.fp_ttl = ip6->ip6_hlim; + fp.fp_flags |= PF_OSFP_DF; + fp.fp_flags |= PF_OSFP_INET6; + strlcpy(srcname, ip6_sprintf((struct in6_addr *)&ip6->ip6_src), + sizeof(srcname)); + } +#endif + else + return (NULL); + fp.fp_wsize = ntohs(tcp->th_win); + + + cnt = (tcp->th_off << 2) - sizeof(*tcp); + optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp)); + for (; cnt > 0; cnt -= optlen, optp += optlen) { + if (*optp == TCPOPT_EOL) + break; + + fp.fp_optcnt++; + if (*optp == TCPOPT_NOP) { + fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) | + PF_OSFP_TCPOPT_NOP; + optlen = 1; + } else { + if (cnt < 2) + return (NULL); + optlen = optp[1]; + if (optlen > cnt || optlen < 2) + return (NULL); + switch (*optp) { + case TCPOPT_MAXSEG: + if (optlen >= TCPOLEN_MAXSEG) + memcpy(&fp.fp_mss, &optp[2], + sizeof(fp.fp_mss)); + fp.fp_tcpopts = (fp.fp_tcpopts << + PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS; + NTOHS(fp.fp_mss); + break; + case TCPOPT_WINDOW: + if (optlen >= TCPOLEN_WINDOW) + memcpy(&fp.fp_wscale, &optp[2], + sizeof(fp.fp_wscale)); + NTOHS(fp.fp_wscale); + fp.fp_tcpopts = (fp.fp_tcpopts << + PF_OSFP_TCPOPT_BITS) | + PF_OSFP_TCPOPT_WSCALE; + break; + case TCPOPT_SACK_PERMITTED: + fp.fp_tcpopts = (fp.fp_tcpopts << + PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK; + break; + case TCPOPT_TIMESTAMP: + if (optlen >= TCPOLEN_TIMESTAMP) { + u_int32_t ts; + memcpy(&ts, &optp[2], sizeof(ts)); + if (ts == 0) + fp.fp_flags |= PF_OSFP_TS0; + + } + fp.fp_tcpopts = (fp.fp_tcpopts << + PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS; + break; + default: + return (NULL); + } + } + optlen = MAX(optlen, 1); /* paranoia */ + } + + DPFPRINTF("fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) " + "(TS=%s,M=%s%d,W=%s%d)\n", + srcname, ntohs(tcp->th_sport), + fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0, + fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt, + (fp.fp_flags & PF_OSFP_TS0) ? "0" : "", + (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" : + (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "", + fp.fp_mss, + (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" : + (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", + fp.fp_wscale); + + if ((fpresult = pf_osfp_find(&V_pf_osfp_list, &fp, + PF_OSFP_MAXTTL_OFFSET))) + return (&fpresult->fp_oses); + return (NULL); +} + +/* Match a fingerprint ID against a list of OSes */ +int +pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os) +{ + struct pf_osfp_entry *entry; + int os_class, os_version, os_subtype; + int en_class, en_version, en_subtype; + + if (os == PF_OSFP_ANY) + return (1); + if (list == NULL) { + DPFPRINTF("osfp no match against %x\n", os); + return (os == PF_OSFP_UNKNOWN); + } + PF_OSFP_UNPACK(os, os_class, os_version, os_subtype); + SLIST_FOREACH(entry, list, fp_entry) { + PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype); + if ((os_class == PF_OSFP_ANY || en_class == os_class) && + (os_version == PF_OSFP_ANY || en_version == os_version) && + (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) { + DPFPRINTF("osfp matched %s %s %s %x==%x\n", + entry->fp_class_nm, entry->fp_version_nm, + entry->fp_subtype_nm, os, entry->fp_os); + return (1); + } + } + DPFPRINTF("fingerprint 0x%x didn't match\n", os); + return (0); +} + +/* Flush the fingerprint list */ +void +pf_osfp_flush(void) +{ + struct pf_os_fingerprint *fp; + struct pf_osfp_entry *entry; + + while ((fp = SLIST_FIRST(&V_pf_osfp_list))) { + SLIST_REMOVE_HEAD(&V_pf_osfp_list, fp_next); + while ((entry = SLIST_FIRST(&fp->fp_oses))) { + SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry); + free(entry, M_PFOSFP); + } + free(fp, M_PFOSFP); + } +} + + +/* Add a fingerprint */ +int +pf_osfp_add(struct pf_osfp_ioctl *fpioc) +{ + struct pf_os_fingerprint *fp, fpadd; + struct pf_osfp_entry *entry; + + PF_RULES_WASSERT(); + + memset(&fpadd, 0, sizeof(fpadd)); + fpadd.fp_tcpopts = fpioc->fp_tcpopts; + fpadd.fp_wsize = fpioc->fp_wsize; + fpadd.fp_psize = fpioc->fp_psize; + fpadd.fp_mss = fpioc->fp_mss; + fpadd.fp_flags = fpioc->fp_flags; + fpadd.fp_optcnt = fpioc->fp_optcnt; + fpadd.fp_wscale = fpioc->fp_wscale; + fpadd.fp_ttl = fpioc->fp_ttl; + +#if 0 /* XXX RYAN wants to fix logging */ + DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d " + "(TS=%s,M=%s%d,W=%s%d) %x\n", + fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm, + fpioc->fp_os.fp_subtype_nm, + (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" : + (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" : + (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" : + (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "", + fpadd.fp_wsize, + fpadd.fp_ttl, + (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0, + (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" : + (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "", + fpadd.fp_psize, + (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt, + (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "", + (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" : + (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "", + fpadd.fp_mss, + (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" : + (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", + fpadd.fp_wscale, + fpioc->fp_os.fp_os); +#endif + + if ((fp = pf_osfp_find_exact(&V_pf_osfp_list, &fpadd))) { + SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { + if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os)) + return (EEXIST); + } + if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT)) + == NULL) + return (ENOMEM); + } else { + if ((fp = malloc(sizeof(*fp), M_PFOSFP, M_ZERO | M_NOWAIT)) + == NULL) + return (ENOMEM); + fp->fp_tcpopts = fpioc->fp_tcpopts; + fp->fp_wsize = fpioc->fp_wsize; + fp->fp_psize = fpioc->fp_psize; + fp->fp_mss = fpioc->fp_mss; + fp->fp_flags = fpioc->fp_flags; + fp->fp_optcnt = fpioc->fp_optcnt; + fp->fp_wscale = fpioc->fp_wscale; + fp->fp_ttl = fpioc->fp_ttl; + SLIST_INIT(&fp->fp_oses); + if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT)) + == NULL) { + free(fp, M_PFOSFP); + return (ENOMEM); + } + pf_osfp_insert(&V_pf_osfp_list, fp); + } + memcpy(entry, &fpioc->fp_os, sizeof(*entry)); + + /* Make sure the strings are NUL terminated */ + entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0'; + entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0'; + entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0'; + + SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry); + +#ifdef PFDEBUG + if ((fp = pf_osfp_validate())) + printf("Invalid fingerprint list\n"); +#endif /* PFDEBUG */ + return (0); +} + + +/* Find a fingerprint in the list */ +static struct pf_os_fingerprint * +pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find, + u_int8_t ttldiff) +{ + struct pf_os_fingerprint *f; + +#define MATCH_INT(_MOD, _DC, _field) \ + if ((f->fp_flags & _DC) == 0) { \ + if ((f->fp_flags & _MOD) == 0) { \ + if (f->_field != find->_field) \ + continue; \ + } else { \ + if (f->_field == 0 || find->_field % f->_field) \ + continue; \ + } \ + } + + SLIST_FOREACH(f, list, fp_next) { + if (f->fp_tcpopts != find->fp_tcpopts || + f->fp_optcnt != find->fp_optcnt || + f->fp_ttl < find->fp_ttl || + f->fp_ttl - find->fp_ttl > ttldiff || + (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) != + (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0))) + continue; + + MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize) + MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss) + MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale) + if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) { + if (f->fp_flags & PF_OSFP_WSIZE_MSS) { + if (find->fp_mss == 0) + continue; + +/* + * Some "smart" NAT devices and DSL routers will tweak the MSS size and + * will set it to whatever is suitable for the link type. + */ +#define SMART_MSS 1460 + if ((find->fp_wsize % find->fp_mss || + find->fp_wsize / find->fp_mss != + f->fp_wsize) && + (find->fp_wsize % SMART_MSS || + find->fp_wsize / SMART_MSS != + f->fp_wsize)) + continue; + } else if (f->fp_flags & PF_OSFP_WSIZE_MTU) { + if (find->fp_mss == 0) + continue; + +#define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr)) +#define SMART_MTU (SMART_MSS + MTUOFF) + if ((find->fp_wsize % (find->fp_mss + MTUOFF) || + find->fp_wsize / (find->fp_mss + MTUOFF) != + f->fp_wsize) && + (find->fp_wsize % SMART_MTU || + find->fp_wsize / SMART_MTU != + f->fp_wsize)) + continue; + } else if (f->fp_flags & PF_OSFP_WSIZE_MOD) { + if (f->fp_wsize == 0 || find->fp_wsize % + f->fp_wsize) + continue; + } else { + if (f->fp_wsize != find->fp_wsize) + continue; + } + } + return (f); + } + + return (NULL); +} + +/* Find an exact fingerprint in the list */ +static struct pf_os_fingerprint * +pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find) +{ + struct pf_os_fingerprint *f; + + SLIST_FOREACH(f, list, fp_next) { + if (f->fp_tcpopts == find->fp_tcpopts && + f->fp_wsize == find->fp_wsize && + f->fp_psize == find->fp_psize && + f->fp_mss == find->fp_mss && + f->fp_flags == find->fp_flags && + f->fp_optcnt == find->fp_optcnt && + f->fp_wscale == find->fp_wscale && + f->fp_ttl == find->fp_ttl) + return (f); + } + + return (NULL); +} + +/* Insert a fingerprint into the list */ +static void +pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins) +{ + struct pf_os_fingerprint *f, *prev = NULL; + + /* XXX need to go semi tree based. can key on tcp options */ + + SLIST_FOREACH(f, list, fp_next) + prev = f; + if (prev) + SLIST_INSERT_AFTER(prev, ins, fp_next); + else + SLIST_INSERT_HEAD(list, ins, fp_next); +} + +/* Fill a fingerprint by its number (from an ioctl) */ +int +pf_osfp_get(struct pf_osfp_ioctl *fpioc) +{ + struct pf_os_fingerprint *fp; + struct pf_osfp_entry *entry; + int num = fpioc->fp_getnum; + int i = 0; + + + memset(fpioc, 0, sizeof(*fpioc)); + SLIST_FOREACH(fp, &V_pf_osfp_list, fp_next) { + SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { + if (i++ == num) { + fpioc->fp_mss = fp->fp_mss; + fpioc->fp_wsize = fp->fp_wsize; + fpioc->fp_flags = fp->fp_flags; + fpioc->fp_psize = fp->fp_psize; + fpioc->fp_ttl = fp->fp_ttl; + fpioc->fp_wscale = fp->fp_wscale; + fpioc->fp_getnum = num; + memcpy(&fpioc->fp_os, entry, + sizeof(fpioc->fp_os)); + return (0); + } + } + } + + return (EBUSY); +} + + +#ifdef PFDEBUG +/* Validate that each signature is reachable */ +static struct pf_os_fingerprint * +pf_osfp_validate(void) +{ + struct pf_os_fingerprint *f, *f2, find; + + SLIST_FOREACH(f, &V_pf_osfp_list, fp_next) { + memcpy(&find, f, sizeof(find)); + + /* We do a few MSS/th_win percolations to make things unique */ + if (find.fp_mss == 0) + find.fp_mss = 128; + if (f->fp_flags & PF_OSFP_WSIZE_MSS) + find.fp_wsize *= find.fp_mss; + else if (f->fp_flags & PF_OSFP_WSIZE_MTU) + find.fp_wsize *= (find.fp_mss + 40); + else if (f->fp_flags & PF_OSFP_WSIZE_MOD) + find.fp_wsize *= 2; + if (f != (f2 = pf_osfp_find(&V_pf_osfp_list, &find, 0))) { + if (f2) + printf("Found \"%s %s %s\" instead of " + "\"%s %s %s\"\n", + SLIST_FIRST(&f2->fp_oses)->fp_class_nm, + SLIST_FIRST(&f2->fp_oses)->fp_version_nm, + SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm, + SLIST_FIRST(&f->fp_oses)->fp_class_nm, + SLIST_FIRST(&f->fp_oses)->fp_version_nm, + SLIST_FIRST(&f->fp_oses)->fp_subtype_nm); + else + printf("Couldn't find \"%s %s %s\"\n", + SLIST_FIRST(&f->fp_oses)->fp_class_nm, + SLIST_FIRST(&f->fp_oses)->fp_version_nm, + SLIST_FIRST(&f->fp_oses)->fp_subtype_nm); + return (f); + } + } + return (NULL); +} +#endif /* PFDEBUG */ diff --git a/sys/netpfil/pf/pf_ruleset.c b/sys/netpfil/pf/pf_ruleset.c new file mode 100644 index 0000000000000..77652a69b3c58 --- /dev/null +++ b/sys/netpfil/pf/pf_ruleset.c @@ -0,0 +1,424 @@ +/* $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002,2003 Henning Brauer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#ifdef _KERNEL +# include +# include +#endif /* _KERNEL */ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef INET6 +#include +#endif /* INET6 */ + + +#ifdef _KERNEL +#define DPFPRINTF(format, x...) \ + if (V_pf_status.debug >= PF_DEBUG_NOISY) \ + printf(format , ##x) +#define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO) +#define rs_free(x) free(x, M_TEMP) + +#else +/* Userland equivalents so we can lend code to pfctl et al. */ + +#include +#include +#include +#include +#include +#define rs_malloc(x) calloc(1, x) +#define rs_free(x) free(x) + +#ifdef PFDEBUG +#include +#define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) +#else +#define DPFPRINTF(format, x...) ((void)0) +#endif /* PFDEBUG */ +#endif /* _KERNEL */ + +#ifdef _KERNEL +VNET_DEFINE(struct pf_anchor_global, pf_anchors); +VNET_DEFINE(struct pf_anchor, pf_main_anchor); +#else /* ! _KERNEL */ +struct pf_anchor_global pf_anchors; +struct pf_anchor pf_main_anchor; +#undef V_pf_anchors +#define V_pf_anchors pf_anchors +#undef pf_main_ruleset +#define pf_main_ruleset pf_main_anchor.ruleset +#endif /* _KERNEL */ + +static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); + +static struct pf_anchor *pf_find_anchor(const char *); + +RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); +RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); + +static __inline int +pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) +{ + int c = strcmp(a->path, b->path); + + return (c ? (c < 0 ? -1 : 1) : 0); +} + +int +pf_get_ruleset_number(u_int8_t action) +{ + switch (action) { + case PF_SCRUB: + case PF_NOSCRUB: + return (PF_RULESET_SCRUB); + break; + case PF_PASS: + case PF_DROP: + return (PF_RULESET_FILTER); + break; + case PF_NAT: + case PF_NONAT: + return (PF_RULESET_NAT); + break; + case PF_BINAT: + case PF_NOBINAT: + return (PF_RULESET_BINAT); + break; + case PF_RDR: + case PF_NORDR: + return (PF_RULESET_RDR); + break; + default: + return (PF_RULESET_MAX); + break; + } +} + +void +pf_init_ruleset(struct pf_ruleset *ruleset) +{ + int i; + + memset(ruleset, 0, sizeof(struct pf_ruleset)); + for (i = 0; i < PF_RULESET_MAX; i++) { + TAILQ_INIT(&ruleset->rules[i].queues[0]); + TAILQ_INIT(&ruleset->rules[i].queues[1]); + ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; + ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; + } +} + +static struct pf_anchor * +pf_find_anchor(const char *path) +{ + struct pf_anchor *key, *found; + + key = (struct pf_anchor *)rs_malloc(sizeof(*key)); + if (key == NULL) + return (NULL); + strlcpy(key->path, path, sizeof(key->path)); + found = RB_FIND(pf_anchor_global, &V_pf_anchors, key); + rs_free(key); + return (found); +} + +struct pf_ruleset * +pf_find_ruleset(const char *path) +{ + struct pf_anchor *anchor; + + while (*path == '/') + path++; + if (!*path) + return (&pf_main_ruleset); + anchor = pf_find_anchor(path); + if (anchor == NULL) + return (NULL); + else + return (&anchor->ruleset); +} + +struct pf_ruleset * +pf_find_or_create_ruleset(const char *path) +{ + char *p, *q, *r; + struct pf_ruleset *ruleset; + struct pf_anchor *anchor = NULL, *dup, *parent = NULL; + + if (path[0] == 0) + return (&pf_main_ruleset); + while (*path == '/') + path++; + ruleset = pf_find_ruleset(path); + if (ruleset != NULL) + return (ruleset); + p = (char *)rs_malloc(MAXPATHLEN); + if (p == NULL) + return (NULL); + strlcpy(p, path, MAXPATHLEN); + while (parent == NULL && (q = strrchr(p, '/')) != NULL) { + *q = 0; + if ((ruleset = pf_find_ruleset(p)) != NULL) { + parent = ruleset->anchor; + break; + } + } + if (q == NULL) + q = p; + else + q++; + strlcpy(p, path, MAXPATHLEN); + if (!*q) { + rs_free(p); + return (NULL); + } + while ((r = strchr(q, '/')) != NULL || *q) { + if (r != NULL) + *r = 0; + if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || + (parent != NULL && strlen(parent->path) >= + MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { + rs_free(p); + return (NULL); + } + anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); + if (anchor == NULL) { + rs_free(p); + return (NULL); + } + RB_INIT(&anchor->children); + strlcpy(anchor->name, q, sizeof(anchor->name)); + if (parent != NULL) { + strlcpy(anchor->path, parent->path, + sizeof(anchor->path)); + strlcat(anchor->path, "/", sizeof(anchor->path)); + } + strlcat(anchor->path, anchor->name, sizeof(anchor->path)); + if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) != + NULL) { + printf("pf_find_or_create_ruleset: RB_INSERT1 " + "'%s' '%s' collides with '%s' '%s'\n", + anchor->path, anchor->name, dup->path, dup->name); + rs_free(anchor); + rs_free(p); + return (NULL); + } + if (parent != NULL) { + anchor->parent = parent; + if ((dup = RB_INSERT(pf_anchor_node, &parent->children, + anchor)) != NULL) { + printf("pf_find_or_create_ruleset: " + "RB_INSERT2 '%s' '%s' collides with " + "'%s' '%s'\n", anchor->path, anchor->name, + dup->path, dup->name); + RB_REMOVE(pf_anchor_global, &V_pf_anchors, + anchor); + rs_free(anchor); + rs_free(p); + return (NULL); + } + } + pf_init_ruleset(&anchor->ruleset); + anchor->ruleset.anchor = anchor; + parent = anchor; + if (r != NULL) + q = r + 1; + else + *q = 0; + } + rs_free(p); + return (&anchor->ruleset); +} + +void +pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) +{ + struct pf_anchor *parent; + int i; + + while (ruleset != NULL) { + if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || + !RB_EMPTY(&ruleset->anchor->children) || + ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || + ruleset->topen) + return; + for (i = 0; i < PF_RULESET_MAX; ++i) + if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || + !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || + ruleset->rules[i].inactive.open) + return; + RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor); + if ((parent = ruleset->anchor->parent) != NULL) + RB_REMOVE(pf_anchor_node, &parent->children, + ruleset->anchor); + rs_free(ruleset->anchor); + if (parent == NULL) + return; + ruleset = &parent->ruleset; + } +} + +int +pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, + const char *name) +{ + char *p, *path; + struct pf_ruleset *ruleset; + + r->anchor = NULL; + r->anchor_relative = 0; + r->anchor_wildcard = 0; + if (!name[0]) + return (0); + path = (char *)rs_malloc(MAXPATHLEN); + if (path == NULL) + return (1); + if (name[0] == '/') + strlcpy(path, name + 1, MAXPATHLEN); + else { + /* relative path */ + r->anchor_relative = 1; + if (s->anchor == NULL || !s->anchor->path[0]) + path[0] = 0; + else + strlcpy(path, s->anchor->path, MAXPATHLEN); + while (name[0] == '.' && name[1] == '.' && name[2] == '/') { + if (!path[0]) { + printf("pf_anchor_setup: .. beyond root\n"); + rs_free(path); + return (1); + } + if ((p = strrchr(path, '/')) != NULL) + *p = 0; + else + path[0] = 0; + r->anchor_relative++; + name += 3; + } + if (path[0]) + strlcat(path, "/", MAXPATHLEN); + strlcat(path, name, MAXPATHLEN); + } + if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { + r->anchor_wildcard = 1; + *p = 0; + } + ruleset = pf_find_or_create_ruleset(path); + rs_free(path); + if (ruleset == NULL || ruleset->anchor == NULL) { + printf("pf_anchor_setup: ruleset\n"); + return (1); + } + r->anchor = ruleset->anchor; + r->anchor->refcnt++; + return (0); +} + +int +pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, + struct pfioc_rule *pr) +{ + pr->anchor_call[0] = 0; + if (r->anchor == NULL) + return (0); + if (!r->anchor_relative) { + strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); + strlcat(pr->anchor_call, r->anchor->path, + sizeof(pr->anchor_call)); + } else { + char *a, *p; + int i; + + a = (char *)rs_malloc(MAXPATHLEN); + if (a == NULL) + return (1); + if (rs->anchor == NULL) + a[0] = 0; + else + strlcpy(a, rs->anchor->path, MAXPATHLEN); + for (i = 1; i < r->anchor_relative; ++i) { + if ((p = strrchr(a, '/')) == NULL) + p = a; + *p = 0; + strlcat(pr->anchor_call, "../", + sizeof(pr->anchor_call)); + } + if (strncmp(a, r->anchor->path, strlen(a))) { + printf("pf_anchor_copyout: '%s' '%s'\n", a, + r->anchor->path); + rs_free(a); + return (1); + } + if (strlen(r->anchor->path) > strlen(a)) + strlcat(pr->anchor_call, r->anchor->path + (a[0] ? + strlen(a) + 1 : 0), sizeof(pr->anchor_call)); + rs_free(a); + } + if (r->anchor_wildcard) + strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", + sizeof(pr->anchor_call)); + return (0); +} + +void +pf_anchor_remove(struct pf_rule *r) +{ + if (r->anchor == NULL) + return; + if (r->anchor->refcnt <= 0) { + printf("pf_anchor_remove: broken refcount\n"); + r->anchor = NULL; + return; + } + if (!--r->anchor->refcnt) + pf_remove_if_empty_ruleset(&r->anchor->ruleset); + r->anchor = NULL; +} diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c new file mode 100644 index 0000000000000..fa88045dc5095 --- /dev/null +++ b/sys/netpfil/pf/pf_table.c @@ -0,0 +1,2191 @@ +/* $OpenBSD: pf_table.c,v 1.79 2008/10/08 06:24:50 mcbride Exp $ */ + +/* + * Copyright (c) 2002 Cedric Berger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define ACCEPT_FLAGS(flags, oklist) \ + do { \ + if ((flags & ~(oklist)) & \ + PFR_FLAG_ALLMASK) \ + return (EINVAL); \ + } while (0) + +#define FILLIN_SIN(sin, addr) \ + do { \ + (sin).sin_len = sizeof(sin); \ + (sin).sin_family = AF_INET; \ + (sin).sin_addr = (addr); \ + } while (0) + +#define FILLIN_SIN6(sin6, addr) \ + do { \ + (sin6).sin6_len = sizeof(sin6); \ + (sin6).sin6_family = AF_INET6; \ + (sin6).sin6_addr = (addr); \ + } while (0) + +#define SWAP(type, a1, a2) \ + do { \ + type tmp = a1; \ + a1 = a2; \ + a2 = tmp; \ + } while (0) + +#define SUNION2PF(su, af) (((af)==AF_INET) ? \ + (struct pf_addr *)&(su)->sin.sin_addr : \ + (struct pf_addr *)&(su)->sin6.sin6_addr) + +#define AF_BITS(af) (((af)==AF_INET)?32:128) +#define ADDR_NETWORK(ad) ((ad)->pfra_net < AF_BITS((ad)->pfra_af)) +#define KENTRY_NETWORK(ke) ((ke)->pfrke_net < AF_BITS((ke)->pfrke_af)) +#define KENTRY_RNF_ROOT(ke) \ + ((((struct radix_node *)(ke))->rn_flags & RNF_ROOT) != 0) + +#define NO_ADDRESSES (-1) +#define ENQUEUE_UNMARKED_ONLY (1) +#define INVERT_NEG_FLAG (1) + +struct pfr_walktree { + enum pfrw_op { + PFRW_MARK, + PFRW_SWEEP, + PFRW_ENQUEUE, + PFRW_GET_ADDRS, + PFRW_GET_ASTATS, + PFRW_POOL_GET, + PFRW_DYNADDR_UPDATE + } pfrw_op; + union { + struct pfr_addr *pfrw1_addr; + struct pfr_astats *pfrw1_astats; + struct pfr_kentryworkq *pfrw1_workq; + struct pfr_kentry *pfrw1_kentry; + struct pfi_dynaddr *pfrw1_dyn; + } pfrw_1; + int pfrw_free; +}; +#define pfrw_addr pfrw_1.pfrw1_addr +#define pfrw_astats pfrw_1.pfrw1_astats +#define pfrw_workq pfrw_1.pfrw1_workq +#define pfrw_kentry pfrw_1.pfrw1_kentry +#define pfrw_dyn pfrw_1.pfrw1_dyn +#define pfrw_cnt pfrw_free + +#define senderr(e) do { rv = (e); goto _bad; } while (0) + +static MALLOC_DEFINE(M_PFTABLE, "pf_table", "pf(4) tables structures"); +static VNET_DEFINE(uma_zone_t, pfr_kentry_z); +#define V_pfr_kentry_z VNET(pfr_kentry_z) +static VNET_DEFINE(uma_zone_t, pfr_kcounters_z); +#define V_pfr_kcounters_z VNET(pfr_kcounters_z) + +static struct pf_addr pfr_ffaddr = { + .addr32 = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff } +}; + +static void pfr_copyout_addr(struct pfr_addr *, + struct pfr_kentry *ke); +static int pfr_validate_addr(struct pfr_addr *); +static void pfr_enqueue_addrs(struct pfr_ktable *, + struct pfr_kentryworkq *, int *, int); +static void pfr_mark_addrs(struct pfr_ktable *); +static struct pfr_kentry + *pfr_lookup_addr(struct pfr_ktable *, + struct pfr_addr *, int); +static struct pfr_kentry *pfr_create_kentry(struct pfr_addr *); +static void pfr_destroy_kentries(struct pfr_kentryworkq *); +static void pfr_destroy_kentry(struct pfr_kentry *); +static void pfr_insert_kentries(struct pfr_ktable *, + struct pfr_kentryworkq *, long); +static void pfr_remove_kentries(struct pfr_ktable *, + struct pfr_kentryworkq *); +static void pfr_clstats_kentries(struct pfr_kentryworkq *, long, + int); +static void pfr_reset_feedback(struct pfr_addr *, int); +static void pfr_prepare_network(union sockaddr_union *, int, int); +static int pfr_route_kentry(struct pfr_ktable *, + struct pfr_kentry *); +static int pfr_unroute_kentry(struct pfr_ktable *, + struct pfr_kentry *); +static int pfr_walktree(struct radix_node *, void *); +static int pfr_validate_table(struct pfr_table *, int, int); +static int pfr_fix_anchor(char *); +static void pfr_commit_ktable(struct pfr_ktable *, long); +static void pfr_insert_ktables(struct pfr_ktableworkq *); +static void pfr_insert_ktable(struct pfr_ktable *); +static void pfr_setflags_ktables(struct pfr_ktableworkq *); +static void pfr_setflags_ktable(struct pfr_ktable *, int); +static void pfr_clstats_ktables(struct pfr_ktableworkq *, long, + int); +static void pfr_clstats_ktable(struct pfr_ktable *, long, int); +static struct pfr_ktable + *pfr_create_ktable(struct pfr_table *, long, int); +static void pfr_destroy_ktables(struct pfr_ktableworkq *, int); +static void pfr_destroy_ktable(struct pfr_ktable *, int); +static int pfr_ktable_compare(struct pfr_ktable *, + struct pfr_ktable *); +static struct pfr_ktable + *pfr_lookup_table(struct pfr_table *); +static void pfr_clean_node_mask(struct pfr_ktable *, + struct pfr_kentryworkq *); +static int pfr_table_count(struct pfr_table *, int); +static int pfr_skip_table(struct pfr_table *, + struct pfr_ktable *, int); +static struct pfr_kentry + *pfr_kentry_byidx(struct pfr_ktable *, int, int); + +static RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); +static RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); + +struct pfr_ktablehead pfr_ktables; +struct pfr_table pfr_nulltable; +int pfr_ktable_cnt; + +void +pfr_initialize(void) +{ + + V_pfr_kentry_z = uma_zcreate("pf table entries", + sizeof(struct pfr_kentry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, + 0); + V_pfr_kcounters_z = uma_zcreate("pf table counters", + sizeof(struct pfr_kcounters), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + V_pf_limits[PF_LIMIT_TABLE_ENTRIES].zone = V_pfr_kentry_z; + V_pf_limits[PF_LIMIT_TABLE_ENTRIES].limit = PFR_KENTRY_HIWAT; +} + +void +pfr_cleanup(void) +{ + + uma_zdestroy(V_pfr_kentry_z); + uma_zdestroy(V_pfr_kcounters_z); +} + +int +pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentryworkq workq; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + pfr_enqueue_addrs(kt, &workq, ndel, 0); + + if (!(flags & PFR_FLAG_DUMMY)) { + pfr_remove_kentries(kt, &workq); + KASSERT(kt->pfrkt_cnt == 0, ("%s: non-null pfrkt_cnt", __func__)); + } + return (0); +} + +int +pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nadd, int flags) +{ + struct pfr_ktable *kt, *tmpkt; + struct pfr_kentryworkq workq; + struct pfr_kentry *p, *q; + struct pfr_addr *ad; + int i, rv, xadd = 0; + long tzero = time_second; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); + if (tmpkt == NULL) + return (ENOMEM); + SLIST_INIT(&workq); + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) + senderr(EINVAL); + p = pfr_lookup_addr(kt, ad, 1); + q = pfr_lookup_addr(tmpkt, ad, 1); + if (flags & PFR_FLAG_FEEDBACK) { + if (q != NULL) + ad->pfra_fback = PFR_FB_DUPLICATE; + else if (p == NULL) + ad->pfra_fback = PFR_FB_ADDED; + else if (p->pfrke_not != ad->pfra_not) + ad->pfra_fback = PFR_FB_CONFLICT; + else + ad->pfra_fback = PFR_FB_NONE; + } + if (p == NULL && q == NULL) { + p = pfr_create_kentry(ad); + if (p == NULL) + senderr(ENOMEM); + if (pfr_route_kentry(tmpkt, p)) { + pfr_destroy_kentry(p); + ad->pfra_fback = PFR_FB_NONE; + } else { + SLIST_INSERT_HEAD(&workq, p, pfrke_workq); + xadd++; + } + } + } + pfr_clean_node_mask(tmpkt, &workq); + if (!(flags & PFR_FLAG_DUMMY)) + pfr_insert_kentries(kt, &workq, tzero); + else + pfr_destroy_kentries(&workq); + if (nadd != NULL) + *nadd = xadd; + pfr_destroy_ktable(tmpkt, 0); + return (0); +_bad: + pfr_clean_node_mask(tmpkt, &workq); + pfr_destroy_kentries(&workq); + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + pfr_destroy_ktable(tmpkt, 0); + return (rv); +} + +int +pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *ndel, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentryworkq workq; + struct pfr_kentry *p; + struct pfr_addr *ad; + int i, rv, xdel = 0, log = 1; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + /* + * there are two algorithms to choose from here. + * with: + * n: number of addresses to delete + * N: number of addresses in the table + * + * one is O(N) and is better for large 'n' + * one is O(n*LOG(N)) and is better for small 'n' + * + * following code try to decide which one is best. + */ + for (i = kt->pfrkt_cnt; i > 0; i >>= 1) + log++; + if (size > kt->pfrkt_cnt/log) { + /* full table scan */ + pfr_mark_addrs(kt); + } else { + /* iterate over addresses to delete */ + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) + return (EINVAL); + p = pfr_lookup_addr(kt, ad, 1); + if (p != NULL) + p->pfrke_mark = 0; + } + } + SLIST_INIT(&workq); + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) + senderr(EINVAL); + p = pfr_lookup_addr(kt, ad, 1); + if (flags & PFR_FLAG_FEEDBACK) { + if (p == NULL) + ad->pfra_fback = PFR_FB_NONE; + else if (p->pfrke_not != ad->pfra_not) + ad->pfra_fback = PFR_FB_CONFLICT; + else if (p->pfrke_mark) + ad->pfra_fback = PFR_FB_DUPLICATE; + else + ad->pfra_fback = PFR_FB_DELETED; + } + if (p != NULL && p->pfrke_not == ad->pfra_not && + !p->pfrke_mark) { + p->pfrke_mark = 1; + SLIST_INSERT_HEAD(&workq, p, pfrke_workq); + xdel++; + } + } + if (!(flags & PFR_FLAG_DUMMY)) + pfr_remove_kentries(kt, &workq); + if (ndel != NULL) + *ndel = xdel; + return (0); +_bad: + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + return (rv); +} + +int +pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *size2, int *nadd, int *ndel, int *nchange, int flags, + u_int32_t ignore_pfrt_flags) +{ + struct pfr_ktable *kt, *tmpkt; + struct pfr_kentryworkq addq, delq, changeq; + struct pfr_kentry *p, *q; + struct pfr_addr ad; + int i, rv, xadd = 0, xdel = 0, xchange = 0; + long tzero = time_second; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, ignore_pfrt_flags, flags & + PFR_FLAG_USERIOCTL)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); + if (tmpkt == NULL) + return (ENOMEM); + pfr_mark_addrs(kt); + SLIST_INIT(&addq); + SLIST_INIT(&delq); + SLIST_INIT(&changeq); + for (i = 0; i < size; i++) { + /* + * XXXGL: undertand pf_if usage of this function + * and make ad a moving pointer + */ + bcopy(addr + i, &ad, sizeof(ad)); + if (pfr_validate_addr(&ad)) + senderr(EINVAL); + ad.pfra_fback = PFR_FB_NONE; + p = pfr_lookup_addr(kt, &ad, 1); + if (p != NULL) { + if (p->pfrke_mark) { + ad.pfra_fback = PFR_FB_DUPLICATE; + goto _skip; + } + p->pfrke_mark = 1; + if (p->pfrke_not != ad.pfra_not) { + SLIST_INSERT_HEAD(&changeq, p, pfrke_workq); + ad.pfra_fback = PFR_FB_CHANGED; + xchange++; + } + } else { + q = pfr_lookup_addr(tmpkt, &ad, 1); + if (q != NULL) { + ad.pfra_fback = PFR_FB_DUPLICATE; + goto _skip; + } + p = pfr_create_kentry(&ad); + if (p == NULL) + senderr(ENOMEM); + if (pfr_route_kentry(tmpkt, p)) { + pfr_destroy_kentry(p); + ad.pfra_fback = PFR_FB_NONE; + } else { + SLIST_INSERT_HEAD(&addq, p, pfrke_workq); + ad.pfra_fback = PFR_FB_ADDED; + xadd++; + } + } +_skip: + if (flags & PFR_FLAG_FEEDBACK) + bcopy(&ad, addr + i, sizeof(ad)); + } + pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); + if ((flags & PFR_FLAG_FEEDBACK) && *size2) { + if (*size2 < size+xdel) { + *size2 = size+xdel; + senderr(0); + } + i = 0; + SLIST_FOREACH(p, &delq, pfrke_workq) { + pfr_copyout_addr(&ad, p); + ad.pfra_fback = PFR_FB_DELETED; + bcopy(&ad, addr + size + i, sizeof(ad)); + i++; + } + } + pfr_clean_node_mask(tmpkt, &addq); + if (!(flags & PFR_FLAG_DUMMY)) { + pfr_insert_kentries(kt, &addq, tzero); + pfr_remove_kentries(kt, &delq); + pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); + } else + pfr_destroy_kentries(&addq); + if (nadd != NULL) + *nadd = xadd; + if (ndel != NULL) + *ndel = xdel; + if (nchange != NULL) + *nchange = xchange; + if ((flags & PFR_FLAG_FEEDBACK) && size2) + *size2 = size+xdel; + pfr_destroy_ktable(tmpkt, 0); + return (0); +_bad: + pfr_clean_node_mask(tmpkt, &addq); + pfr_destroy_kentries(&addq); + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + pfr_destroy_ktable(tmpkt, 0); + return (rv); +} + +int +pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nmatch, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentry *p; + struct pfr_addr *ad; + int i, xmatch = 0; + + PF_RULES_RASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_REPLACE); + if (pfr_validate_table(tbl, 0, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) + return (EINVAL); + if (ADDR_NETWORK(ad)) + return (EINVAL); + p = pfr_lookup_addr(kt, ad, 0); + if (flags & PFR_FLAG_REPLACE) + pfr_copyout_addr(ad, p); + ad->pfra_fback = (p == NULL) ? PFR_FB_NONE : + (p->pfrke_not ? PFR_FB_NOTMATCH : PFR_FB_MATCH); + if (p != NULL && !p->pfrke_not) + xmatch++; + } + if (nmatch != NULL) + *nmatch = xmatch; + return (0); +} + +int +pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, + int flags) +{ + struct pfr_ktable *kt; + struct pfr_walktree w; + int rv; + + PF_RULES_RASSERT(); + + ACCEPT_FLAGS(flags, 0); + if (pfr_validate_table(tbl, 0, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_cnt > *size) { + *size = kt->pfrkt_cnt; + return (0); + } + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_GET_ADDRS; + w.pfrw_addr = addr; + w.pfrw_free = kt->pfrkt_cnt; + rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + if (!rv) + rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, + &w); + if (rv) + return (rv); + + KASSERT(w.pfrw_free == 0, ("%s: corruption detected (%d)", __func__, + w.pfrw_free)); + + *size = kt->pfrkt_cnt; + return (0); +} + +int +pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, + int flags) +{ + struct pfr_ktable *kt; + struct pfr_walktree w; + struct pfr_kentryworkq workq; + int rv; + long tzero = time_second; + + PF_RULES_RASSERT(); + + /* XXX PFR_FLAG_CLSTATS disabled */ + ACCEPT_FLAGS(flags, 0); + if (pfr_validate_table(tbl, 0, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_cnt > *size) { + *size = kt->pfrkt_cnt; + return (0); + } + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_GET_ASTATS; + w.pfrw_astats = addr; + w.pfrw_free = kt->pfrkt_cnt; + rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + if (!rv) + rv = kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, + &w); + if (!rv && (flags & PFR_FLAG_CLSTATS)) { + pfr_enqueue_addrs(kt, &workq, NULL, 0); + pfr_clstats_kentries(&workq, tzero, 0); + } + if (rv) + return (rv); + + if (w.pfrw_free) { + printf("pfr_get_astats: corruption detected (%d).\n", + w.pfrw_free); + return (ENOTTY); + } + *size = kt->pfrkt_cnt; + return (0); +} + +int +pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nzero, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentryworkq workq; + struct pfr_kentry *p; + struct pfr_addr *ad; + int i, rv, xzero = 0; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + SLIST_INIT(&workq); + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) + senderr(EINVAL); + p = pfr_lookup_addr(kt, ad, 1); + if (flags & PFR_FLAG_FEEDBACK) { + ad->pfra_fback = (p != NULL) ? + PFR_FB_CLEARED : PFR_FB_NONE; + } + if (p != NULL) { + SLIST_INSERT_HEAD(&workq, p, pfrke_workq); + xzero++; + } + } + + if (!(flags & PFR_FLAG_DUMMY)) + pfr_clstats_kentries(&workq, 0, 0); + if (nzero != NULL) + *nzero = xzero; + return (0); +_bad: + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + return (rv); +} + +static int +pfr_validate_addr(struct pfr_addr *ad) +{ + int i; + + switch (ad->pfra_af) { +#ifdef INET + case AF_INET: + if (ad->pfra_net > 32) + return (-1); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (ad->pfra_net > 128) + return (-1); + break; +#endif /* INET6 */ + default: + return (-1); + } + if (ad->pfra_net < 128 && + (((caddr_t)ad)[ad->pfra_net/8] & (0xFF >> (ad->pfra_net%8)))) + return (-1); + for (i = (ad->pfra_net+7)/8; i < sizeof(ad->pfra_u); i++) + if (((caddr_t)ad)[i]) + return (-1); + if (ad->pfra_not && ad->pfra_not != 1) + return (-1); + if (ad->pfra_fback) + return (-1); + return (0); +} + +static void +pfr_enqueue_addrs(struct pfr_ktable *kt, struct pfr_kentryworkq *workq, + int *naddr, int sweep) +{ + struct pfr_walktree w; + + SLIST_INIT(workq); + bzero(&w, sizeof(w)); + w.pfrw_op = sweep ? PFRW_SWEEP : PFRW_ENQUEUE; + w.pfrw_workq = workq; + if (kt->pfrkt_ip4 != NULL) + if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, + &w)) + printf("pfr_enqueue_addrs: IPv4 walktree failed.\n"); + if (kt->pfrkt_ip6 != NULL) + if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, + &w)) + printf("pfr_enqueue_addrs: IPv6 walktree failed.\n"); + if (naddr != NULL) + *naddr = w.pfrw_cnt; +} + +static void +pfr_mark_addrs(struct pfr_ktable *kt) +{ + struct pfr_walktree w; + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_MARK; + if (kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) + printf("pfr_mark_addrs: IPv4 walktree failed.\n"); + if (kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) + printf("pfr_mark_addrs: IPv6 walktree failed.\n"); +} + + +static struct pfr_kentry * +pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact) +{ + union sockaddr_union sa, mask; + struct radix_node_head *head = NULL; + struct pfr_kentry *ke; + + bzero(&sa, sizeof(sa)); + if (ad->pfra_af == AF_INET) { + FILLIN_SIN(sa.sin, ad->pfra_ip4addr); + head = kt->pfrkt_ip4; + } else if ( ad->pfra_af == AF_INET6 ) { + FILLIN_SIN6(sa.sin6, ad->pfra_ip6addr); + head = kt->pfrkt_ip6; + } + if (ADDR_NETWORK(ad)) { + pfr_prepare_network(&mask, ad->pfra_af, ad->pfra_net); + ke = (struct pfr_kentry *)rn_lookup(&sa, &mask, head); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + } else { + ke = (struct pfr_kentry *)rn_match(&sa, head); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + if (exact && ke && KENTRY_NETWORK(ke)) + ke = NULL; + } + return (ke); +} + +static struct pfr_kentry * +pfr_create_kentry(struct pfr_addr *ad) +{ + struct pfr_kentry *ke; + + ke = uma_zalloc(V_pfr_kentry_z, M_NOWAIT | M_ZERO); + if (ke == NULL) + return (NULL); + + if (ad->pfra_af == AF_INET) + FILLIN_SIN(ke->pfrke_sa.sin, ad->pfra_ip4addr); + else if (ad->pfra_af == AF_INET6) + FILLIN_SIN6(ke->pfrke_sa.sin6, ad->pfra_ip6addr); + ke->pfrke_af = ad->pfra_af; + ke->pfrke_net = ad->pfra_net; + ke->pfrke_not = ad->pfra_not; + return (ke); +} + +static void +pfr_destroy_kentries(struct pfr_kentryworkq *workq) +{ + struct pfr_kentry *p, *q; + + for (p = SLIST_FIRST(workq); p != NULL; p = q) { + q = SLIST_NEXT(p, pfrke_workq); + pfr_destroy_kentry(p); + } +} + +static void +pfr_destroy_kentry(struct pfr_kentry *ke) +{ + if (ke->pfrke_counters) + uma_zfree(V_pfr_kcounters_z, ke->pfrke_counters); + uma_zfree(V_pfr_kentry_z, ke); +} + +static void +pfr_insert_kentries(struct pfr_ktable *kt, + struct pfr_kentryworkq *workq, long tzero) +{ + struct pfr_kentry *p; + int rv, n = 0; + + SLIST_FOREACH(p, workq, pfrke_workq) { + rv = pfr_route_kentry(kt, p); + if (rv) { + printf("pfr_insert_kentries: cannot route entry " + "(code=%d).\n", rv); + break; + } + p->pfrke_tzero = tzero; + n++; + } + kt->pfrkt_cnt += n; +} + +int +pfr_insert_kentry(struct pfr_ktable *kt, struct pfr_addr *ad, long tzero) +{ + struct pfr_kentry *p; + int rv; + + p = pfr_lookup_addr(kt, ad, 1); + if (p != NULL) + return (0); + p = pfr_create_kentry(ad); + if (p == NULL) + return (EINVAL); + + rv = pfr_route_kentry(kt, p); + if (rv) + return (rv); + + p->pfrke_tzero = tzero; + kt->pfrkt_cnt++; + + return (0); +} + +static void +pfr_remove_kentries(struct pfr_ktable *kt, + struct pfr_kentryworkq *workq) +{ + struct pfr_kentry *p; + int n = 0; + + SLIST_FOREACH(p, workq, pfrke_workq) { + pfr_unroute_kentry(kt, p); + n++; + } + kt->pfrkt_cnt -= n; + pfr_destroy_kentries(workq); +} + +static void +pfr_clean_node_mask(struct pfr_ktable *kt, + struct pfr_kentryworkq *workq) +{ + struct pfr_kentry *p; + + SLIST_FOREACH(p, workq, pfrke_workq) + pfr_unroute_kentry(kt, p); +} + +static void +pfr_clstats_kentries(struct pfr_kentryworkq *workq, long tzero, int negchange) +{ + struct pfr_kentry *p; + + SLIST_FOREACH(p, workq, pfrke_workq) { + if (negchange) + p->pfrke_not = !p->pfrke_not; + if (p->pfrke_counters) { + uma_zfree(V_pfr_kcounters_z, p->pfrke_counters); + p->pfrke_counters = NULL; + } + p->pfrke_tzero = tzero; + } +} + +static void +pfr_reset_feedback(struct pfr_addr *addr, int size) +{ + struct pfr_addr *ad; + int i; + + for (i = 0, ad = addr; i < size; i++, ad++) + ad->pfra_fback = PFR_FB_NONE; +} + +static void +pfr_prepare_network(union sockaddr_union *sa, int af, int net) +{ + int i; + + bzero(sa, sizeof(*sa)); + if (af == AF_INET) { + sa->sin.sin_len = sizeof(sa->sin); + sa->sin.sin_family = AF_INET; + sa->sin.sin_addr.s_addr = net ? htonl(-1 << (32-net)) : 0; + } else if (af == AF_INET6) { + sa->sin6.sin6_len = sizeof(sa->sin6); + sa->sin6.sin6_family = AF_INET6; + for (i = 0; i < 4; i++) { + if (net <= 32) { + sa->sin6.sin6_addr.s6_addr32[i] = + net ? htonl(-1 << (32-net)) : 0; + break; + } + sa->sin6.sin6_addr.s6_addr32[i] = 0xFFFFFFFF; + net -= 32; + } + } +} + +static int +pfr_route_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) +{ + union sockaddr_union mask; + struct radix_node *rn; + struct radix_node_head *head = NULL; + + bzero(ke->pfrke_node, sizeof(ke->pfrke_node)); + if (ke->pfrke_af == AF_INET) + head = kt->pfrkt_ip4; + else if (ke->pfrke_af == AF_INET6) + head = kt->pfrkt_ip6; + + if (KENTRY_NETWORK(ke)) { + pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); + rn = rn_addroute(&ke->pfrke_sa, &mask, head, ke->pfrke_node); + } else + rn = rn_addroute(&ke->pfrke_sa, NULL, head, ke->pfrke_node); + + return (rn == NULL ? -1 : 0); +} + +static int +pfr_unroute_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) +{ + union sockaddr_union mask; + struct radix_node *rn; + struct radix_node_head *head = NULL; + + if (ke->pfrke_af == AF_INET) + head = kt->pfrkt_ip4; + else if (ke->pfrke_af == AF_INET6) + head = kt->pfrkt_ip6; + + if (KENTRY_NETWORK(ke)) { + pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); + rn = rn_delete(&ke->pfrke_sa, &mask, head); + } else + rn = rn_delete(&ke->pfrke_sa, NULL, head); + + if (rn == NULL) { + printf("pfr_unroute_kentry: delete failed.\n"); + return (-1); + } + return (0); +} + +static void +pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke) +{ + bzero(ad, sizeof(*ad)); + if (ke == NULL) + return; + ad->pfra_af = ke->pfrke_af; + ad->pfra_net = ke->pfrke_net; + ad->pfra_not = ke->pfrke_not; + if (ad->pfra_af == AF_INET) + ad->pfra_ip4addr = ke->pfrke_sa.sin.sin_addr; + else if (ad->pfra_af == AF_INET6) + ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr; +} + +static int +pfr_walktree(struct radix_node *rn, void *arg) +{ + struct pfr_kentry *ke = (struct pfr_kentry *)rn; + struct pfr_walktree *w = arg; + + switch (w->pfrw_op) { + case PFRW_MARK: + ke->pfrke_mark = 0; + break; + case PFRW_SWEEP: + if (ke->pfrke_mark) + break; + /* FALLTHROUGH */ + case PFRW_ENQUEUE: + SLIST_INSERT_HEAD(w->pfrw_workq, ke, pfrke_workq); + w->pfrw_cnt++; + break; + case PFRW_GET_ADDRS: + if (w->pfrw_free-- > 0) { + pfr_copyout_addr(w->pfrw_addr, ke); + w->pfrw_addr++; + } + break; + case PFRW_GET_ASTATS: + if (w->pfrw_free-- > 0) { + struct pfr_astats as; + + pfr_copyout_addr(&as.pfras_a, ke); + + if (ke->pfrke_counters) { + bcopy(ke->pfrke_counters->pfrkc_packets, + as.pfras_packets, sizeof(as.pfras_packets)); + bcopy(ke->pfrke_counters->pfrkc_bytes, + as.pfras_bytes, sizeof(as.pfras_bytes)); + } else { + bzero(as.pfras_packets, sizeof(as.pfras_packets)); + bzero(as.pfras_bytes, sizeof(as.pfras_bytes)); + as.pfras_a.pfra_fback = PFR_FB_NOCOUNT; + } + as.pfras_tzero = ke->pfrke_tzero; + + bcopy(&as, w->pfrw_astats, sizeof(as)); + w->pfrw_astats++; + } + break; + case PFRW_POOL_GET: + if (ke->pfrke_not) + break; /* negative entries are ignored */ + if (!w->pfrw_cnt--) { + w->pfrw_kentry = ke; + return (1); /* finish search */ + } + break; + case PFRW_DYNADDR_UPDATE: + { + union sockaddr_union pfr_mask; + + if (ke->pfrke_af == AF_INET) { + if (w->pfrw_dyn->pfid_acnt4++ > 0) + break; + pfr_prepare_network(&pfr_mask, AF_INET, ke->pfrke_net); + w->pfrw_dyn->pfid_addr4 = *SUNION2PF(&ke->pfrke_sa, + AF_INET); + w->pfrw_dyn->pfid_mask4 = *SUNION2PF(&pfr_mask, + AF_INET); + } else if (ke->pfrke_af == AF_INET6){ + if (w->pfrw_dyn->pfid_acnt6++ > 0) + break; + pfr_prepare_network(&pfr_mask, AF_INET6, ke->pfrke_net); + w->pfrw_dyn->pfid_addr6 = *SUNION2PF(&ke->pfrke_sa, + AF_INET6); + w->pfrw_dyn->pfid_mask6 = *SUNION2PF(&pfr_mask, + AF_INET6); + } + break; + } + } + return (0); +} + +int +pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p; + int xdel = 0; + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ALLRSETS); + if (pfr_fix_anchor(filter->pfrt_anchor)) + return (EINVAL); + if (pfr_table_count(filter, flags) < 0) + return (ENOENT); + + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (pfr_skip_table(filter, p, flags)) + continue; + if (!strcmp(p->pfrkt_anchor, PF_RESERVED_ANCHOR)) + continue; + if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) + continue; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } + if (!(flags & PFR_FLAG_DUMMY)) + pfr_setflags_ktables(&workq); + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) +{ + struct pfr_ktableworkq addq, changeq; + struct pfr_ktable *p, *q, *r, key; + int i, rv, xadd = 0; + long tzero = time_second; + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + SLIST_INIT(&addq); + SLIST_INIT(&changeq); + for (i = 0; i < size; i++) { + bcopy(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t)); + if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK, + flags & PFR_FLAG_USERIOCTL)) + senderr(EINVAL); + key.pfrkt_flags |= PFR_TFLAG_ACTIVE; + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p == NULL) { + p = pfr_create_ktable(&key.pfrkt_t, tzero, 1); + if (p == NULL) + senderr(ENOMEM); + SLIST_FOREACH(q, &addq, pfrkt_workq) { + if (!pfr_ktable_compare(p, q)) + goto _skip; + } + SLIST_INSERT_HEAD(&addq, p, pfrkt_workq); + xadd++; + if (!key.pfrkt_anchor[0]) + goto _skip; + + /* find or create root table */ + bzero(key.pfrkt_anchor, sizeof(key.pfrkt_anchor)); + r = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (r != NULL) { + p->pfrkt_root = r; + goto _skip; + } + SLIST_FOREACH(q, &addq, pfrkt_workq) { + if (!pfr_ktable_compare(&key, q)) { + p->pfrkt_root = q; + goto _skip; + } + } + key.pfrkt_flags = 0; + r = pfr_create_ktable(&key.pfrkt_t, 0, 1); + if (r == NULL) + senderr(ENOMEM); + SLIST_INSERT_HEAD(&addq, r, pfrkt_workq); + p->pfrkt_root = r; + } else if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { + SLIST_FOREACH(q, &changeq, pfrkt_workq) + if (!pfr_ktable_compare(&key, q)) + goto _skip; + p->pfrkt_nflags = (p->pfrkt_flags & + ~PFR_TFLAG_USRMASK) | key.pfrkt_flags; + SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq); + xadd++; + } +_skip: + ; + } + if (!(flags & PFR_FLAG_DUMMY)) { + pfr_insert_ktables(&addq); + pfr_setflags_ktables(&changeq); + } else + pfr_destroy_ktables(&addq, 0); + if (nadd != NULL) + *nadd = xadd; + return (0); +_bad: + pfr_destroy_ktables(&addq, 0); + return (rv); +} + +int +pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p, *q, key; + int i, xdel = 0; + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + bcopy(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t)); + if (pfr_validate_table(&key.pfrkt_t, 0, + flags & PFR_FLAG_USERIOCTL)) + return (EINVAL); + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { + SLIST_FOREACH(q, &workq, pfrkt_workq) + if (!pfr_ktable_compare(p, q)) + goto _skip; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } +_skip: + ; + } + + if (!(flags & PFR_FLAG_DUMMY)) + pfr_setflags_ktables(&workq); + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, + int flags) +{ + struct pfr_ktable *p; + int n, nn; + + PF_RULES_RASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS); + if (pfr_fix_anchor(filter->pfrt_anchor)) + return (EINVAL); + n = nn = pfr_table_count(filter, flags); + if (n < 0) + return (ENOENT); + if (n > *size) { + *size = n; + return (0); + } + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (pfr_skip_table(filter, p, flags)) + continue; + if (n-- <= 0) + continue; + bcopy(&p->pfrkt_t, tbl++, sizeof(*tbl)); + } + + KASSERT(n == 0, ("%s: corruption detected (%d)", __func__, n)); + + *size = nn; + return (0); +} + +int +pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, + int flags) +{ + struct pfr_ktable *p; + struct pfr_ktableworkq workq; + int n, nn; + long tzero = time_second; + + /* XXX PFR_FLAG_CLSTATS disabled */ + ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS); + if (pfr_fix_anchor(filter->pfrt_anchor)) + return (EINVAL); + n = nn = pfr_table_count(filter, flags); + if (n < 0) + return (ENOENT); + if (n > *size) { + *size = n; + return (0); + } + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (pfr_skip_table(filter, p, flags)) + continue; + if (n-- <= 0) + continue; + bcopy(&p->pfrkt_ts, tbl++, sizeof(*tbl)); + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + } + if (flags & PFR_FLAG_CLSTATS) + pfr_clstats_ktables(&workq, tzero, + flags & PFR_FLAG_ADDRSTOO); + + KASSERT(n == 0, ("%s: corruption detected (%d)", __func__, n)); + + *size = nn; + return (0); +} + +int +pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p, key; + int i, xzero = 0; + long tzero = time_second; + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + bcopy(tbl + i, &key.pfrkt_t, sizeof(key.pfrkt_t)); + if (pfr_validate_table(&key.pfrkt_t, 0, 0)) + return (EINVAL); + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p != NULL) { + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xzero++; + } + } + if (!(flags & PFR_FLAG_DUMMY)) + pfr_clstats_ktables(&workq, tzero, flags & PFR_FLAG_ADDRSTOO); + if (nzero != NULL) + *nzero = xzero; + return (0); +} + +int +pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, + int *nchange, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p, *q, key; + int i, xchange = 0, xdel = 0; + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + if ((setflag & ~PFR_TFLAG_USRMASK) || + (clrflag & ~PFR_TFLAG_USRMASK) || + (setflag & clrflag)) + return (EINVAL); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + bcopy(tbl + i, &key.pfrkt_t, sizeof(key.pfrkt_t)); + if (pfr_validate_table(&key.pfrkt_t, 0, + flags & PFR_FLAG_USERIOCTL)) + return (EINVAL); + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { + p->pfrkt_nflags = (p->pfrkt_flags | setflag) & + ~clrflag; + if (p->pfrkt_nflags == p->pfrkt_flags) + goto _skip; + SLIST_FOREACH(q, &workq, pfrkt_workq) + if (!pfr_ktable_compare(p, q)) + goto _skip; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + if ((p->pfrkt_flags & PFR_TFLAG_PERSIST) && + (clrflag & PFR_TFLAG_PERSIST) && + !(p->pfrkt_flags & PFR_TFLAG_REFERENCED)) + xdel++; + else + xchange++; + } +_skip: + ; + } + if (!(flags & PFR_FLAG_DUMMY)) + pfr_setflags_ktables(&workq); + if (nchange != NULL) + *nchange = xchange; + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_ina_begin(struct pfr_table *trs, u_int32_t *ticket, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p; + struct pf_ruleset *rs; + int xdel = 0; + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + rs = pf_find_or_create_ruleset(trs->pfrt_anchor); + if (rs == NULL) + return (ENOMEM); + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || + pfr_skip_table(trs, p, 0)) + continue; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } + if (!(flags & PFR_FLAG_DUMMY)) { + pfr_setflags_ktables(&workq); + if (ticket != NULL) + *ticket = ++rs->tticket; + rs->topen = 1; + } else + pf_remove_if_empty_ruleset(rs); + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nadd, int *naddr, u_int32_t ticket, int flags) +{ + struct pfr_ktableworkq tableq; + struct pfr_kentryworkq addrq; + struct pfr_ktable *kt, *rt, *shadow, key; + struct pfr_kentry *p; + struct pfr_addr *ad; + struct pf_ruleset *rs; + int i, rv, xadd = 0, xaddr = 0; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO); + if (size && !(flags & PFR_FLAG_ADDRSTOO)) + return (EINVAL); + if (pfr_validate_table(tbl, PFR_TFLAG_USRMASK, + flags & PFR_FLAG_USERIOCTL)) + return (EINVAL); + rs = pf_find_ruleset(tbl->pfrt_anchor); + if (rs == NULL || !rs->topen || ticket != rs->tticket) + return (EBUSY); + tbl->pfrt_flags |= PFR_TFLAG_INACTIVE; + SLIST_INIT(&tableq); + kt = RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl); + if (kt == NULL) { + kt = pfr_create_ktable(tbl, 0, 1); + if (kt == NULL) + return (ENOMEM); + SLIST_INSERT_HEAD(&tableq, kt, pfrkt_workq); + xadd++; + if (!tbl->pfrt_anchor[0]) + goto _skip; + + /* find or create root table */ + bzero(&key, sizeof(key)); + strlcpy(key.pfrkt_name, tbl->pfrt_name, sizeof(key.pfrkt_name)); + rt = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (rt != NULL) { + kt->pfrkt_root = rt; + goto _skip; + } + rt = pfr_create_ktable(&key.pfrkt_t, 0, 1); + if (rt == NULL) { + pfr_destroy_ktables(&tableq, 0); + return (ENOMEM); + } + SLIST_INSERT_HEAD(&tableq, rt, pfrkt_workq); + kt->pfrkt_root = rt; + } else if (!(kt->pfrkt_flags & PFR_TFLAG_INACTIVE)) + xadd++; +_skip: + shadow = pfr_create_ktable(tbl, 0, 0); + if (shadow == NULL) { + pfr_destroy_ktables(&tableq, 0); + return (ENOMEM); + } + SLIST_INIT(&addrq); + for (i = 0, ad = addr; i < size; i++, ad++) { + if (pfr_validate_addr(ad)) + senderr(EINVAL); + if (pfr_lookup_addr(shadow, ad, 1) != NULL) + continue; + p = pfr_create_kentry(ad); + if (p == NULL) + senderr(ENOMEM); + if (pfr_route_kentry(shadow, p)) { + pfr_destroy_kentry(p); + continue; + } + SLIST_INSERT_HEAD(&addrq, p, pfrke_workq); + xaddr++; + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (kt->pfrkt_shadow != NULL) + pfr_destroy_ktable(kt->pfrkt_shadow, 1); + kt->pfrkt_flags |= PFR_TFLAG_INACTIVE; + pfr_insert_ktables(&tableq); + shadow->pfrkt_cnt = (flags & PFR_FLAG_ADDRSTOO) ? + xaddr : NO_ADDRESSES; + kt->pfrkt_shadow = shadow; + } else { + pfr_clean_node_mask(shadow, &addrq); + pfr_destroy_ktable(shadow, 0); + pfr_destroy_ktables(&tableq, 0); + pfr_destroy_kentries(&addrq); + } + if (nadd != NULL) + *nadd = xadd; + if (naddr != NULL) + *naddr = xaddr; + return (0); +_bad: + pfr_destroy_ktable(shadow, 0); + pfr_destroy_ktables(&tableq, 0); + pfr_destroy_kentries(&addrq); + return (rv); +} + +int +pfr_ina_rollback(struct pfr_table *trs, u_int32_t ticket, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p; + struct pf_ruleset *rs; + int xdel = 0; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + rs = pf_find_ruleset(trs->pfrt_anchor); + if (rs == NULL || !rs->topen || ticket != rs->tticket) + return (0); + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || + pfr_skip_table(trs, p, 0)) + continue; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } + if (!(flags & PFR_FLAG_DUMMY)) { + pfr_setflags_ktables(&workq); + rs->topen = 0; + pf_remove_if_empty_ruleset(rs); + } + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd, + int *nchange, int flags) +{ + struct pfr_ktable *p, *q; + struct pfr_ktableworkq workq; + struct pf_ruleset *rs; + int xadd = 0, xchange = 0; + long tzero = time_second; + + PF_RULES_WASSERT(); + + ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY); + rs = pf_find_ruleset(trs->pfrt_anchor); + if (rs == NULL || !rs->topen || ticket != rs->tticket) + return (EBUSY); + + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || + pfr_skip_table(trs, p, 0)) + continue; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + if (p->pfrkt_flags & PFR_TFLAG_ACTIVE) + xchange++; + else + xadd++; + } + + if (!(flags & PFR_FLAG_DUMMY)) { + for (p = SLIST_FIRST(&workq); p != NULL; p = q) { + q = SLIST_NEXT(p, pfrkt_workq); + pfr_commit_ktable(p, tzero); + } + rs->topen = 0; + pf_remove_if_empty_ruleset(rs); + } + if (nadd != NULL) + *nadd = xadd; + if (nchange != NULL) + *nchange = xchange; + + return (0); +} + +static void +pfr_commit_ktable(struct pfr_ktable *kt, long tzero) +{ + struct pfr_ktable *shadow = kt->pfrkt_shadow; + int nflags; + + PF_RULES_WASSERT(); + + if (shadow->pfrkt_cnt == NO_ADDRESSES) { + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + pfr_clstats_ktable(kt, tzero, 1); + } else if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) { + /* kt might contain addresses */ + struct pfr_kentryworkq addrq, addq, changeq, delq, garbageq; + struct pfr_kentry *p, *q, *next; + struct pfr_addr ad; + + pfr_enqueue_addrs(shadow, &addrq, NULL, 0); + pfr_mark_addrs(kt); + SLIST_INIT(&addq); + SLIST_INIT(&changeq); + SLIST_INIT(&delq); + SLIST_INIT(&garbageq); + pfr_clean_node_mask(shadow, &addrq); + for (p = SLIST_FIRST(&addrq); p != NULL; p = next) { + next = SLIST_NEXT(p, pfrke_workq); /* XXX */ + pfr_copyout_addr(&ad, p); + q = pfr_lookup_addr(kt, &ad, 1); + if (q != NULL) { + if (q->pfrke_not != p->pfrke_not) + SLIST_INSERT_HEAD(&changeq, q, + pfrke_workq); + q->pfrke_mark = 1; + SLIST_INSERT_HEAD(&garbageq, p, pfrke_workq); + } else { + p->pfrke_tzero = tzero; + SLIST_INSERT_HEAD(&addq, p, pfrke_workq); + } + } + pfr_enqueue_addrs(kt, &delq, NULL, ENQUEUE_UNMARKED_ONLY); + pfr_insert_kentries(kt, &addq, tzero); + pfr_remove_kentries(kt, &delq); + pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); + pfr_destroy_kentries(&garbageq); + } else { + /* kt cannot contain addresses */ + SWAP(struct radix_node_head *, kt->pfrkt_ip4, + shadow->pfrkt_ip4); + SWAP(struct radix_node_head *, kt->pfrkt_ip6, + shadow->pfrkt_ip6); + SWAP(int, kt->pfrkt_cnt, shadow->pfrkt_cnt); + pfr_clstats_ktable(kt, tzero, 1); + } + nflags = ((shadow->pfrkt_flags & PFR_TFLAG_USRMASK) | + (kt->pfrkt_flags & PFR_TFLAG_SETMASK) | PFR_TFLAG_ACTIVE) + & ~PFR_TFLAG_INACTIVE; + pfr_destroy_ktable(shadow, 0); + kt->pfrkt_shadow = NULL; + pfr_setflags_ktable(kt, nflags); +} + +static int +pfr_validate_table(struct pfr_table *tbl, int allowedflags, int no_reserved) +{ + int i; + + if (!tbl->pfrt_name[0]) + return (-1); + if (no_reserved && !strcmp(tbl->pfrt_anchor, PF_RESERVED_ANCHOR)) + return (-1); + if (tbl->pfrt_name[PF_TABLE_NAME_SIZE-1]) + return (-1); + for (i = strlen(tbl->pfrt_name); i < PF_TABLE_NAME_SIZE; i++) + if (tbl->pfrt_name[i]) + return (-1); + if (pfr_fix_anchor(tbl->pfrt_anchor)) + return (-1); + if (tbl->pfrt_flags & ~allowedflags) + return (-1); + return (0); +} + +/* + * Rewrite anchors referenced by tables to remove slashes + * and check for validity. + */ +static int +pfr_fix_anchor(char *anchor) +{ + size_t siz = MAXPATHLEN; + int i; + + if (anchor[0] == '/') { + char *path; + int off; + + path = anchor; + off = 1; + while (*++path == '/') + off++; + bcopy(path, anchor, siz - off); + memset(anchor + siz - off, 0, off); + } + if (anchor[siz - 1]) + return (-1); + for (i = strlen(anchor); i < siz; i++) + if (anchor[i]) + return (-1); + return (0); +} + +static int +pfr_table_count(struct pfr_table *filter, int flags) +{ + struct pf_ruleset *rs; + + PF_RULES_ASSERT(); + + if (flags & PFR_FLAG_ALLRSETS) + return (pfr_ktable_cnt); + if (filter->pfrt_anchor[0]) { + rs = pf_find_ruleset(filter->pfrt_anchor); + return ((rs != NULL) ? rs->tables : -1); + } + return (pf_main_ruleset.tables); +} + +static int +pfr_skip_table(struct pfr_table *filter, struct pfr_ktable *kt, int flags) +{ + if (flags & PFR_FLAG_ALLRSETS) + return (0); + if (strcmp(filter->pfrt_anchor, kt->pfrkt_anchor)) + return (1); + return (0); +} + +static void +pfr_insert_ktables(struct pfr_ktableworkq *workq) +{ + struct pfr_ktable *p; + + SLIST_FOREACH(p, workq, pfrkt_workq) + pfr_insert_ktable(p); +} + +static void +pfr_insert_ktable(struct pfr_ktable *kt) +{ + + PF_RULES_WASSERT(); + + RB_INSERT(pfr_ktablehead, &pfr_ktables, kt); + pfr_ktable_cnt++; + if (kt->pfrkt_root != NULL) + if (!kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]++) + pfr_setflags_ktable(kt->pfrkt_root, + kt->pfrkt_root->pfrkt_flags|PFR_TFLAG_REFDANCHOR); +} + +static void +pfr_setflags_ktables(struct pfr_ktableworkq *workq) +{ + struct pfr_ktable *p, *q; + + for (p = SLIST_FIRST(workq); p; p = q) { + q = SLIST_NEXT(p, pfrkt_workq); + pfr_setflags_ktable(p, p->pfrkt_nflags); + } +} + +static void +pfr_setflags_ktable(struct pfr_ktable *kt, int newf) +{ + struct pfr_kentryworkq addrq; + + PF_RULES_WASSERT(); + + if (!(newf & PFR_TFLAG_REFERENCED) && + !(newf & PFR_TFLAG_PERSIST)) + newf &= ~PFR_TFLAG_ACTIVE; + if (!(newf & PFR_TFLAG_ACTIVE)) + newf &= ~PFR_TFLAG_USRMASK; + if (!(newf & PFR_TFLAG_SETMASK)) { + RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt); + if (kt->pfrkt_root != NULL) + if (!--kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]) + pfr_setflags_ktable(kt->pfrkt_root, + kt->pfrkt_root->pfrkt_flags & + ~PFR_TFLAG_REFDANCHOR); + pfr_destroy_ktable(kt, 1); + pfr_ktable_cnt--; + return; + } + if (!(newf & PFR_TFLAG_ACTIVE) && kt->pfrkt_cnt) { + pfr_enqueue_addrs(kt, &addrq, NULL, 0); + pfr_remove_kentries(kt, &addrq); + } + if (!(newf & PFR_TFLAG_INACTIVE) && kt->pfrkt_shadow != NULL) { + pfr_destroy_ktable(kt->pfrkt_shadow, 1); + kt->pfrkt_shadow = NULL; + } + kt->pfrkt_flags = newf; +} + +static void +pfr_clstats_ktables(struct pfr_ktableworkq *workq, long tzero, int recurse) +{ + struct pfr_ktable *p; + + SLIST_FOREACH(p, workq, pfrkt_workq) + pfr_clstats_ktable(p, tzero, recurse); +} + +static void +pfr_clstats_ktable(struct pfr_ktable *kt, long tzero, int recurse) +{ + struct pfr_kentryworkq addrq; + + if (recurse) { + pfr_enqueue_addrs(kt, &addrq, NULL, 0); + pfr_clstats_kentries(&addrq, tzero, 0); + } + bzero(kt->pfrkt_packets, sizeof(kt->pfrkt_packets)); + bzero(kt->pfrkt_bytes, sizeof(kt->pfrkt_bytes)); + kt->pfrkt_match = kt->pfrkt_nomatch = 0; + kt->pfrkt_tzero = tzero; +} + +static struct pfr_ktable * +pfr_create_ktable(struct pfr_table *tbl, long tzero, int attachruleset) +{ + struct pfr_ktable *kt; + struct pf_ruleset *rs; + + PF_RULES_WASSERT(); + + kt = malloc(sizeof(*kt), M_PFTABLE, M_NOWAIT|M_ZERO); + if (kt == NULL) + return (NULL); + kt->pfrkt_t = *tbl; + + if (attachruleset) { + rs = pf_find_or_create_ruleset(tbl->pfrt_anchor); + if (!rs) { + pfr_destroy_ktable(kt, 0); + return (NULL); + } + kt->pfrkt_rs = rs; + rs->tables++; + } + + if (!rn_inithead((void **)&kt->pfrkt_ip4, + offsetof(struct sockaddr_in, sin_addr) * 8) || + !rn_inithead((void **)&kt->pfrkt_ip6, + offsetof(struct sockaddr_in6, sin6_addr) * 8)) { + pfr_destroy_ktable(kt, 0); + return (NULL); + } + kt->pfrkt_tzero = tzero; + + return (kt); +} + +static void +pfr_destroy_ktables(struct pfr_ktableworkq *workq, int flushaddr) +{ + struct pfr_ktable *p, *q; + + for (p = SLIST_FIRST(workq); p; p = q) { + q = SLIST_NEXT(p, pfrkt_workq); + pfr_destroy_ktable(p, flushaddr); + } +} + +static void +pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) +{ + struct pfr_kentryworkq addrq; + + if (flushaddr) { + pfr_enqueue_addrs(kt, &addrq, NULL, 0); + pfr_clean_node_mask(kt, &addrq); + pfr_destroy_kentries(&addrq); + } + if (kt->pfrkt_ip4 != NULL) { + RADIX_NODE_HEAD_DESTROY(kt->pfrkt_ip4); + free((caddr_t)kt->pfrkt_ip4, M_RTABLE); + } + if (kt->pfrkt_ip6 != NULL) { + RADIX_NODE_HEAD_DESTROY(kt->pfrkt_ip6); + free((caddr_t)kt->pfrkt_ip6, M_RTABLE); + } + if (kt->pfrkt_shadow != NULL) + pfr_destroy_ktable(kt->pfrkt_shadow, flushaddr); + if (kt->pfrkt_rs != NULL) { + kt->pfrkt_rs->tables--; + pf_remove_if_empty_ruleset(kt->pfrkt_rs); + } + free(kt, M_PFTABLE); +} + +static 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 struct pfr_ktable * +pfr_lookup_table(struct pfr_table *tbl) +{ + /* struct pfr_ktable start like a struct pfr_table */ + return (RB_FIND(pfr_ktablehead, &pfr_ktables, + (struct pfr_ktable *)tbl)); +} + +int +pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) +{ + struct pfr_kentry *ke = NULL; + int match; + + PF_RULES_RASSERT(); + + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (0); + + switch (af) { +#ifdef INET + case AF_INET: + { + struct sockaddr_in sin; + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = a->addr32[0]; + ke = (struct pfr_kentry *)rn_match(&sin, kt->pfrkt_ip4); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + bcopy(a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + ke = (struct pfr_kentry *)rn_match(&sin6, kt->pfrkt_ip6); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + } +#endif /* INET6 */ + } + match = (ke && !ke->pfrke_not); + if (match) + kt->pfrkt_match++; + else + kt->pfrkt_nomatch++; + return (match); +} + +void +pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, + u_int64_t len, int dir_out, int op_pass, int notrule) +{ + struct pfr_kentry *ke = NULL; + + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return; + + switch (af) { +#ifdef INET + case AF_INET: + { + struct sockaddr_in sin; + + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = a->addr32[0]; + ke = (struct pfr_kentry *)rn_match(&sin, kt->pfrkt_ip4); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + bcopy(a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + ke = (struct pfr_kentry *)rn_match(&sin6, kt->pfrkt_ip6); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + } +#endif /* INET6 */ + default: + ; + } + if ((ke == NULL || ke->pfrke_not) != notrule) { + if (op_pass != PFR_OP_PASS) + printf("pfr_update_stats: assertion failed.\n"); + op_pass = PFR_OP_XPASS; + } + kt->pfrkt_packets[dir_out][op_pass]++; + kt->pfrkt_bytes[dir_out][op_pass] += len; + if (ke != NULL && op_pass != PFR_OP_XPASS && + (kt->pfrkt_flags & PFR_TFLAG_COUNTERS)) { + if (ke->pfrke_counters == NULL) + ke->pfrke_counters = uma_zalloc(V_pfr_kcounters_z, + M_NOWAIT | M_ZERO); + if (ke->pfrke_counters != NULL) { + ke->pfrke_counters->pfrkc_packets[dir_out][op_pass]++; + ke->pfrke_counters->pfrkc_bytes[dir_out][op_pass] += len; + } + } +} + +struct pfr_ktable * +pfr_attach_table(struct pf_ruleset *rs, char *name) +{ + struct pfr_ktable *kt, *rt; + struct pfr_table tbl; + struct pf_anchor *ac = rs->anchor; + + PF_RULES_WASSERT(); + + bzero(&tbl, sizeof(tbl)); + strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)); + if (ac != NULL) + strlcpy(tbl.pfrt_anchor, ac->path, sizeof(tbl.pfrt_anchor)); + kt = pfr_lookup_table(&tbl); + if (kt == NULL) { + kt = pfr_create_ktable(&tbl, time_second, 1); + if (kt == NULL) + return (NULL); + if (ac != NULL) { + bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor)); + rt = pfr_lookup_table(&tbl); + if (rt == NULL) { + rt = pfr_create_ktable(&tbl, 0, 1); + if (rt == NULL) { + pfr_destroy_ktable(kt, 0); + return (NULL); + } + pfr_insert_ktable(rt); + } + kt->pfrkt_root = rt; + } + pfr_insert_ktable(kt); + } + if (!kt->pfrkt_refcnt[PFR_REFCNT_RULE]++) + pfr_setflags_ktable(kt, kt->pfrkt_flags|PFR_TFLAG_REFERENCED); + return (kt); +} + +void +pfr_detach_table(struct pfr_ktable *kt) +{ + + PF_RULES_WASSERT(); + KASSERT(kt->pfrkt_refcnt[PFR_REFCNT_RULE] > 0, ("%s: refcount %d\n", + __func__, kt->pfrkt_refcnt[PFR_REFCNT_RULE])); + + if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE]) + pfr_setflags_ktable(kt, kt->pfrkt_flags&~PFR_TFLAG_REFERENCED); +} + +int +pfr_pool_get(struct pfr_ktable *kt, int *pidx, struct pf_addr *counter, + sa_family_t af) +{ + struct pf_addr *addr, *cur, *mask; + union sockaddr_union uaddr, umask; + struct pfr_kentry *ke, *ke2 = NULL; + int idx = -1, use_counter = 0; + + switch (af) { + case AF_INET: + uaddr.sin.sin_len = sizeof(struct sockaddr_in); + uaddr.sin.sin_family = AF_INET; + break; + case AF_INET6: + uaddr.sin6.sin6_len = sizeof(struct sockaddr_in6); + uaddr.sin6.sin6_family = AF_INET6; + break; + } + addr = SUNION2PF(&uaddr, af); + + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (-1); + + if (pidx != NULL) + idx = *pidx; + if (counter != NULL && idx >= 0) + use_counter = 1; + if (idx < 0) + idx = 0; + +_next_block: + ke = pfr_kentry_byidx(kt, idx, af); + if (ke == NULL) { + kt->pfrkt_nomatch++; + return (1); + } + pfr_prepare_network(&umask, af, ke->pfrke_net); + cur = SUNION2PF(&ke->pfrke_sa, af); + mask = SUNION2PF(&umask, af); + + if (use_counter) { + /* is supplied address within block? */ + if (!PF_MATCHA(0, cur, mask, counter, af)) { + /* no, go to next block in table */ + idx++; + use_counter = 0; + goto _next_block; + } + PF_ACPY(addr, counter, af); + } else { + /* use first address of block */ + PF_ACPY(addr, cur, af); + } + + if (!KENTRY_NETWORK(ke)) { + /* this is a single IP address - no possible nested block */ + PF_ACPY(counter, addr, af); + *pidx = idx; + kt->pfrkt_match++; + return (0); + } + for (;;) { + /* we don't want to use a nested block */ + switch (af) { + case AF_INET: + ke2 = (struct pfr_kentry *)rn_match(&uaddr, + kt->pfrkt_ip4); + break; + case AF_INET6: + ke2 = (struct pfr_kentry *)rn_match(&uaddr, + kt->pfrkt_ip6); + break; + } + /* no need to check KENTRY_RNF_ROOT() here */ + if (ke2 == ke) { + /* lookup return the same block - perfect */ + PF_ACPY(counter, addr, af); + *pidx = idx; + kt->pfrkt_match++; + return (0); + } + + /* we need to increase the counter past the nested block */ + pfr_prepare_network(&umask, AF_INET, ke2->pfrke_net); + PF_POOLMASK(addr, addr, SUNION2PF(&umask, af), &pfr_ffaddr, af); + PF_AINC(addr, af); + if (!PF_MATCHA(0, cur, mask, addr, af)) { + /* ok, we reached the end of our main block */ + /* go to next block in table */ + idx++; + use_counter = 0; + goto _next_block; + } + } +} + +static struct pfr_kentry * +pfr_kentry_byidx(struct pfr_ktable *kt, int idx, int af) +{ + struct pfr_walktree w; + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_POOL_GET; + w.pfrw_cnt = idx; + + switch (af) { +#ifdef INET + case AF_INET: + kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + return (w.pfrw_kentry); +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); + return (w.pfrw_kentry); +#endif /* INET6 */ + default: + return (NULL); + } +} + +void +pfr_dynaddr_update(struct pfr_ktable *kt, struct pfi_dynaddr *dyn) +{ + struct pfr_walktree w; + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_DYNADDR_UPDATE; + w.pfrw_dyn = dyn; + + dyn->pfid_acnt4 = 0; + dyn->pfid_acnt6 = 0; + if (!dyn->pfid_af || dyn->pfid_af == AF_INET) + kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + if (!dyn->pfid_af || dyn->pfid_af == AF_INET6) + kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); +} diff --git a/usr.sbin/authpf/Makefile b/usr.sbin/authpf/Makefile index 0a24b8efc087f..1d15beb45c8c2 100644 --- a/usr.sbin/authpf/Makefile +++ b/usr.sbin/authpf/Makefile @@ -1,7 +1,6 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../contrib/pf/authpf -.PATH: ${.CURDIR}/../../contrib/pf/pfctl PROG= authpf MAN= authpf.8 @@ -11,8 +10,6 @@ BINMODE= 6555 SRCS= authpf.c -CFLAGS+= -I${.CURDIR}/../../contrib/pf/pfctl - # XXX ALTQ: CFLAGS+= -DENABLE_ALTQ diff --git a/usr.sbin/ftp-proxy/ftp-proxy/Makefile b/usr.sbin/ftp-proxy/ftp-proxy/Makefile index c21a10749825e..de4988815016a 100644 --- a/usr.sbin/ftp-proxy/ftp-proxy/Makefile +++ b/usr.sbin/ftp-proxy/ftp-proxy/Makefile @@ -8,7 +8,6 @@ MAN= ftp-proxy.8 SRCS= ftp-proxy.c filter.c CFLAGS+= -I${.CURDIR}/../../../contrib/pf/libevent -CFLAGS+= -I${.CURDIR}/../../../sys/contrib/pf LDADD+= ${LIBEVENT} DPADD+= ${LIBEVENT} -- cgit v1.3 From eae8be706e50f75f7a0a3652bf77a7bc78b57996 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Fri, 14 Sep 2012 17:50:42 +0000 Subject: Bump date missed in r202756 PR: docs/171624 Submitted by: bdrewery Approved by: gabor MFC after: 3 days --- lib/libpam/modules/pam_lastlog/pam_lastlog.8 | 2 +- libexec/comsat/comsat.8 | 2 +- libexec/ftpd/ftpd.8 | 2 +- share/man/man3/sysexits.3 | 2 +- share/man/man7/hier.7 | 2 +- usr.bin/finger/finger.1 | 2 +- usr.bin/last/last.1 | 2 +- usr.bin/ncplist/ncplist.1 | 2 +- usr.bin/talk/talk.1 | 2 +- usr.bin/users/users.1 | 2 +- usr.bin/w/w.1 | 2 +- usr.sbin/ac/ac.8 | 2 +- usr.sbin/rwhod/rwhod.8 | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) (limited to 'usr.sbin') diff --git a/lib/libpam/modules/pam_lastlog/pam_lastlog.8 b/lib/libpam/modules/pam_lastlog/pam_lastlog.8 index 8ee5decc36273..cd75fff05df76 100644 --- a/lib/libpam/modules/pam_lastlog/pam_lastlog.8 +++ b/lib/libpam/modules/pam_lastlog/pam_lastlog.8 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 24, 2002 +.Dd January 21, 2010 .Dt PAM_LASTLOG 8 .Os .Sh NAME diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8 index 1f35cff9a0c68..55976025b6ad9 100644 --- a/libexec/comsat/comsat.8 +++ b/libexec/comsat/comsat.8 @@ -32,7 +32,7 @@ .\" @(#)comsat.8 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd July 9, 2002 +.Dd January 21, 2010 .Dt COMSAT 8 .Os .Sh NAME diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 543e1c0a50333..f7dbc88af48ed 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -32,7 +32,7 @@ .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd April 20, 2007 +.Dd January 21, 2010 .Dt FTPD 8 .Os .Sh NAME diff --git a/share/man/man3/sysexits.3 b/share/man/man3/sysexits.3 index 2ef3407b32556..a0f9af98d42eb 100644 --- a/share/man/man3/sysexits.3 +++ b/share/man/man3/sysexits.3 @@ -26,7 +26,7 @@ .\" $FreeBSD$ .\" .\" " -.Dd March 31, 1996 +.Dd January 21, 2010 .Dt SYSEXITS 3 .Os .Sh NAME diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7 index df76f46df55ab..2989eb4e386d9 100644 --- a/share/man/man7/hier.7 +++ b/share/man/man7/hier.7 @@ -32,7 +32,7 @@ .\" @(#)hier.7 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" -.Dd May 25, 2008 +.Dd January 21, 2010 .Dt HIER 7 .Os .Sh NAME diff --git a/usr.bin/finger/finger.1 b/usr.bin/finger/finger.1 index c38aaa828b3db..b430ab49cd925 100644 --- a/usr.bin/finger/finger.1 +++ b/usr.bin/finger/finger.1 @@ -28,7 +28,7 @@ .\" @(#)finger.1 8.3 (Berkeley) 5/5/94 .\" $FreeBSD$ .\" -.Dd April 11, 2007 +.Dd January 21, 2010 .Dt FINGER 1 .Os .Sh NAME diff --git a/usr.bin/last/last.1 b/usr.bin/last/last.1 index 4ac12bfe87c0c..6cb2e6873a6be 100644 --- a/usr.bin/last/last.1 +++ b/usr.bin/last/last.1 @@ -28,7 +28,7 @@ .\" @(#)last.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd July 27, 2003 +.Dd January 21, 2010 .Dt LAST 1 .Os .Sh NAME diff --git a/usr.bin/ncplist/ncplist.1 b/usr.bin/ncplist/ncplist.1 index 335fcc7128762..1c5eb892c0cf4 100644 --- a/usr.bin/ncplist/ncplist.1 +++ b/usr.bin/ncplist/ncplist.1 @@ -1,5 +1,5 @@ .\" $FreeBSD$ -.Dd June 24, 1999 +.Dd January 21, 2010 .Dt NCPLIST 1 .Os .Sh NAME diff --git a/usr.bin/talk/talk.1 b/usr.bin/talk/talk.1 index 327bfed786cfa..abfa150711b5d 100644 --- a/usr.bin/talk/talk.1 +++ b/usr.bin/talk/talk.1 @@ -28,7 +28,7 @@ .\" @(#)talk.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd July 3, 2004 +.Dd January 21, 2010 .Dt TALK 1 .Os .Sh NAME diff --git a/usr.bin/users/users.1 b/usr.bin/users/users.1 index e2d604d275445..53252e504333e 100644 --- a/usr.bin/users/users.1 +++ b/usr.bin/users/users.1 @@ -28,7 +28,7 @@ .\" @(#)users.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd June 6, 1993 +.Dd January 21, 2010 .Dt USERS 1 .Os .Sh NAME diff --git a/usr.bin/w/w.1 b/usr.bin/w/w.1 index 0c43dcd6a5825..345db49c662f4 100644 --- a/usr.bin/w/w.1 +++ b/usr.bin/w/w.1 @@ -28,7 +28,7 @@ .\" @(#)w.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd June 6, 1993 +.Dd January 21, 2010 .Dt W 1 .Os .Sh NAME diff --git a/usr.sbin/ac/ac.8 b/usr.sbin/ac/ac.8 index cc6fb9392bf4a..26667d864fca8 100644 --- a/usr.sbin/ac/ac.8 +++ b/usr.sbin/ac/ac.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 15, 1994 +.Dd January 21, 2010 .Dt AC 8 .Os .Sh NAME diff --git a/usr.sbin/rwhod/rwhod.8 b/usr.sbin/rwhod/rwhod.8 index 59fb4adf65a21..d4ecf4e500d71 100644 --- a/usr.sbin/rwhod/rwhod.8 +++ b/usr.sbin/rwhod/rwhod.8 @@ -28,7 +28,7 @@ .\" @(#)rwhod.8 8.2 (Berkeley) 12/11/93 .\" $FreeBSD$ .\" -.Dd December 11, 1993 +.Dd January 21, 2010 .Dt RWHOD 8 .Os .Sh NAME -- cgit v1.3 From 96240c89f0a9736e05e353de8e5c9a559da8fcf0 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Fri, 14 Sep 2012 21:28:56 +0000 Subject: Correct double "the the" Approved by: cperciva MFC after: 3 days --- lib/libc/stdio/scanf_l.3 | 2 +- share/examples/diskless/README.BOOTP | 2 +- sys/arm/ti/ti_scm.c | 2 +- sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c | 2 +- sys/dev/buslogic/bt.c | 2 +- sys/dev/e1000/e1000_82575.c | 2 +- sys/dev/e1000/e1000_ich8lan.c | 2 +- sys/dev/isci/scil/sati.c | 2 +- sys/dev/isci/scil/sati_callbacks.h | 2 +- sys/dev/isci/scil/sci_base_controller.h | 2 +- sys/dev/isci/scil/sci_base_domain.h | 2 +- sys/dev/isci/scil/sci_base_phy.h | 2 +- sys/dev/isci/scil/sci_base_remote_device.h | 2 +- sys/dev/isci/scil/scic_io_request.h | 4 ++-- sys/dev/isci/scil/scic_sds_phy.c | 2 +- sys/dev/isci/scil/scic_sds_port.h | 2 +- sys/dev/isci/scil/scic_sds_remote_node_context.h | 2 +- sys/dev/isci/scil/scic_sds_request.c | 2 +- sys/dev/isci/scil/scu_bios_definitions.h | 8 ++++---- sys/dev/isp/isp_freebsd.c | 2 +- sys/dev/mps/mps_sas.c | 4 ++-- sys/kern/subr_unit.c | 2 +- sys/sparc64/sparc64/ata_machdep.c | 2 +- sys/vm/vm_pageout.c | 2 +- tools/kerneldoc/subsys/common-Doxyfile | 2 +- usr.bin/lex/NEWS | 2 +- usr.sbin/bsdconfig/networking/include/media.subr | 2 +- usr.sbin/tcpdump/tcpdump/tcpdump.1 | 4 ++-- 28 files changed, 34 insertions(+), 34 deletions(-) (limited to 'usr.sbin') diff --git a/lib/libc/stdio/scanf_l.3 b/lib/libc/stdio/scanf_l.3 index 04ea5d942d335..405601e1fcb84 100644 --- a/lib/libc/stdio/scanf_l.3 +++ b/lib/libc/stdio/scanf_l.3 @@ -57,7 +57,7 @@ The above functions scan input according to a specified in the locale .Fa loc . They behave in the same way as the versions without the _l suffix, but use -the specific locale rather than the the global or per-thread locale. +the specific locale rather than the global or per-thread locale. See the specific manual pages for more information. .Sh SEE ALSO .Xr scanf 3 , diff --git a/share/examples/diskless/README.BOOTP b/share/examples/diskless/README.BOOTP index 5ee53437a6f6a..6383bb7c0536f 100644 --- a/share/examples/diskless/README.BOOTP +++ b/share/examples/diskless/README.BOOTP @@ -85,7 +85,7 @@ options NULLFS # nullfs to map /var/tmp to /tmp BOOTP SERVER SETUP - The BOOTP server must be running on the same logical LAN as the the + The BOOTP server must be running on the same logical LAN as the BOOTP client(s). You need to setup two things: (1) You need to NFS-export /, /usr, and /var. diff --git a/sys/arm/ti/ti_scm.c b/sys/arm/ti/ti_scm.c index 0ae39e99f6dba..ce31f6e4b51bb 100644 --- a/sys/arm/ti/ti_scm.c +++ b/sys/arm/ti/ti_scm.c @@ -202,7 +202,7 @@ ti_scm_padconf_set(const char *padname, const char *muxmode, unsigned int state) * ti_scm_padconf_get - gets the muxmode and state for a pad/pin * @padname: the name of the pad, i.e. "c12" * @muxmode: upon return will contain the name of the muxmode of the pin - * @state: upon return will contain the state of the the pad/pin + * @state: upon return will contain the state of the pad/pin * * * LOCKING: diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c index ee209cad1c56b..679ebe2aa4874 100644 --- a/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c +++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c @@ -82,7 +82,7 @@ static cn_ungrab_t aju_cnungrab; /* * I/O routines lifted from Deimos. This is not only MIPS-specific, but also - * BERI-specific, as we're hard coding the the address at which we expect to + * BERI-specific, as we're hard coding the address at which we expect to * find the Altera JTAG UART and using it unconditionally. We use these * low-level routines so that we can perform console I/O long before newbus * has initialised and devices have attached. The TTY layer of the driver diff --git a/sys/dev/buslogic/bt.c b/sys/dev/buslogic/bt.c index 88d152feea1c1..f2504b8dd4ece 100644 --- a/sys/dev/buslogic/bt.c +++ b/sys/dev/buslogic/bt.c @@ -1945,7 +1945,7 @@ bt_cmd(struct bt_softc *bt, bt_op_t opcode, u_int8_t *params, u_int param_len, bt_outb(bt, COMMAND_REG, opcode); /* - * Wait for up to 1sec for each byte of the the + * Wait for up to 1sec for each byte of the * parameter list sent to be sent. */ timeout = 10000; diff --git a/sys/dev/e1000/e1000_82575.c b/sys/dev/e1000/e1000_82575.c index a04e3fad22fa1..a0d48b36d9ec3 100644 --- a/sys/dev/e1000/e1000_82575.c +++ b/sys/dev/e1000/e1000_82575.c @@ -2280,7 +2280,7 @@ out: * e1000_reset_mdicnfg_82580 - Reset MDICNFG destination and com_mdio bits * @hw: pointer to the HW structure * - * This resets the the MDICNFG.Destination and MDICNFG.Com_MDIO bits based on + * This resets the MDICNFG.Destination and MDICNFG.Com_MDIO bits based on * the values found in the EEPROM. This addresses an issue in which these * bits are not restored from EEPROM after reset. **/ diff --git a/sys/dev/e1000/e1000_ich8lan.c b/sys/dev/e1000/e1000_ich8lan.c index 98aefc4fc0b92..906ce85680a23 100644 --- a/sys/dev/e1000/e1000_ich8lan.c +++ b/sys/dev/e1000/e1000_ich8lan.c @@ -3228,7 +3228,7 @@ out: * @hw: pointer to the HW structure * * ICH8 use the PCI Express bus, but does not contain a PCI Express Capability - * register, so the the bus width is hard coded. + * register, so the bus width is hard coded. **/ static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw) { diff --git a/sys/dev/isci/scil/sati.c b/sys/dev/isci/scil/sati.c index 299aa94d5d1da..57df757ca7169 100644 --- a/sys/dev/isci/scil/sati.c +++ b/sys/dev/isci/scil/sati.c @@ -1236,7 +1236,7 @@ void sati_sequence_terminate( ) { // Decode the sequence type to determine how to handle the termination - // of the the translation method. + // of the translation method. switch (sequence->type) { case SATI_SEQUENCE_UNMAP: diff --git a/sys/dev/isci/scil/sati_callbacks.h b/sys/dev/isci/scil/sati_callbacks.h index 61dad4dfb65a1..e6cd57fa8bd2a 100644 --- a/sys/dev/isci/scil/sati_callbacks.h +++ b/sys/dev/isci/scil/sati_callbacks.h @@ -153,7 +153,7 @@ void sati_cb_get_data_byte( ); /** - * @brief This callback method gets the the task type for the SCSI task + * @brief This callback method gets the task type for the SCSI task * request. * * @param[in] scsi_task This parameter specifies the user's SCSI Task request. diff --git a/sys/dev/isci/scil/sci_base_controller.h b/sys/dev/isci/scil/sci_base_controller.h index 5436840166cb2..cdf38a13ae8d2 100644 --- a/sys/dev/isci/scil/sci_base_controller.h +++ b/sys/dev/isci/scil/sci_base_controller.h @@ -113,7 +113,7 @@ typedef enum _SCI_BASE_CONTROLLER_STATES SCI_BASE_CONTROLLER_STATE_INITIALIZED, /** - * This state indicates the the controller is in the process of becoming + * This state indicates the controller is in the process of becoming * ready (i.e. starting). In this state no new IO operations are permitted. * This state is entered from the INITIALIZED state. */ diff --git a/sys/dev/isci/scil/sci_base_domain.h b/sys/dev/isci/scil/sci_base_domain.h index efe0f2bdaa5d4..537c8ff68cded 100644 --- a/sys/dev/isci/scil/sci_base_domain.h +++ b/sys/dev/isci/scil/sci_base_domain.h @@ -92,7 +92,7 @@ typedef enum _SCI_BASE_DOMAIN_STATES SCI_BASE_DOMAIN_STATE_STARTING, /** - * This state indicates the the domain is now ready. Thus, the user + * This state indicates the domain is now ready. Thus, the user * is able to perform IO operations to remote devices in this domain. * This state is entered from the STOPPED state. * This state is entered from the STOPPING state. diff --git a/sys/dev/isci/scil/sci_base_phy.h b/sys/dev/isci/scil/sci_base_phy.h index 09e57863347c8..141b47a2693a3 100644 --- a/sys/dev/isci/scil/sci_base_phy.h +++ b/sys/dev/isci/scil/sci_base_phy.h @@ -102,7 +102,7 @@ typedef enum _SCI_BASE_PHY_STATES SCI_BASE_PHY_STATE_STARTING, /** - * This state indicates the the phy is now ready. Thus, the user + * This state indicates the phy is now ready. Thus, the user * is able to perform IO operations utilizing this phy as long as it * is currently part of a valid port. * This state is entered from the STARTING state. diff --git a/sys/dev/isci/scil/sci_base_remote_device.h b/sys/dev/isci/scil/sci_base_remote_device.h index 95870f32c0680..9c220f2447022 100644 --- a/sys/dev/isci/scil/sci_base_remote_device.h +++ b/sys/dev/isci/scil/sci_base_remote_device.h @@ -93,7 +93,7 @@ typedef enum _SCI_BASE_REMOTE_DEVICE_STATES SCI_BASE_REMOTE_DEVICE_STATE_STOPPED, /** - * This state indicates the the remote device is in the process of + * This state indicates the remote device is in the process of * becoming ready (i.e. starting). In this state no new IO operations * are permitted. * This state is entered from the STOPPED state. diff --git a/sys/dev/isci/scil/scic_io_request.h b/sys/dev/isci/scil/scic_io_request.h index ee1405e074746..ec1ab32bb8c0b 100644 --- a/sys/dev/isci/scil/scic_io_request.h +++ b/sys/dev/isci/scil/scic_io_request.h @@ -260,12 +260,12 @@ typedef struct SCIC_SMP_PASSTHRU_REQUEST_CALLBACKS */ U8 (* scic_cb_smp_passthru_get_frame_type) ( void *); /** - * Function pointer to get the function in the the smp request + * Function pointer to get the function in the smp request */ U8 (* scic_cb_smp_passthru_get_function) ( void * ); /** - * Function pointer to get the "allocated response length" in the the smp request + * Function pointer to get the "allocated response length" in the smp request */ U8 (* scic_cb_smp_passthru_get_allocated_response_length) ( void * ); diff --git a/sys/dev/isci/scil/scic_sds_phy.c b/sys/dev/isci/scil/scic_sds_phy.c index f53ab3d32935f..bc738215f8ec3 100644 --- a/sys/dev/isci/scil/scic_sds_phy.c +++ b/sys/dev/isci/scil/scic_sds_phy.c @@ -658,7 +658,7 @@ void scic_sds_phy_construct( // Clear out the error counter data memset(this_phy->error_counter, 0, sizeof(this_phy->error_counter)); - // Initialize the the substate machines + // Initialize the substate machines sci_base_state_machine_construct( &this_phy->starting_substate_machine, &this_phy->parent.parent, diff --git a/sys/dev/isci/scil/scic_sds_port.h b/sys/dev/isci/scil/scic_sds_port.h index c9ac9c4a66939..b07997b2636d4 100644 --- a/sys/dev/isci/scil/scic_sds_port.h +++ b/sys/dev/isci/scil/scic_sds_port.h @@ -109,7 +109,7 @@ struct SCIC_SDS_REQUEST; /** * @struct SCIC_SDS_PORT * - * The core port object provides the the abstraction for an SCU port. + * The core port object provides the abstraction for an SCU port. */ typedef struct SCIC_SDS_PORT { diff --git a/sys/dev/isci/scil/scic_sds_remote_node_context.h b/sys/dev/isci/scil/scic_sds_remote_node_context.h index 1e0667101f3b9..4b4481db1ebfa 100644 --- a/sys/dev/isci/scil/scic_sds_remote_node_context.h +++ b/sys/dev/isci/scil/scic_sds_remote_node_context.h @@ -237,7 +237,7 @@ enum SCIC_SDS_REMOTE_NODE_CONTEXT_DESTINATION_STATE * * @brief This structure contains the data associated with the remote * node context object. The remote node context (RNC) object models - * the the remote device information necessary to manage the + * the remote device information necessary to manage the * silicon RNC. */ typedef struct SCIC_SDS_REMOTE_NODE_CONTEXT diff --git a/sys/dev/isci/scil/scic_sds_request.c b/sys/dev/isci/scil/scic_sds_request.c index 020cdf82645c2..f126e5876e394 100644 --- a/sys/dev/isci/scil/scic_sds_request.c +++ b/sys/dev/isci/scil/scic_sds_request.c @@ -2679,7 +2679,7 @@ void scic_sds_request_initial_state_enter( /** * This method implements the actions taken when entering the * SCI_BASE_REQUEST_STATE_CONSTRUCTED state. - * The method sets the state handlers for the the constructed state. + * The method sets the state handlers for the constructed state. * * @param[in] object The io request object that is to enter the constructed * state. diff --git a/sys/dev/isci/scil/scu_bios_definitions.h b/sys/dev/isci/scil/scu_bios_definitions.h index d0d12b5a0d394..67d2ffd5911db 100644 --- a/sys/dev/isci/scil/scu_bios_definitions.h +++ b/sys/dev/isci/scil/scu_bios_definitions.h @@ -299,7 +299,7 @@ typedef struct SCI_BIOS_OEM_PARAM_ELEMENT } sas_address; /** - * These are the per PHY equalization settings associated with the the + * These are the per PHY equalization settings associated with the * AFE XCVR Tx Amplitude and Equalization Control Register Set * (0 thru 3). * @@ -496,7 +496,7 @@ typedef struct SCI_BIOS_OEM_PARAM_ELEMENT_v_1_1 } sas_address; /** - * These are the per PHY equalization settings associated with the the + * These are the per PHY equalization settings associated with the * AFE XCVR Tx Amplitude and Equalization Control Register Set * (0 thru 3). * @@ -706,7 +706,7 @@ typedef struct SCI_BIOS_OEM_PARAM_ELEMENT_v_1_2 } sas_address; /** - * These are the per PHY equalization settings associated with the the + * These are the per PHY equalization settings associated with the * AFE XCVR Tx Amplitude and Equalization Control Register Set * (0 thru 3). * @@ -927,7 +927,7 @@ typedef struct SCI_BIOS_OEM_PARAM_ELEMENT_v_1_3 } sas_address; /** - * These are the per PHY equalization settings associated with the the + * These are the per PHY equalization settings associated with the * AFE XCVR Tx Amplitude and Equalization Control Register Set * (0 thru 3). * diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c index 77d45a01e665f..9a24c4452514f 100644 --- a/sys/dev/isp/isp_freebsd.c +++ b/sys/dev/isp/isp_freebsd.c @@ -1889,7 +1889,7 @@ isp_target_start_ctio(ispsoftc_t *isp, union ccb *ccb, enum Start_Ctio_How how) * Mode 1, status, no data. Only possible when we are sending status, have * no data to transfer, and the sense length can fit in the ct7_entry. * - * Mode 2, status, no data. We have to use this in the case the the response + * Mode 2, status, no data. We have to use this in the case the response * length won't fit into a ct2_entry_t. * * We'll fill out this structure with information as if this were a diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c index dba981e7ed337..dcc4c9e811265 100644 --- a/sys/dev/mps/mps_sas.c +++ b/sys/dev/mps/mps_sas.c @@ -360,7 +360,7 @@ mpssas_remove_volume(struct mps_softc *sc, struct mps_command *tm) mps_printf(sc, "Reset aborted %u commands\n", reply->TerminationCount); mps_free_reply(sc, tm->cm_reply_data); - tm->cm_reply = NULL; /* Ensures the the reply won't get re-freed */ + tm->cm_reply = NULL; /* Ensures the reply won't get re-freed */ mps_printf(sc, "clearing target %u handle 0x%04x\n", targ->tid, handle); @@ -550,7 +550,7 @@ mpssas_remove_device(struct mps_softc *sc, struct mps_command *tm) mps_dprint(sc, MPS_INFO, "Reset aborted %u commands\n", le32toh(reply->TerminationCount)); mps_free_reply(sc, tm->cm_reply_data); - tm->cm_reply = NULL; /* Ensures the the reply won't get re-freed */ + tm->cm_reply = NULL; /* Ensures the reply won't get re-freed */ /* Reuse the existing command */ req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)tm->cm_req; diff --git a/sys/kern/subr_unit.c b/sys/kern/subr_unit.c index 7f90a02ebf2a6..9cf17819e1d1a 100644 --- a/sys/kern/subr_unit.c +++ b/sys/kern/subr_unit.c @@ -63,7 +63,7 @@ * in the usermode test program included, the worst case usage * was 798 bytes on i386 for 5000 allocated and 5000 free units. * * The worst case is where every other unit number is allocated and - * the the rest are free. In that case 44 + N/4 bytes are used where + * the rest are free. In that case 44 + N/4 bytes are used where * N is the number of the highest unit allocated. */ diff --git a/sys/sparc64/sparc64/ata_machdep.c b/sys/sparc64/sparc64/ata_machdep.c index 85ad14cf35684..48fab8ed8024e 100644 --- a/sys/sparc64/sparc64/ata_machdep.c +++ b/sys/sparc64/sparc64/ata_machdep.c @@ -40,7 +40,7 @@ sparc64_ata_disk_firmware_geom_adjust(struct disk *disk) * and sectors so the geometry of large disks has to be adjusted. * If the disk is > 32GB at 16 heads and 63 sectors, adjust to 255 * sectors (this matches what the OpenSolaris dad(7D) driver does). - * If the the disk is even > 128GB, additionally adjust the heads to + * If the disk is even > 128GB, additionally adjust the heads to * 255. This allows disks up to the 2TB limit of the extended VTOC8. * XXX the OpenSolaris dad(7D) driver limits the mediasize to 128GB. */ diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index 6b1031f847ca5..b60661545b834 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -881,7 +881,7 @@ vm_pageout_scan(int pass) uma_reclaim(); /* - * The addl_page_shortage is the the number of temporarily + * The addl_page_shortage is the number of temporarily * stuck pages in the inactive queue. In other words, the * number of pages from cnt.v_inactive_count that should be * discounted in setting the target for the active queue scan. diff --git a/tools/kerneldoc/subsys/common-Doxyfile b/tools/kerneldoc/subsys/common-Doxyfile index be733e20c4f4f..b9acf4facbce2 100644 --- a/tools/kerneldoc/subsys/common-Doxyfile +++ b/tools/kerneldoc/subsys/common-Doxyfile @@ -230,7 +230,7 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = $(DOXYGEN_SRC_INCLUDE_PATH) \ . INCLUDE_FILE_PATTERNS = *.h -# __FreeBSD__ is normally defined to the the major version number of +# __FreeBSD__ is normally defined to the major version number of # FreeBSD. In the kernel source it is just checked if it is defined, and # __FreeBSD_version is used for version dependend code. # To make live simple on major version bumps, just define __FreeBSD__ to 1. diff --git a/usr.bin/lex/NEWS b/usr.bin/lex/NEWS index e6328851d5a86..1f8a8cc336b63 100644 --- a/usr.bin/lex/NEWS +++ b/usr.bin/lex/NEWS @@ -615,7 +615,7 @@ Changes between release 2.4.3 (03Dec93) and release 2.4.2: sources. One side-effect of this change is that -+ and -CF are now incompatible. - - libfl.a now supplies private versions of the the / + - libfl.a now supplies private versions of the / string routines needed by flex and the scanners it generates, to enhance portability to some BSD systems. diff --git a/usr.sbin/bsdconfig/networking/include/media.subr b/usr.sbin/bsdconfig/networking/include/media.subr index 0f0f9fe826974..2d8bd3bc23c8d 100644 --- a/usr.sbin/bsdconfig/networking/include/media.subr +++ b/usr.sbin/bsdconfig/networking/include/media.subr @@ -56,7 +56,7 @@ f_ifconfig_options() set -- $( f_sysrc_get ifconfig_$interface ) # - # Return if the the interface is configured for DHCP + # Return if the interface is configured for DHCP # glob="[Dd][Hh][Cc][Pp]" case "$*" in diff --git a/usr.sbin/tcpdump/tcpdump/tcpdump.1 b/usr.sbin/tcpdump/tcpdump/tcpdump.1 index 5034bb1be5299..18f3f36e5864a 100644 --- a/usr.sbin/tcpdump/tcpdump/tcpdump.1 +++ b/usr.sbin/tcpdump/tcpdump/tcpdump.1 @@ -1436,11 +1436,11 @@ not be useful to people who are not familiar with the workings of AFS and RX. .LP If the -v (verbose) flag is given twice, acknowledgement packets and -additional header information is printed, such as the the RX call ID, +additional header information is printed, such as the RX call ID, call number, sequence number, serial number, and the RX packet flags. .LP If the -v flag is given twice, additional information is printed, -such as the the RX call ID, serial number, and the RX packet flags. +such as the RX call ID, serial number, and the RX packet flags. The MTU negotiation information is also printed from RX ack packets. .LP If the -v flag is given three times, the security index and service id -- cgit v1.3 From 6b63b7b72653597a7ed810f761b7d68c3ac42cf4 Mon Sep 17 00:00:00 2001 From: Isabell Long Date: Sat, 15 Sep 2012 16:24:03 +0000 Subject: Remove a reference to CVS and to freefall from a user-facing man page. PR: docs/171658 Reported by: Chris Petrik (c dot petrik dot sosa at gmail dot com) Approved by: gabor (mentor) MFC after: 5 days --- usr.sbin/adduser/adduser.8 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8 index 03f7e34fe7207..f23ecffd5b595 100644 --- a/usr.sbin/adduser/adduser.8 +++ b/usr.sbin/adduser/adduser.8 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 16, 2008 +.Dd September 15, 2012 .Dt ADDUSER 8 .Os .Sh NAME @@ -129,9 +129,8 @@ they can safely run with a umask of 002 instead of the usual 022 and create files in their home directory without worrying about others being able to change them. .Pp -For a shared area you create a separate UID/GID (like cvs or ncvs on freefall), -you place each person that should be able to access this area into that new -group. +For a shared area you create a separate UID/GID, you place each person +that should be able to access this area into that new group. .Pp This model of UID/GID administration allows far greater flexibility than lumping users into groups and having to muck with the umask when working in a shared -- cgit v1.3 From 6cbae38f63b6d9af0ab74c4f2c5a87604eb4c7f4 Mon Sep 17 00:00:00 2001 From: Dag-Erling Smørgrav Date: Sun, 16 Sep 2012 15:22:15 +0000 Subject: Warn about filesystem-based attacks. --- usr.sbin/jail/jail.8 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index 9204686d23356..b96cfef24cc4a 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 23, 2012 +.Dd September 15, 2012 .Dt JAIL 8 .Os .Sh NAME @@ -1225,3 +1225,11 @@ directory that is moved out of the jail's chroot, then the process may gain access to the file space outside of the jail. It is recommended that directories always be copied, rather than moved, out of a jail. +.Pp +In addition, there are several ways in which an unprivileged user +outside the jail can cooperate with a privileged user inside the jail +and thereby obtain elevated privileges in the host environment. +Most of these attacks can be mitigated by ensuring that the jail root +is not accessible to unprivileged users in the host environment. +Regardless, as a general rule, untrusted users with privileged access +to a jail should not be given access to the host environment. -- cgit v1.3 From 16c3b091aea74955bfb01b2ceaba57bd3d1c660b Mon Sep 17 00:00:00 2001 From: Mikolaj Golub Date: Mon, 17 Sep 2012 07:32:53 +0000 Subject: In snmp_hostres, device_map table is used for consistent device table indexing. When a device has gone it is not removed from device_map table but just its entry_p field is set to NULL. So when traversing device_map in disk_OS_get_ATA_disks() and disk_OS_get_MD_disks() check for entry_p being NULL, otherwise the bsnmpd crash is possible when a removed map entry is dereferenced. Before the fix, for disk_OS_get_ATA_disks() the crash could be easily reproduced running: atacontrol detach ata1 The crash was not observed in disk_OS_get_MD_disks() because currently snmp_hostres does no see md(4) disks: to get the device list it uses devinfo(3), which does not return md devices. Reported by: Miroslav Lachman 000.fbsd quip.cz MFC after: 1 week --- usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c index 25ba56a848719..567535092846c 100644 --- a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c @@ -287,6 +287,9 @@ disk_OS_get_ATA_disks(void) /* Walk over the device table looking for ata disks */ STAILQ_FOREACH(map, &device_map, link) { + /* Skip deleted entries. */ + if (map->entry_p == NULL) + continue; for (found = lookup; found->media != DSM_UNKNOWN; found++) { if (strncmp(map->name_key, found->dev_name, strlen(found->dev_name)) != 0) @@ -345,6 +348,9 @@ disk_OS_get_MD_disks(void) /* Look for md devices */ STAILQ_FOREACH(map, &device_map, link) { + /* Skip deleted entries. */ + if (map->entry_p == NULL) + continue; if (sscanf(map->name_key, "md%d", &unit) != 1) continue; -- cgit v1.3 From 389c8bd51ead1f1c111e8f8baad65762f6823ff4 Mon Sep 17 00:00:00 2001 From: Gavin Atkinson Date: Tue, 18 Sep 2012 22:04:59 +0000 Subject: Align the PCI Express #defines with the style used for the PCI-X #defines. This also has the advantage that it makes the names more compact, iand also allows us to correct the non-uniform naming of the PCIM_LINK_* defines, making them all consistent amongst themselves. This is a mostly mechanical rename: s/PCIR_EXPRESS_/PCIER_/g s/PCIM_EXP_/PCIEM_/g s/PCIM_LINK_/PCIEM_LINK_/g When this is MFC'd, #defines will be added for the old names to assist out-of-tree drivers. Discussed with: jhb MFC after: 1 week --- sys/dev/alc/if_alc.c | 16 +-- sys/dev/bge/if_bge.c | 16 +-- sys/dev/cxgb/cxgb_main.c | 4 +- sys/dev/cxgb/cxgb_osdep.h | 10 +- sys/dev/cxgbe/osdep.h | 14 +-- sys/dev/cxgbe/t4_main.c | 6 +- sys/dev/e1000/if_em.c | 6 +- sys/dev/et/if_et.c | 6 +- sys/dev/jme/if_jme.c | 2 +- sys/dev/pci/pci.c | 80 ++++++------ sys/dev/pci/pcireg.h | 282 +++++++++++++++++++++--------------------- sys/dev/re/if_re.c | 8 +- sys/ofed/include/linux/pci.h | 4 +- sys/powerpc/mpc85xx/pci_fdt.c | 12 +- usr.sbin/pciconf/cap.c | 50 ++++---- usr.sbin/pciconf/err.c | 18 +-- 16 files changed, 267 insertions(+), 267 deletions(-) (limited to 'usr.sbin') diff --git a/sys/dev/alc/if_alc.c b/sys/dev/alc/if_alc.c index c73075c6a8fcb..2a0ddb2f509b6 100644 --- a/sys/dev/alc/if_alc.c +++ b/sys/dev/alc/if_alc.c @@ -683,7 +683,7 @@ alc_aspm(struct alc_softc *sc, int media) if ((sc->alc_flags & (ALC_FLAG_APS | ALC_FLAG_PCIE)) == (ALC_FLAG_APS | ALC_FLAG_PCIE)) linkcfg = CSR_READ_2(sc, sc->alc_expcap + - PCIR_EXPRESS_LINK_CTL); + PCIER_LINK_CTL); else linkcfg = 0; pmcfg &= ~PM_CFG_SERDES_PD_EX_L1; @@ -698,7 +698,7 @@ alc_aspm(struct alc_softc *sc, int media) if (sc->alc_ident->deviceid == DEVICEID_ATHEROS_AR8152_B && sc->alc_rev == ATHEROS_AR8152_B_V10) linkcfg |= 0x80; - CSR_WRITE_2(sc, sc->alc_expcap + PCIR_EXPRESS_LINK_CTL, + CSR_WRITE_2(sc, sc->alc_expcap + PCIER_LINK_CTL, linkcfg); pmcfg &= ~(PM_CFG_EN_BUFS_RX_L0S | PM_CFG_SA_DLY_ENB | PM_CFG_HOTRST); @@ -798,10 +798,10 @@ alc_attach(device_t dev) if (pci_find_cap(dev, PCIY_EXPRESS, &base) == 0) { sc->alc_flags |= ALC_FLAG_PCIE; sc->alc_expcap = base; - burst = CSR_READ_2(sc, base + PCIR_EXPRESS_DEVICE_CTL); + burst = CSR_READ_2(sc, base + PCIER_DEVICE_CTL); sc->alc_dma_rd_burst = - (burst & PCIM_EXP_CTL_MAX_READ_REQUEST) >> 12; - sc->alc_dma_wr_burst = (burst & PCIM_EXP_CTL_MAX_PAYLOAD) >> 5; + (burst & PCIEM_CTL_MAX_READ_REQUEST) >> 12; + sc->alc_dma_wr_burst = (burst & PCIEM_CTL_MAX_PAYLOAD) >> 5; if (bootverbose) { device_printf(dev, "Read request size : %u bytes.\n", alc_dma_burst[sc->alc_dma_rd_burst]); @@ -831,9 +831,9 @@ alc_attach(device_t dev) CSR_WRITE_4(sc, ALC_PCIE_PHYMISC2, val); } /* Disable ASPM L0S and L1. */ - cap = CSR_READ_2(sc, base + PCIR_EXPRESS_LINK_CAP); - if ((cap & PCIM_LINK_CAP_ASPM) != 0) { - ctl = CSR_READ_2(sc, base + PCIR_EXPRESS_LINK_CTL); + cap = CSR_READ_2(sc, base + PCIER_LINK_CAP); + if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { + ctl = CSR_READ_2(sc, base + PCIER_LINK_CTL); if ((ctl & 0x08) != 0) sc->alc_rcb = DMA_CFG_RCB_128; if (bootverbose) diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c index 16cfe27a22023..4ddcd8c7e2dae 100644 --- a/sys/dev/bge/if_bge.c +++ b/sys/dev/bge/if_bge.c @@ -3625,17 +3625,17 @@ bge_reset(struct bge_softc *sc) pci_write_config(dev, 0xC4, val | (1 << 15), 4); } devctl = pci_read_config(dev, - sc->bge_expcap + PCIR_EXPRESS_DEVICE_CTL, 2); + sc->bge_expcap + PCIER_DEVICE_CTL, 2); /* Clear enable no snoop and disable relaxed ordering. */ - devctl &= ~(PCIM_EXP_CTL_RELAXED_ORD_ENABLE | - PCIM_EXP_CTL_NOSNOOP_ENABLE); - pci_write_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_CTL, + devctl &= ~(PCIEM_CTL_RELAXED_ORD_ENABLE | + PCIEM_CTL_NOSNOOP_ENABLE); + pci_write_config(dev, sc->bge_expcap + PCIER_DEVICE_CTL, devctl, 2); /* Clear error status. */ - pci_write_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_STA, - PCIM_EXP_STA_CORRECTABLE_ERROR | - PCIM_EXP_STA_NON_FATAL_ERROR | PCIM_EXP_STA_FATAL_ERROR | - PCIM_EXP_STA_UNSUPPORTED_REQ, 2); + pci_write_config(dev, sc->bge_expcap + PCIER_DEVICE_STA, + PCIEM_STA_CORRECTABLE_ERROR | + PCIEM_STA_NON_FATAL_ERROR | PCIEM_STA_FATAL_ERROR | + PCIEM_STA_UNSUPPORTED_REQ, 2); } /* Reset some of the PCI state that got zapped by reset. */ diff --git a/sys/dev/cxgb/cxgb_main.c b/sys/dev/cxgb/cxgb_main.c index 62ad05152dbfd..b0987167f8c65 100644 --- a/sys/dev/cxgb/cxgb_main.c +++ b/sys/dev/cxgb/cxgb_main.c @@ -476,8 +476,8 @@ cxgb_controller_attach(device_t dev) if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { uint16_t lnk; - lnk = pci_read_config(dev, reg + PCIR_EXPRESS_LINK_STA, 2); - sc->link_width = (lnk & PCIM_LINK_STA_WIDTH) >> 4; + lnk = pci_read_config(dev, reg + PCIER_LINK_STA, 2); + sc->link_width = (lnk & PCIEM_LINK_STA_WIDTH) >> 4; if (sc->link_width < 8 && (ai->caps & SUPPORTED_10000baseT_Full)) { device_printf(sc->dev, diff --git a/sys/dev/cxgb/cxgb_osdep.h b/sys/dev/cxgb/cxgb_osdep.h index 37171d94f5ac6..71572d5ad8838 100644 --- a/sys/dev/cxgb/cxgb_osdep.h +++ b/sys/dev/cxgb/cxgb_osdep.h @@ -215,11 +215,11 @@ static const int debug_flags = DBG_RX; #define PCI_VPD_DATA PCIR_VPD_DATA #define PCI_CAP_ID_EXP PCIY_EXPRESS -#define PCI_EXP_DEVCTL PCIR_EXPRESS_DEVICE_CTL -#define PCI_EXP_DEVCTL_PAYLOAD PCIM_EXP_CTL_MAX_PAYLOAD -#define PCI_EXP_DEVCTL_READRQ PCIM_EXP_CTL_MAX_READ_REQUEST -#define PCI_EXP_LNKCTL PCIR_EXPRESS_LINK_CTL -#define PCI_EXP_LNKSTA PCIR_EXPRESS_LINK_STA +#define PCI_EXP_DEVCTL PCIER_DEVICE_CTL +#define PCI_EXP_DEVCTL_PAYLOAD PCIEM_CTL_MAX_PAYLOAD +#define PCI_EXP_DEVCTL_READRQ PCIEM_CTL_MAX_READ_REQUEST +#define PCI_EXP_LNKCTL PCIER_LINK_CTL +#define PCI_EXP_LNKSTA PCIER_LINK_STA /* * Linux compatibility macros diff --git a/sys/dev/cxgbe/osdep.h b/sys/dev/cxgbe/osdep.h index f2a75cf3c29eb..a8c68c21fcc7a 100644 --- a/sys/dev/cxgbe/osdep.h +++ b/sys/dev/cxgbe/osdep.h @@ -118,13 +118,13 @@ typedef boolean_t bool; #define PCI_VPD_DATA PCIR_VPD_DATA #define PCI_CAP_ID_EXP PCIY_EXPRESS -#define PCI_EXP_DEVCTL PCIR_EXPRESS_DEVICE_CTL -#define PCI_EXP_DEVCTL_PAYLOAD PCIM_EXP_CTL_MAX_PAYLOAD -#define PCI_EXP_DEVCTL_READRQ PCIM_EXP_CTL_MAX_READ_REQUEST -#define PCI_EXP_LNKCTL PCIR_EXPRESS_LINK_CTL -#define PCI_EXP_LNKSTA PCIR_EXPRESS_LINK_STA -#define PCI_EXP_LNKSTA_CLS PCIM_LINK_STA_SPEED -#define PCI_EXP_LNKSTA_NLW PCIM_LINK_STA_WIDTH +#define PCI_EXP_DEVCTL PCIER_DEVICE_CTL +#define PCI_EXP_DEVCTL_PAYLOAD PCIEM_CTL_MAX_PAYLOAD +#define PCI_EXP_DEVCTL_READRQ PCIEM_CTL_MAX_READ_REQUEST +#define PCI_EXP_LNKCTL PCIER_LINK_CTL +#define PCI_EXP_LNKSTA PCIER_LINK_STA +#define PCI_EXP_LNKSTA_CLS PCIEM_LINK_STA_SPEED +#define PCI_EXP_LNKSTA_NLW PCIEM_LINK_STA_WIDTH #define PCI_EXP_DEVCTL2 0x28 static inline int diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 5b46e142519cb..50a8725ff03ed 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -429,9 +429,9 @@ t4_attach(device_t dev) uint32_t v; pci_set_max_read_req(dev, 4096); - v = pci_read_config(dev, i + PCIR_EXPRESS_DEVICE_CTL, 2); - v |= PCIM_EXP_CTL_RELAXED_ORD_ENABLE; - pci_write_config(dev, i + PCIR_EXPRESS_DEVICE_CTL, v, 2); + v = pci_read_config(dev, i + PCIER_DEVICE_CTL, 2); + v |= PCIEM_CTL_RELAXED_ORD_ENABLE; + pci_write_config(dev, i + PCIER_DEVICE_CTL, v, 2); } snprintf(sc->lockname, sizeof(sc->lockname), "%s", diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index c57293b264358..a4f43e7840e02 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -5113,11 +5113,11 @@ em_disable_aspm(struct adapter *adapter) } if (pci_find_cap(dev, PCIY_EXPRESS, &base) != 0) return; - reg = base + PCIR_EXPRESS_LINK_CAP; + reg = base + PCIER_LINK_CAP; link_cap = pci_read_config(dev, reg, 2); - if ((link_cap & PCIM_LINK_CAP_ASPM) == 0) + if ((link_cap & PCIEM_LINK_CAP_ASPM) == 0) return; - reg = base + PCIR_EXPRESS_LINK_CTL; + reg = base + PCIER_LINK_CTL; link_ctrl = pci_read_config(dev, reg, 2); link_ctrl &= 0xFFFC; /* turn off bit 1 and 2 */ pci_write_config(dev, reg, link_ctrl, 2); diff --git a/sys/dev/et/if_et.c b/sys/dev/et/if_et.c index bde680e5981d3..dfdcde73438bd 100644 --- a/sys/dev/et/if_et.c +++ b/sys/dev/et/if_et.c @@ -700,8 +700,8 @@ et_bus_config(struct et_softc *sc) * max playload size */ val = pci_read_config(sc->dev, - sc->sc_expcap + PCIR_EXPRESS_DEVICE_CAP, 4); - max_plsz = val & PCIM_EXP_CAP_MAX_PAYLOAD; + sc->sc_expcap + PCIER_DEVICE_CAP, 4); + max_plsz = val & PCIEM_CAP_MAX_PAYLOAD; switch (max_plsz) { case ET_PCIV_DEVICE_CAPS_PLSZ_128: @@ -732,7 +732,7 @@ et_bus_config(struct et_softc *sc) * Set L0s and L1 latency timer to 2us */ val = pci_read_config(sc->dev, ET_PCIR_L0S_L1_LATENCY, 4); - val &= ~(PCIM_LINK_CAP_L0S_EXIT | PCIM_LINK_CAP_L1_EXIT); + val &= ~(PCIEM_LINK_CAP_L0S_EXIT | PCIEM_LINK_CAP_L1_EXIT); /* L0s exit latency : 2us */ val |= 0x00005000; /* L1 exit latency : 2us */ diff --git a/sys/dev/jme/if_jme.c b/sys/dev/jme/if_jme.c index 7f0f9174f8af3..13270b78d40cd 100644 --- a/sys/dev/jme/if_jme.c +++ b/sys/dev/jme/if_jme.c @@ -778,7 +778,7 @@ jme_attach(device_t dev) /* Set max allowable DMA size. */ if (pci_find_cap(dev, PCIY_EXPRESS, &i) == 0) { sc->jme_flags |= JME_FLAG_PCIE; - burst = pci_read_config(dev, i + PCIR_EXPRESS_DEVICE_CTL, 2); + burst = pci_read_config(dev, i + PCIER_DEVICE_CTL, 2); if (bootverbose) { device_printf(dev, "Read request size : %d bytes.\n", 128 << ((burst >> 12) & 0x07)); diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 650342101cda0..3de932ab8740e 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -743,8 +743,8 @@ pci_read_cap(device_t pcib, pcicfgregs *cfg) */ pcie_chipset = 1; cfg->pcie.pcie_location = ptr; - val = REG(ptr + PCIR_EXPRESS_FLAGS, 2); - cfg->pcie.pcie_type = val & PCIM_EXP_FLAGS_TYPE; + val = REG(ptr + PCIER_FLAGS, 2); + cfg->pcie.pcie_type = val & PCIEM_FLAGS_TYPE; break; default: break; @@ -1791,8 +1791,8 @@ pci_get_max_read_req(device_t dev) cap = dinfo->cfg.pcie.pcie_location; if (cap == 0) return (0); - val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); - val &= PCIM_EXP_CTL_MAX_READ_REQUEST; + val = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2); + val &= PCIEM_CTL_MAX_READ_REQUEST; val >>= 12; return (1 << (val + 7)); } @@ -1812,10 +1812,10 @@ pci_set_max_read_req(device_t dev, int size) if (size > 4096) size = 4096; size = (1 << (fls(size) - 1)); - val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); - val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; + val = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2); + val &= ~PCIEM_CTL_MAX_READ_REQUEST; val |= (fls(size) - 8) << 12; - pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2); + pci_write_config(dev, cap + PCIER_DEVICE_CTL, val, 2); return (size); } @@ -4469,28 +4469,28 @@ pci_cfg_restore_pcie(device_t dev, struct pci_devinfo *dinfo) cfg = &dinfo->cfg.pcie; pos = cfg->pcie_location; - version = cfg->pcie_flags & PCIM_EXP_FLAGS_VERSION; + version = cfg->pcie_flags & PCIEM_FLAGS_VERSION; - WREG(PCIR_EXPRESS_DEVICE_CTL, cfg->pcie_device_ctl); + WREG(PCIER_DEVICE_CTL, cfg->pcie_device_ctl); - if (version > 1 || cfg->pcie_type == PCIM_EXP_TYPE_ROOT_PORT || - cfg->pcie_type == PCIM_EXP_TYPE_ENDPOINT || - cfg->pcie_type == PCIM_EXP_TYPE_LEGACY_ENDPOINT) - WREG(PCIR_EXPRESS_LINK_CTL, cfg->pcie_link_ctl); + if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT || + cfg->pcie_type == PCIEM_TYPE_ENDPOINT || + cfg->pcie_type == PCIEM_TYPE_LEGACY_ENDPOINT) + WREG(PCIER_LINK_CTL, cfg->pcie_link_ctl); - if (version > 1 || (cfg->pcie_type == PCIM_EXP_TYPE_ROOT_PORT || - (cfg->pcie_type == PCIM_EXP_TYPE_DOWNSTREAM_PORT && - (cfg->pcie_flags & PCIM_EXP_FLAGS_SLOT)))) - WREG(PCIR_EXPRESS_SLOT_CTL, cfg->pcie_slot_ctl); + if (version > 1 || (cfg->pcie_type == PCIEM_TYPE_ROOT_PORT || + (cfg->pcie_type == PCIEM_TYPE_DOWNSTREAM_PORT && + (cfg->pcie_flags & PCIEM_FLAGS_SLOT)))) + WREG(PCIER_SLOT_CTL, cfg->pcie_slot_ctl); - if (version > 1 || cfg->pcie_type == PCIM_EXP_TYPE_ROOT_PORT || - cfg->pcie_type == PCIM_EXP_TYPE_ROOT_EC) - WREG(PCIR_EXPRESS_ROOT_CTL, cfg->pcie_root_ctl); + if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT || + cfg->pcie_type == PCIEM_TYPE_ROOT_EC) + WREG(PCIER_ROOT_CTL, cfg->pcie_root_ctl); if (version > 1) { - WREG(PCIR_EXPRESS_DEVICE_CTL2, cfg->pcie_device_ctl2); - WREG(PCIR_EXPRESS_LINK_CTL2, cfg->pcie_link_ctl2); - WREG(PCIR_EXPRESS_SLOT_CTL2, cfg->pcie_slot_ctl2); + WREG(PCIER_DEVICE_CTL2, cfg->pcie_device_ctl2); + WREG(PCIER_LINK_CTL2, cfg->pcie_link_ctl2); + WREG(PCIER_SLOT_CTL2, cfg->pcie_slot_ctl2); } #undef WREG } @@ -4562,30 +4562,30 @@ pci_cfg_save_pcie(device_t dev, struct pci_devinfo *dinfo) cfg = &dinfo->cfg.pcie; pos = cfg->pcie_location; - cfg->pcie_flags = RREG(PCIR_EXPRESS_FLAGS); + cfg->pcie_flags = RREG(PCIER_FLAGS); - version = cfg->pcie_flags & PCIM_EXP_FLAGS_VERSION; + version = cfg->pcie_flags & PCIEM_FLAGS_VERSION; - cfg->pcie_device_ctl = RREG(PCIR_EXPRESS_DEVICE_CTL); + cfg->pcie_device_ctl = RREG(PCIER_DEVICE_CTL); - if (version > 1 || cfg->pcie_type == PCIM_EXP_TYPE_ROOT_PORT || - cfg->pcie_type == PCIM_EXP_TYPE_ENDPOINT || - cfg->pcie_type == PCIM_EXP_TYPE_LEGACY_ENDPOINT) - cfg->pcie_link_ctl = RREG(PCIR_EXPRESS_LINK_CTL); + if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT || + cfg->pcie_type == PCIEM_TYPE_ENDPOINT || + cfg->pcie_type == PCIEM_TYPE_LEGACY_ENDPOINT) + cfg->pcie_link_ctl = RREG(PCIER_LINK_CTL); - if (version > 1 || (cfg->pcie_type == PCIM_EXP_TYPE_ROOT_PORT || - (cfg->pcie_type == PCIM_EXP_TYPE_DOWNSTREAM_PORT && - (cfg->pcie_flags & PCIM_EXP_FLAGS_SLOT)))) - cfg->pcie_slot_ctl = RREG(PCIR_EXPRESS_SLOT_CTL); + if (version > 1 || (cfg->pcie_type == PCIEM_TYPE_ROOT_PORT || + (cfg->pcie_type == PCIEM_TYPE_DOWNSTREAM_PORT && + (cfg->pcie_flags & PCIEM_FLAGS_SLOT)))) + cfg->pcie_slot_ctl = RREG(PCIER_SLOT_CTL); - if (version > 1 || cfg->pcie_type == PCIM_EXP_TYPE_ROOT_PORT || - cfg->pcie_type == PCIM_EXP_TYPE_ROOT_EC) - cfg->pcie_root_ctl = RREG(PCIR_EXPRESS_ROOT_CTL); + if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT || + cfg->pcie_type == PCIEM_TYPE_ROOT_EC) + cfg->pcie_root_ctl = RREG(PCIER_ROOT_CTL); if (version > 1) { - cfg->pcie_device_ctl2 = RREG(PCIR_EXPRESS_DEVICE_CTL2); - cfg->pcie_link_ctl2 = RREG(PCIR_EXPRESS_LINK_CTL2); - cfg->pcie_slot_ctl2 = RREG(PCIR_EXPRESS_SLOT_CTL2); + cfg->pcie_device_ctl2 = RREG(PCIER_DEVICE_CTL2); + cfg->pcie_link_ctl2 = RREG(PCIER_LINK_CTL2); + cfg->pcie_slot_ctl2 = RREG(PCIER_SLOT_CTL2); } #undef RREG } diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h index 1913ca2e1c226..735d5f654d896 100644 --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -629,147 +629,147 @@ #define PCIR_SUBVENDCAP_ID 0x4 /* PCI Express definitions */ -#define PCIR_EXPRESS_FLAGS 0x2 -#define PCIM_EXP_FLAGS_VERSION 0x000F -#define PCIM_EXP_FLAGS_TYPE 0x00F0 -#define PCIM_EXP_TYPE_ENDPOINT 0x0000 -#define PCIM_EXP_TYPE_LEGACY_ENDPOINT 0x0010 -#define PCIM_EXP_TYPE_ROOT_PORT 0x0040 -#define PCIM_EXP_TYPE_UPSTREAM_PORT 0x0050 -#define PCIM_EXP_TYPE_DOWNSTREAM_PORT 0x0060 -#define PCIM_EXP_TYPE_PCI_BRIDGE 0x0070 -#define PCIM_EXP_TYPE_PCIE_BRIDGE 0x0080 -#define PCIM_EXP_TYPE_ROOT_INT_EP 0x0090 -#define PCIM_EXP_TYPE_ROOT_EC 0x00a0 -#define PCIM_EXP_FLAGS_SLOT 0x0100 -#define PCIM_EXP_FLAGS_IRQ 0x3e00 -#define PCIR_EXPRESS_DEVICE_CAP 0x4 -#define PCIM_EXP_CAP_MAX_PAYLOAD 0x00000007 -#define PCIM_EXP_CAP_PHANTHOM_FUNCS 0x00000018 -#define PCIM_EXP_CAP_EXT_TAG_FIELD 0x00000020 -#define PCIM_EXP_CAP_L0S_LATENCY 0x000001c0 -#define PCIM_EXP_CAP_L1_LATENCY 0x00000e00 -#define PCIM_EXP_CAP_ROLE_ERR_RPT 0x00008000 -#define PCIM_EXP_CAP_SLOT_PWR_LIM_VAL 0x03fc0000 -#define PCIM_EXP_CAP_SLOT_PWR_LIM_SCALE 0x0c000000 -#define PCIM_EXP_CAP_FLR 0x10000000 -#define PCIR_EXPRESS_DEVICE_CTL 0x8 -#define PCIM_EXP_CTL_COR_ENABLE 0x0001 -#define PCIM_EXP_CTL_NFER_ENABLE 0x0002 -#define PCIM_EXP_CTL_FER_ENABLE 0x0004 -#define PCIM_EXP_CTL_URR_ENABLE 0x0008 -#define PCIM_EXP_CTL_RELAXED_ORD_ENABLE 0x0010 -#define PCIM_EXP_CTL_MAX_PAYLOAD 0x00e0 -#define PCIM_EXP_CTL_EXT_TAG_FIELD 0x0100 -#define PCIM_EXP_CTL_PHANTHOM_FUNCS 0x0200 -#define PCIM_EXP_CTL_AUX_POWER_PM 0x0400 -#define PCIM_EXP_CTL_NOSNOOP_ENABLE 0x0800 -#define PCIM_EXP_CTL_MAX_READ_REQUEST 0x7000 -#define PCIM_EXP_CTL_BRDG_CFG_RETRY 0x8000 /* PCI-E - PCI/PCI-X bridges */ -#define PCIM_EXP_CTL_INITIATE_FLR 0x8000 /* FLR capable endpoints */ -#define PCIR_EXPRESS_DEVICE_STA 0xa -#define PCIM_EXP_STA_CORRECTABLE_ERROR 0x0001 -#define PCIM_EXP_STA_NON_FATAL_ERROR 0x0002 -#define PCIM_EXP_STA_FATAL_ERROR 0x0004 -#define PCIM_EXP_STA_UNSUPPORTED_REQ 0x0008 -#define PCIM_EXP_STA_AUX_POWER 0x0010 -#define PCIM_EXP_STA_TRANSACTION_PND 0x0020 -#define PCIR_EXPRESS_LINK_CAP 0xc -#define PCIM_LINK_CAP_MAX_SPEED 0x0000000f -#define PCIM_LINK_CAP_MAX_WIDTH 0x000003f0 -#define PCIM_LINK_CAP_ASPM 0x00000c00 -#define PCIM_LINK_CAP_L0S_EXIT 0x00007000 -#define PCIM_LINK_CAP_L1_EXIT 0x00038000 -#define PCIM_LINK_CAP_CLOCK_PM 0x00040000 -#define PCIM_LINK_CAP_SURPRISE_DOWN 0x00080000 -#define PCIM_LINK_CAP_DL_ACTIVE 0x00100000 -#define PCIM_LINK_CAP_LINK_BW_NOTIFY 0x00200000 -#define PCIM_LINK_CAP_ASPM_COMPLIANCE 0x00400000 -#define PCIM_LINK_CAP_PORT 0xff000000 -#define PCIR_EXPRESS_LINK_CTL 0x10 -#define PCIM_EXP_LINK_CTL_ASPMC_DIS 0x0000 -#define PCIM_EXP_LINK_CTL_ASPMC_L0S 0x0001 -#define PCIM_EXP_LINK_CTL_ASPMC_L1 0x0002 -#define PCIM_EXP_LINK_CTL_ASPMC 0x0003 -#define PCIM_EXP_LINK_CTL_RCB 0x0008 -#define PCIM_EXP_LINK_CTL_LINK_DIS 0x0010 -#define PCIM_EXP_LINK_CTL_RETRAIN_LINK 0x0020 -#define PCIM_EXP_LINK_CTL_COMMON_CLOCK 0x0040 -#define PCIM_EXP_LINK_CTL_EXTENDED_SYNC 0x0080 -#define PCIM_EXP_LINK_CTL_ECPM 0x0100 -#define PCIM_EXP_LINK_CTL_HAWD 0x0200 -#define PCIM_EXP_LINK_CTL_LBMIE 0x0400 -#define PCIM_EXP_LINK_CTL_LABIE 0x0800 -#define PCIR_EXPRESS_LINK_STA 0x12 -#define PCIM_LINK_STA_SPEED 0x000f -#define PCIM_LINK_STA_WIDTH 0x03f0 -#define PCIM_LINK_STA_TRAINING_ERROR 0x0400 -#define PCIM_LINK_STA_TRAINING 0x0800 -#define PCIM_LINK_STA_SLOT_CLOCK 0x1000 -#define PCIM_LINK_STA_DL_ACTIVE 0x2000 -#define PCIM_LINK_STA_LINK_BW_MGMT 0x4000 -#define PCIM_LINK_STA_LINK_AUTO_BW 0x8000 -#define PCIR_EXPRESS_SLOT_CAP 0x14 -#define PCIM_EXP_SLOT_CAP_APB 0x00000001 -#define PCIM_EXP_SLOT_CAP_PCP 0x00000002 -#define PCIM_EXP_SLOT_CAP_MRLSP 0x00000004 -#define PCIM_EXP_SLOT_CAP_AIP 0x00000008 -#define PCIM_EXP_SLOT_CAP_PIP 0x00000010 -#define PCIM_EXP_SLOT_CAP_HPS 0x00000020 -#define PCIM_EXP_SLOT_CAP_HPC 0x00000040 -#define PCIM_EXP_SLOT_CAP_SPLV 0x00007f80 -#define PCIM_EXP_SLOT_CAP_SPLS 0x00018000 -#define PCIM_EXP_SLOT_CAP_EIP 0x00020000 -#define PCIM_EXP_SLOT_CAP_NCCS 0x00040000 -#define PCIM_EXP_SLOT_CAP_PSN 0xfff80000 -#define PCIR_EXPRESS_SLOT_CTL 0x18 -#define PCIM_EXP_SLOT_CTL_ABPE 0x0001 -#define PCIM_EXP_SLOT_CTL_PFDE 0x0002 -#define PCIM_EXP_SLOT_CTL_MRLSCE 0x0004 -#define PCIM_EXP_SLOT_CTL_PDCE 0x0008 -#define PCIM_EXP_SLOT_CTL_CCIE 0x0010 -#define PCIM_EXP_SLOT_CTL_HPIE 0x0020 -#define PCIM_EXP_SLOT_CTL_AIC 0x00c0 -#define PCIM_EXP_SLOT_CTL_PIC 0x0300 -#define PCIM_EXP_SLOT_CTL_PCC 0x0400 -#define PCIM_EXP_SLOT_CTL_EIC 0x0800 -#define PCIM_EXP_SLOT_CTL_DLLSCE 0x1000 -#define PCIR_EXPRESS_SLOT_STA 0x1a -#define PCIM_EXP_SLOT_STA_ABP 0x0001 -#define PCIM_EXP_SLOT_STA_PFD 0x0002 -#define PCIM_EXP_SLOT_STA_MRLSC 0x0004 -#define PCIM_EXP_SLOT_STA_PDC 0x0008 -#define PCIM_EXP_SLOT_STA_CC 0x0010 -#define PCIM_EXP_SLOT_STA_MRLSS 0x0020 -#define PCIM_EXP_SLOT_STA_PDS 0x0040 -#define PCIM_EXP_SLOT_STA_EIS 0x0080 -#define PCIM_EXP_SLOT_STA_DLLSC 0x0100 -#define PCIR_EXPRESS_ROOT_CTL 0x1c -#define PCIR_EXPRESS_ROOT_CAP 0x1e -#define PCIR_EXPRESS_ROOT_STA 0x20 -#define PCIR_EXPRESS_DEVICE_CAP2 0x24 -#define PCIR_EXPRESS_DEVICE_CTL2 0x28 -#define PCIM_EXP_CTL2_COMP_TIMEOUT_VAL 0x000f -#define PCIM_EXP_CTL2_COMP_TIMEOUT_DIS 0x0010 -#define PCIM_EXP_CTL2_ARI 0x0020 -#define PCIM_EXP_CTL2_ATOMIC_REQ_ENABLE 0x0040 -#define PCIM_EXP_CTL2_ATOMIC_EGR_BLOCK 0x0080 -#define PCIM_EXP_CTL2_ID_ORDERED_REQ_EN 0x0100 -#define PCIM_EXP_CTL2_ID_ORDERED_CMP_EN 0x0200 -#define PCIM_EXP_CTL2_LTR_ENABLE 0x0400 -#define PCIM_EXP_CTL2_OBFF 0x6000 -#define PCIM_EXP_OBFF_DISABLE 0x0000 -#define PCIM_EXP_OBFF_MSGA_ENABLE 0x2000 -#define PCIM_EXP_OBFF_MSGB_ENABLE 0x4000 -#define PCIM_EXP_OBFF_WAKE_ENABLE 0x6000 -#define PCIM_EXP_CTL2_END2END_TLP 0x8000 -#define PCIR_EXPRESS_DEVICE_STA2 0x2a -#define PCIR_EXPRESS_LINK_CAP2 0x2c -#define PCIR_EXPRESS_LINK_CTL2 0x30 -#define PCIR_EXPRESS_LINK_STA2 0x32 -#define PCIR_EXPRESS_SLOT_CAP2 0x34 -#define PCIR_EXPRESS_SLOT_CTL2 0x38 -#define PCIR_EXPRESS_SLOT_STA2 0x3a +#define PCIER_FLAGS 0x2 +#define PCIEM_FLAGS_VERSION 0x000F +#define PCIEM_FLAGS_TYPE 0x00F0 +#define PCIEM_TYPE_ENDPOINT 0x0000 +#define PCIEM_TYPE_LEGACY_ENDPOINT 0x0010 +#define PCIEM_TYPE_ROOT_PORT 0x0040 +#define PCIEM_TYPE_UPSTREAM_PORT 0x0050 +#define PCIEM_TYPE_DOWNSTREAM_PORT 0x0060 +#define PCIEM_TYPE_PCI_BRIDGE 0x0070 +#define PCIEM_TYPE_PCIE_BRIDGE 0x0080 +#define PCIEM_TYPE_ROOT_INT_EP 0x0090 +#define PCIEM_TYPE_ROOT_EC 0x00a0 +#define PCIEM_FLAGS_SLOT 0x0100 +#define PCIEM_FLAGS_IRQ 0x3e00 +#define PCIER_DEVICE_CAP 0x4 +#define PCIEM_CAP_MAX_PAYLOAD 0x00000007 +#define PCIEM_CAP_PHANTHOM_FUNCS 0x00000018 +#define PCIEM_CAP_EXT_TAG_FIELD 0x00000020 +#define PCIEM_CAP_L0S_LATENCY 0x000001c0 +#define PCIEM_CAP_L1_LATENCY 0x00000e00 +#define PCIEM_CAP_ROLE_ERR_RPT 0x00008000 +#define PCIEM_CAP_SLOT_PWR_LIM_VAL 0x03fc0000 +#define PCIEM_CAP_SLOT_PWR_LIM_SCALE 0x0c000000 +#define PCIEM_CAP_FLR 0x10000000 +#define PCIER_DEVICE_CTL 0x8 +#define PCIEM_CTL_COR_ENABLE 0x0001 +#define PCIEM_CTL_NFER_ENABLE 0x0002 +#define PCIEM_CTL_FER_ENABLE 0x0004 +#define PCIEM_CTL_URR_ENABLE 0x0008 +#define PCIEM_CTL_RELAXED_ORD_ENABLE 0x0010 +#define PCIEM_CTL_MAX_PAYLOAD 0x00e0 +#define PCIEM_CTL_EXT_TAG_FIELD 0x0100 +#define PCIEM_CTL_PHANTHOM_FUNCS 0x0200 +#define PCIEM_CTL_AUX_POWER_PM 0x0400 +#define PCIEM_CTL_NOSNOOP_ENABLE 0x0800 +#define PCIEM_CTL_MAX_READ_REQUEST 0x7000 +#define PCIEM_CTL_BRDG_CFG_RETRY 0x8000 /* PCI-E - PCI/PCI-X bridges */ +#define PCIEM_CTL_INITIATE_FLR 0x8000 /* FLR capable endpoints */ +#define PCIER_DEVICE_STA 0xa +#define PCIEM_STA_CORRECTABLE_ERROR 0x0001 +#define PCIEM_STA_NON_FATAL_ERROR 0x0002 +#define PCIEM_STA_FATAL_ERROR 0x0004 +#define PCIEM_STA_UNSUPPORTED_REQ 0x0008 +#define PCIEM_STA_AUX_POWER 0x0010 +#define PCIEM_STA_TRANSACTION_PND 0x0020 +#define PCIER_LINK_CAP 0xc +#define PCIEM_LINK_CAP_MAX_SPEED 0x0000000f +#define PCIEM_LINK_CAP_MAX_WIDTH 0x000003f0 +#define PCIEM_LINK_CAP_ASPM 0x00000c00 +#define PCIEM_LINK_CAP_L0S_EXIT 0x00007000 +#define PCIEM_LINK_CAP_L1_EXIT 0x00038000 +#define PCIEM_LINK_CAP_CLOCK_PM 0x00040000 +#define PCIEM_LINK_CAP_SURPRISE_DOWN 0x00080000 +#define PCIEM_LINK_CAP_DL_ACTIVE 0x00100000 +#define PCIEM_LINK_CAP_LINK_BW_NOTIFY 0x00200000 +#define PCIEM_LINK_CAP_ASPM_COMPLIANCE 0x00400000 +#define PCIEM_LINK_CAP_PORT 0xff000000 +#define PCIER_LINK_CTL 0x10 +#define PCIEM_LINK_CTL_ASPMC_DIS 0x0000 +#define PCIEM_LINK_CTL_ASPMC_L0S 0x0001 +#define PCIEM_LINK_CTL_ASPMC_L1 0x0002 +#define PCIEM_LINK_CTL_ASPMC 0x0003 +#define PCIEM_LINK_CTL_RCB 0x0008 +#define PCIEM_LINK_CTL_LINK_DIS 0x0010 +#define PCIEM_LINK_CTL_RETRAIN_LINK 0x0020 +#define PCIEM_LINK_CTL_COMMON_CLOCK 0x0040 +#define PCIEM_LINK_CTL_EXTENDED_SYNC 0x0080 +#define PCIEM_LINK_CTL_ECPM 0x0100 +#define PCIEM_LINK_CTL_HAWD 0x0200 +#define PCIEM_LINK_CTL_LBMIE 0x0400 +#define PCIEM_LINK_CTL_LABIE 0x0800 +#define PCIER_LINK_STA 0x12 +#define PCIEM_LINK_STA_SPEED 0x000f +#define PCIEM_LINK_STA_WIDTH 0x03f0 +#define PCIEM_LINK_STA_TRAINING_ERROR 0x0400 +#define PCIEM_LINK_STA_TRAINING 0x0800 +#define PCIEM_LINK_STA_SLOT_CLOCK 0x1000 +#define PCIEM_LINK_STA_DL_ACTIVE 0x2000 +#define PCIEM_LINK_STA_LINK_BW_MGMT 0x4000 +#define PCIEM_LINK_STA_LINK_AUTO_BW 0x8000 +#define PCIER_SLOT_CAP 0x14 +#define PCIEM_SLOT_CAP_APB 0x00000001 +#define PCIEM_SLOT_CAP_PCP 0x00000002 +#define PCIEM_SLOT_CAP_MRLSP 0x00000004 +#define PCIEM_SLOT_CAP_AIP 0x00000008 +#define PCIEM_SLOT_CAP_PIP 0x00000010 +#define PCIEM_SLOT_CAP_HPS 0x00000020 +#define PCIEM_SLOT_CAP_HPC 0x00000040 +#define PCIEM_SLOT_CAP_SPLV 0x00007f80 +#define PCIEM_SLOT_CAP_SPLS 0x00018000 +#define PCIEM_SLOT_CAP_EIP 0x00020000 +#define PCIEM_SLOT_CAP_NCCS 0x00040000 +#define PCIEM_SLOT_CAP_PSN 0xfff80000 +#define PCIER_SLOT_CTL 0x18 +#define PCIEM_SLOT_CTL_ABPE 0x0001 +#define PCIEM_SLOT_CTL_PFDE 0x0002 +#define PCIEM_SLOT_CTL_MRLSCE 0x0004 +#define PCIEM_SLOT_CTL_PDCE 0x0008 +#define PCIEM_SLOT_CTL_CCIE 0x0010 +#define PCIEM_SLOT_CTL_HPIE 0x0020 +#define PCIEM_SLOT_CTL_AIC 0x00c0 +#define PCIEM_SLOT_CTL_PIC 0x0300 +#define PCIEM_SLOT_CTL_PCC 0x0400 +#define PCIEM_SLOT_CTL_EIC 0x0800 +#define PCIEM_SLOT_CTL_DLLSCE 0x1000 +#define PCIER_SLOT_STA 0x1a +#define PCIEM_SLOT_STA_ABP 0x0001 +#define PCIEM_SLOT_STA_PFD 0x0002 +#define PCIEM_SLOT_STA_MRLSC 0x0004 +#define PCIEM_SLOT_STA_PDC 0x0008 +#define PCIEM_SLOT_STA_CC 0x0010 +#define PCIEM_SLOT_STA_MRLSS 0x0020 +#define PCIEM_SLOT_STA_PDS 0x0040 +#define PCIEM_SLOT_STA_EIS 0x0080 +#define PCIEM_SLOT_STA_DLLSC 0x0100 +#define PCIER_ROOT_CTL 0x1c +#define PCIER_ROOT_CAP 0x1e +#define PCIER_ROOT_STA 0x20 +#define PCIER_DEVICE_CAP2 0x24 +#define PCIER_DEVICE_CTL2 0x28 +#define PCIEM_CTL2_COMP_TIMEOUT_VAL 0x000f +#define PCIEM_CTL2_COMP_TIMEOUT_DIS 0x0010 +#define PCIEM_CTL2_ARI 0x0020 +#define PCIEM_CTL2_ATOMIC_REQ_ENABLE 0x0040 +#define PCIEM_CTL2_ATOMIC_EGR_BLOCK 0x0080 +#define PCIEM_CTL2_ID_ORDERED_REQ_EN 0x0100 +#define PCIEM_CTL2_ID_ORDERED_CMP_EN 0x0200 +#define PCIEM_CTL2_LTR_ENABLE 0x0400 +#define PCIEM_CTL2_OBFF 0x6000 +#define PCIEM_OBFF_DISABLE 0x0000 +#define PCIEM_OBFF_MSGA_ENABLE 0x2000 +#define PCIEM_OBFF_MSGB_ENABLE 0x4000 +#define PCIEM_OBFF_WAKE_ENABLE 0x6000 +#define PCIEM_CTL2_END2END_TLP 0x8000 +#define PCIER_DEVICE_STA2 0x2a +#define PCIER_LINK_CAP2 0x2c +#define PCIER_LINK_CTL2 0x30 +#define PCIER_LINK_STA2 0x32 +#define PCIER_SLOT_CAP2 0x34 +#define PCIER_SLOT_CTL2 0x38 +#define PCIER_SLOT_STA2 0x3a /* MSI-X definitions */ #define PCIR_MSIX_CTRL 0x2 diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c index 493bfaa4909ee..006731080f27d 100644 --- a/sys/dev/re/if_re.c +++ b/sys/dev/re/if_re.c @@ -1343,14 +1343,14 @@ re_attach(device_t dev) /* Disable ASPM L0S/L1. */ if (sc->rl_expcap != 0) { cap = pci_read_config(dev, sc->rl_expcap + - PCIR_EXPRESS_LINK_CAP, 2); - if ((cap & PCIM_LINK_CAP_ASPM) != 0) { + PCIER_LINK_CAP, 2); + if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { ctl = pci_read_config(dev, sc->rl_expcap + - PCIR_EXPRESS_LINK_CTL, 2); + PCIER_LINK_CTL, 2); if ((ctl & 0x0003) != 0) { ctl &= ~0x0003; pci_write_config(dev, sc->rl_expcap + - PCIR_EXPRESS_LINK_CTL, ctl, 2); + PCIER_LINK_CTL, ctl, 2); device_printf(dev, "ASPM disabled\n"); } } else diff --git a/sys/ofed/include/linux/pci.h b/sys/ofed/include/linux/pci.h index f9828ef3a2820..b05f6f27c512b 100644 --- a/sys/ofed/include/linux/pci.h +++ b/sys/ofed/include/linux/pci.h @@ -82,8 +82,8 @@ struct pci_device_id { #define PCI_VENDOR_ID PCIR_DEVVENDOR #define PCI_COMMAND PCIR_COMMAND -#define PCI_EXP_DEVCTL PCIR_EXPRESS_DEVICE_CTL -#define PCI_EXP_LNKCTL PCIR_EXPRESS_LINK_CTL +#define PCI_EXP_DEVCTL PCIER_DEVICE_CTL +#define PCI_EXP_LNKCTL PCIER_LINK_CTL #define IORESOURCE_MEM SYS_RES_MEMORY #define IORESOURCE_IO SYS_RES_IOPORT diff --git a/sys/powerpc/mpc85xx/pci_fdt.c b/sys/powerpc/mpc85xx/pci_fdt.c index c431d66d0a5ac..724384d5707fb 100644 --- a/sys/powerpc/mpc85xx/pci_fdt.c +++ b/sys/powerpc/mpc85xx/pci_fdt.c @@ -848,10 +848,10 @@ fsl_pcib_err_init(device_t dev) 0xffffffff); dsr = fsl_pcib_cfgread(sc, 0, 0, 0, - sc->sc_pcie_capreg + PCIR_EXPRESS_DEVICE_STA, 2); + sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2); if (dsr) fsl_pcib_cfgwrite(sc, 0, 0, 0, - sc->sc_pcie_capreg + PCIR_EXPRESS_DEVICE_STA, + sc->sc_pcie_capreg + PCIER_DEVICE_STA, 0xffff, 2); /* Enable all errors reporting */ @@ -861,11 +861,11 @@ fsl_pcib_err_init(device_t dev) /* Enable error reporting: URR, FER, NFER */ dcr = fsl_pcib_cfgread(sc, 0, 0, 0, - sc->sc_pcie_capreg + PCIR_EXPRESS_DEVICE_CTL, 4); - dcr |= PCIM_EXP_CTL_URR_ENABLE | PCIM_EXP_CTL_FER_ENABLE | - PCIM_EXP_CTL_NFER_ENABLE; + sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4); + dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE | + PCIEM_CTL_NFER_ENABLE; fsl_pcib_cfgwrite(sc, 0, 0, 0, - sc->sc_pcie_capreg + PCIR_EXPRESS_DEVICE_CTL, dcr, 4); + sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4); } } diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index 77f116caedfd9..0dff0b2342711 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -369,55 +369,55 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) uint32_t val; uint16_t flags; - flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_FLAGS, 2); - printf("PCI-Express %d ", flags & PCIM_EXP_FLAGS_VERSION); - switch (flags & PCIM_EXP_FLAGS_TYPE) { - case PCIM_EXP_TYPE_ENDPOINT: + flags = read_config(fd, &p->pc_sel, ptr + PCIER_FLAGS, 2); + printf("PCI-Express %d ", flags & PCIEM_FLAGS_VERSION); + switch (flags & PCIEM_FLAGS_TYPE) { + case PCIEM_TYPE_ENDPOINT: printf("endpoint"); break; - case PCIM_EXP_TYPE_LEGACY_ENDPOINT: + case PCIEM_TYPE_LEGACY_ENDPOINT: printf("legacy endpoint"); break; - case PCIM_EXP_TYPE_ROOT_PORT: + case PCIEM_TYPE_ROOT_PORT: printf("root port"); break; - case PCIM_EXP_TYPE_UPSTREAM_PORT: + case PCIEM_TYPE_UPSTREAM_PORT: printf("upstream port"); break; - case PCIM_EXP_TYPE_DOWNSTREAM_PORT: + case PCIEM_TYPE_DOWNSTREAM_PORT: printf("downstream port"); break; - case PCIM_EXP_TYPE_PCI_BRIDGE: + case PCIEM_TYPE_PCI_BRIDGE: printf("PCI bridge"); break; - case PCIM_EXP_TYPE_PCIE_BRIDGE: + case PCIEM_TYPE_PCIE_BRIDGE: printf("PCI to PCIe bridge"); break; - case PCIM_EXP_TYPE_ROOT_INT_EP: + case PCIEM_TYPE_ROOT_INT_EP: printf("root endpoint"); break; - case PCIM_EXP_TYPE_ROOT_EC: + case PCIEM_TYPE_ROOT_EC: printf("event collector"); break; default: - printf("type %d", (flags & PCIM_EXP_FLAGS_TYPE) >> 4); + printf("type %d", (flags & PCIEM_FLAGS_TYPE) >> 4); break; } - if (flags & PCIM_EXP_FLAGS_SLOT) + if (flags & PCIEM_FLAGS_SLOT) printf(" slot"); - if (flags & PCIM_EXP_FLAGS_IRQ) - printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 9); - val = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_DEVICE_CAP, 4); - flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_DEVICE_CTL, 2); + if (flags & PCIEM_FLAGS_IRQ) + printf(" IRQ %d", (flags & PCIEM_FLAGS_IRQ) >> 9); + val = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP, 4); + flags = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL, 2); printf(" max data %d(%d)", - MAX_PAYLOAD((flags & PCIM_EXP_CTL_MAX_PAYLOAD) >> 5), - MAX_PAYLOAD(val & PCIM_EXP_CAP_MAX_PAYLOAD)); - if (val & PCIM_EXP_CAP_FLR) + MAX_PAYLOAD((flags & PCIEM_CTL_MAX_PAYLOAD) >> 5), + MAX_PAYLOAD(val & PCIEM_CAP_MAX_PAYLOAD)); + if (val & PCIEM_CAP_FLR) printf(" FLR"); - val = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_LINK_CAP, 4); - flags = read_config(fd, &p->pc_sel, ptr+ PCIR_EXPRESS_LINK_STA, 2); - printf(" link x%d(x%d)", (flags & PCIM_LINK_STA_WIDTH) >> 4, - (val & PCIM_LINK_CAP_MAX_WIDTH) >> 4); + val = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CAP, 4); + flags = read_config(fd, &p->pc_sel, ptr+ PCIER_LINK_STA, 2); + printf(" link x%d(x%d)", (flags & PCIEM_LINK_STA_WIDTH) >> 4, + (val & PCIEM_LINK_CAP_MAX_WIDTH) >> 4); } static void diff --git a/usr.sbin/pciconf/err.c b/usr.sbin/pciconf/err.c index 6b364f6c51db6..b67d1f55e81b7 100644 --- a/usr.sbin/pciconf/err.c +++ b/usr.sbin/pciconf/err.c @@ -63,18 +63,18 @@ static struct bit_table pci_status[] = { /* Error indicators in the PCI-Express device status register. */ static struct bit_table pcie_device_status[] = { - { PCIM_EXP_STA_CORRECTABLE_ERROR, "Correctable Error Detected" }, - { PCIM_EXP_STA_NON_FATAL_ERROR, "Non-Fatal Error Detected" }, - { PCIM_EXP_STA_FATAL_ERROR, "Fatal Error Detected" }, - { PCIM_EXP_STA_UNSUPPORTED_REQ, "Unsupported Request Detected" }, + { PCIEM_STA_CORRECTABLE_ERROR, "Correctable Error Detected" }, + { PCIEM_STA_NON_FATAL_ERROR, "Non-Fatal Error Detected" }, + { PCIEM_STA_FATAL_ERROR, "Fatal Error Detected" }, + { PCIEM_STA_UNSUPPORTED_REQ, "Unsupported Request Detected" }, { 0, NULL }, }; /* Valid error indicator bits in the PCI-Express device status register. */ -#define PCIE_ERRORS (PCIM_EXP_STA_CORRECTABLE_ERROR | \ - PCIM_EXP_STA_NON_FATAL_ERROR | \ - PCIM_EXP_STA_FATAL_ERROR | \ - PCIM_EXP_STA_UNSUPPORTED_REQ) +#define PCIE_ERRORS (PCIEM_STA_CORRECTABLE_ERROR | \ + PCIEM_STA_NON_FATAL_ERROR | \ + PCIEM_STA_FATAL_ERROR | \ + PCIEM_STA_UNSUPPORTED_REQ) /* AER Uncorrected errors. */ static struct bit_table aer_uc[] = { @@ -153,7 +153,7 @@ list_errors(int fd, struct pci_conf *p) return; /* Check for PCI-e errors. */ - sta = read_config(fd, &p->pc_sel, pcie + PCIR_EXPRESS_DEVICE_STA, 2); + sta = read_config(fd, &p->pc_sel, pcie + PCIER_DEVICE_STA, 2); print_bits("PCI-e errors", pcie_device_status, sta & PCIE_ERRORS); /* See if this device supports AER. */ -- cgit v1.3 From 8781da230cb24ebba6bfca888ab8c3dbf0685181 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Tue, 18 Sep 2012 22:09:23 +0000 Subject: if a file in plist starts with / then do not prefix it with "prefix" [1] pkg info -g returns 1 if a file mismatch [2] flush stdout in pkg info -g [3] clean up quiet mode (-q | --quiet) output of pkg_version(1) [4] fix missing error call in uname check added to pkg_version(1) [5] fix pkg_add(1) fails to install with -C from bad path [6] only resolve path from pkg_add(1) -p if the given prefix do not start with a '/' [7] PR: bin/13128 [1] bin/139015 [2] bin/113702 [3] bin/142570 [4] bin/146857 [5] bin/157543 [6] Submitted by: cy [1] Anton Yuzhaninov [2] Ighighi [3] "N.J. Mann" [4] gcooper [5] David Naylor [6] netchild [7] MFC after: 2 weeks --- usr.sbin/pkg_install/add/main.c | 4 +++- usr.sbin/pkg_install/create/perform.c | 12 ++++++++---- usr.sbin/pkg_install/info/info.h | 2 +- usr.sbin/pkg_install/info/perform.c | 4 ++-- usr.sbin/pkg_install/info/show.c | 33 +++++++++++++++++++++++--------- usr.sbin/pkg_install/lib/lib.h | 2 +- usr.sbin/pkg_install/lib/plist.c | 5 ++++- usr.sbin/pkg_install/version/perform.c | 35 +++++++++++++++++++++++----------- 8 files changed, 67 insertions(+), 30 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c index 39c1247a6b9c5..5932e1acb23ce 100644 --- a/usr.sbin/pkg_install/add/main.c +++ b/usr.sbin/pkg_install/add/main.c @@ -288,7 +288,9 @@ main(int argc, char **argv) } /* Perform chroot if requested */ if (Chroot != NULL) { - if (chroot(Chroot)) + if (chdir(Chroot)) + errx(1, "chdir to %s failed", Chroot); + if (chroot(".")) errx(1, "chroot to %s failed", Chroot); } /* Make sure the sub-execs we invoke get found */ diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c index 44095518b0e0d..e2a884ace7da3 100644 --- a/usr.sbin/pkg_install/create/perform.c +++ b/usr.sbin/pkg_install/create/perform.c @@ -215,10 +215,14 @@ pkg_perform(char **pkgs) /* Prefix should add an @cwd to the packing list */ if (Prefix) { - char resolved_prefix[PATH_MAX]; - if (realpath(Prefix, resolved_prefix) == NULL) - err(EXIT_FAILURE, "couldn't resolve path for prefix: %s", Prefix); - add_plist_top(&plist, PLIST_CWD, resolved_prefix); + if (Prefix[0] != '/') { + char resolved_prefix[PATH_MAX]; + if (realpath(Prefix, resolved_prefix) == NULL) + err(EXIT_FAILURE, "couldn't resolve path for prefix: %s", Prefix); + add_plist_top(&plist, PLIST_CWD, resolved_prefix); + } else { + add_plist_top(&plist, PLIST_CWD, Prefix); + } } /* Add the origin if asked, at the top */ diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h index 6aa1aa728a8a9..6569b5f30e4c7 100644 --- a/usr.sbin/pkg_install/info/info.h +++ b/usr.sbin/pkg_install/info/info.h @@ -77,7 +77,7 @@ extern void show_plist(const char *, Package *, plist_t, Boolean); extern void show_files(const char *, Package *); extern void show_index(const char *, const char *); extern void show_size(const char *, Package *); -extern void show_cksum(const char *, Package *); +extern int show_cksum(const char *, Package *); extern void show_origin(const char *, Package *); extern void show_fmtrev(const char *, Package *); diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c index bba2de83a7493..551f862a0326b 100644 --- a/usr.sbin/pkg_install/info/perform.c +++ b/usr.sbin/pkg_install/info/perform.c @@ -221,7 +221,7 @@ pkg_do(char *pkg) if ((Flags & SHOW_SIZE) && installed) show_size("Package Size:\n", &plist); if ((Flags & SHOW_CKSUM) && installed) - show_cksum("Mismatched Checksums:\n", &plist); + code += show_cksum("Mismatched Checksums:\n", &plist); if (Flags & SHOW_ORIGIN) show_origin("Origin:\n", &plist); if (Flags & SHOW_FMTREV) @@ -234,7 +234,7 @@ pkg_do(char *pkg) leave_playpen(); if (isTMP) unlink(fname); - return code; + return (code ? 1 : 0); } void diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c index 3dc47f9c19c1a..be05695836392 100644 --- a/usr.sbin/pkg_install/info/show.c +++ b/usr.sbin/pkg_install/info/show.c @@ -61,8 +61,10 @@ show_index(const char *title, const char *fname) strlcpy(line, "???\n", sizeof(line)); - if (!Quiet) + if (!Quiet) { printf("%s%s", InfoPrefix, title); + fflush(stdout); + } fp = fopen(fname, "r"); if (fp == (FILE *) NULL) { warnx("show_file: can't open '%s' for reading", fname); @@ -88,8 +90,10 @@ show_plist(const char *title, Package *plist, plist_t type, Boolean showall) Boolean ign = FALSE; char *prefix = NULL; - if (!Quiet) + if (!Quiet) { printf("%s%s", InfoPrefix, title); + fflush(stdout); + } p = plist->head; while (p) { if (p->type != type && showall != TRUE) { @@ -272,8 +276,10 @@ show_size(const char *title, Package *plist) char *prefix = NULL; descr = getbsize(&headerlen, &blksize); - if (!Quiet) + if (!Quiet) { printf("%s%s", InfoPrefix, title); + fflush(stdout); + } for (p = plist->head; p != NULL; p = p->next) { switch (p->type) { case PLIST_FILE: @@ -316,16 +322,19 @@ show_size(const char *title, Package *plist) } /* Show files that don't match the recorded checksum */ -void +int show_cksum(const char *title, Package *plist) { PackingList p; const char *dir = "."; char *prefix = NULL; char tmp[FILENAME_MAX]; + int errcode = 0; - if (!Quiet) + if (!Quiet) { printf("%s%s", InfoPrefix, title); + fflush(stdout); + } for (p = plist->head; p != NULL; p = p->next) if (p->type == PLIST_CWD) { @@ -337,9 +346,10 @@ show_cksum(const char *title, Package *plist) dir = p->name; } else if (p->type == PLIST_FILE) { snprintf(tmp, FILENAME_MAX, "%s/%s", elide_root(dir), p->name); - if (!fexists(tmp)) + if (!fexists(tmp)) { warnx("%s doesn't exist", tmp); - else if (p->next && p->next->type == PLIST_COMMENT && + errcode = 1; + } else if (p->next && p->next->type == PLIST_COMMENT && (strncmp(p->next->name, "MD5:", 4) == 0)) { char *cp = NULL, buf[33]; @@ -366,6 +376,7 @@ show_cksum(const char *title, Package *plist) } } } + return (errcode); } /* Show an "origin" path (usually category/portname) */ @@ -373,8 +384,10 @@ void show_origin(const char *title, Package *plist) { - if (!Quiet) + if (!Quiet) { printf("%s%s", InfoPrefix, title); + fflush(stdout); + } printf("%s\n", plist->origin != NULL ? plist->origin : ""); } @@ -383,7 +396,9 @@ void show_fmtrev(const char *title, Package *plist) { - if (!Quiet) + if (!Quiet) { printf("%s%s", InfoPrefix, title); + fflush(stdout); + } printf("%d.%d\n", plist->fmtver_maj, plist->fmtver_mnr); } diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index e368b809d825f..a6935ade78bd5 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -99,7 +99,7 @@ * Version of the package tools - increase whenever you make a change * in the code that is not cosmetic only. */ -#define PKG_INSTALL_VERSION 20120913 +#define PKG_INSTALL_VERSION 20120918 #define PKG_WRAPCONF_FNAME "/var/db/pkg_install.conf" #define main(argc, argv) real_main(argc, argv) diff --git a/usr.sbin/pkg_install/lib/plist.c b/usr.sbin/pkg_install/lib/plist.c index a1b5c559aa421..bce6d04018cb4 100644 --- a/usr.sbin/pkg_install/lib/plist.c +++ b/usr.sbin/pkg_install/lib/plist.c @@ -458,7 +458,10 @@ delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg) case PLIST_FILE: last_file = p->name; - sprintf(tmp, "%s/%s", Where, p->name); + if (*p->name == '/') + strlcpy(tmp, p->name, FILENAME_MAX); + else + sprintf(tmp, "%s/%s", Where, p->name); if (isdir(tmp) && fexists(tmp) && !issymlink(tmp)) { warnx("cannot delete specified file '%s' - it is a directory!\n" "this packing list is incorrect - ignoring delete request", tmp); diff --git a/usr.sbin/pkg_install/version/perform.c b/usr.sbin/pkg_install/version/perform.c index 733cb5e69e5e0..a5ac36fb5cb4b 100644 --- a/usr.sbin/pkg_install/version/perform.c +++ b/usr.sbin/pkg_install/version/perform.c @@ -56,10 +56,11 @@ pkg_perform(char **indexarg) struct utsname u; if (uname(&u) == -1) { - warn("%s(): failed to determine uname information", __func__); + warn("%s: failed to determine uname information", __func__); return 1; } else if ((rel_major_ver = (int) strtol(u.release, NULL, 10)) <= 0) { - + warnx("%s: bad release version specified: %s", __func__, u.release); + return 1; } /* @@ -321,19 +322,31 @@ show_version(Package plist, const char *latest, const char *source) ver = strrchr(latest, '-'); ver = ver ? &ver[1] : latest; if (cmp < 0 && OUTPUT('<')) { - printf("%-34s %c", tmp, Quiet ? '\0' : '<'); - if (Verbose) - printf(" needs updating (%s has %s)", source, ver); + if (Quiet) + printf("%s", tmp); + else { + printf("%-34s <", tmp); + if (Verbose) + printf(" needs updating (%s has %s)", source, ver); + } printf("\n"); } else if (cmp == 0 && OUTPUT('=')) { - printf("%-34s %c", tmp, Quiet ? '\0' : '='); - if (Verbose) - printf(" up-to-date with %s", source); + if (Quiet) + printf("%s", tmp); + else { + printf("%-34s =", tmp); + if (Verbose) + printf(" up-to-date with %s", source); + } printf("\n"); } else if (cmp > 0 && OUTPUT('>')) { - printf("%-34s %c", tmp, Quiet ? '\0' : '>'); - if (Verbose) - printf(" succeeds %s (%s has %s)", source, source, ver); + if (Quiet) + printf("%s", tmp); + else { + printf("%-34s >", tmp); + if (Verbose) + printf(" succeeds %s (%s has %s)", source, source, ver); + } printf("\n"); } } -- cgit v1.3 From ab2043b81eaba0d7d7769b4a58b2b6d17bc464a3 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Tue, 18 Sep 2012 22:28:42 +0000 Subject: Move major includes into /usr/share/bsdconfig for easy external access. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/Makefile | 1 + usr.sbin/bsdconfig/bsdconfig | 10 +- usr.sbin/bsdconfig/console/console | 10 +- usr.sbin/bsdconfig/console/font | 12 +- usr.sbin/bsdconfig/console/keymap | 12 +- usr.sbin/bsdconfig/console/repeat | 12 +- usr.sbin/bsdconfig/console/saver | 12 +- usr.sbin/bsdconfig/console/screenmap | 12 +- usr.sbin/bsdconfig/console/ttys | 12 +- usr.sbin/bsdconfig/diskmgmt/diskmgmt | 10 +- usr.sbin/bsdconfig/docsinstall/docsinstall | 10 +- usr.sbin/bsdconfig/dot/dot | 8 +- usr.sbin/bsdconfig/include/Makefile | 3 +- usr.sbin/bsdconfig/include/common.subr | 299 ---- usr.sbin/bsdconfig/include/dialog.subr | 1441 ------------------- usr.sbin/bsdconfig/include/mustberoot.subr | 360 ----- usr.sbin/bsdconfig/include/strings.subr | 104 -- usr.sbin/bsdconfig/include/sysrc.subr | 616 --------- usr.sbin/bsdconfig/mouse/disable | 12 +- usr.sbin/bsdconfig/mouse/enable | 12 +- usr.sbin/bsdconfig/mouse/flags | 12 +- usr.sbin/bsdconfig/mouse/mouse | 10 +- usr.sbin/bsdconfig/mouse/port | 12 +- usr.sbin/bsdconfig/mouse/type | 12 +- usr.sbin/bsdconfig/networking/Makefile | 2 +- usr.sbin/bsdconfig/networking/defaultrouter | 12 +- usr.sbin/bsdconfig/networking/devices | 22 +- usr.sbin/bsdconfig/networking/hostname | 12 +- usr.sbin/bsdconfig/networking/include/Makefile | 3 +- usr.sbin/bsdconfig/networking/include/common.subr | 65 - usr.sbin/bsdconfig/networking/include/device.subr | 489 ------- .../bsdconfig/networking/include/hostname.subr | 211 --- usr.sbin/bsdconfig/networking/include/ipaddr.subr | 236 ---- usr.sbin/bsdconfig/networking/include/media.subr | 235 ---- usr.sbin/bsdconfig/networking/include/netmask.subr | 215 --- usr.sbin/bsdconfig/networking/include/resolv.subr | 509 ------- usr.sbin/bsdconfig/networking/include/routing.subr | 168 --- usr.sbin/bsdconfig/networking/nameservers | 12 +- usr.sbin/bsdconfig/networking/networking | 12 +- usr.sbin/bsdconfig/networking/share/Makefile | 12 + usr.sbin/bsdconfig/networking/share/common.subr | 65 + usr.sbin/bsdconfig/networking/share/device.subr | 489 +++++++ usr.sbin/bsdconfig/networking/share/hostname.subr | 211 +++ usr.sbin/bsdconfig/networking/share/ipaddr.subr | 236 ++++ usr.sbin/bsdconfig/networking/share/media.subr | 235 ++++ usr.sbin/bsdconfig/networking/share/netmask.subr | 215 +++ usr.sbin/bsdconfig/networking/share/resolv.subr | 509 +++++++ usr.sbin/bsdconfig/networking/share/routing.subr | 168 +++ usr.sbin/bsdconfig/password/Makefile | 2 +- usr.sbin/bsdconfig/password/include/Makefile | 2 +- usr.sbin/bsdconfig/password/include/password.subr | 122 -- usr.sbin/bsdconfig/password/password | 12 +- usr.sbin/bsdconfig/password/share/Makefile | 11 + usr.sbin/bsdconfig/password/share/password.subr | 122 ++ usr.sbin/bsdconfig/security/kern_securelevel | 12 +- usr.sbin/bsdconfig/security/security | 12 +- usr.sbin/bsdconfig/share/Makefile | 11 + usr.sbin/bsdconfig/share/common.subr | 299 ++++ usr.sbin/bsdconfig/share/dialog.subr | 1443 ++++++++++++++++++++ usr.sbin/bsdconfig/share/mustberoot.subr | 362 +++++ usr.sbin/bsdconfig/share/strings.subr | 104 ++ usr.sbin/bsdconfig/share/sysrc.subr | 618 +++++++++ usr.sbin/bsdconfig/startup/Makefile | 2 +- usr.sbin/bsdconfig/startup/include/Makefile | 2 +- usr.sbin/bsdconfig/startup/include/rcconf.subr | 466 ------- usr.sbin/bsdconfig/startup/include/rcedit.subr | 106 -- usr.sbin/bsdconfig/startup/include/rcvar.subr | 206 --- usr.sbin/bsdconfig/startup/misc | 12 +- usr.sbin/bsdconfig/startup/rcadd | 12 +- usr.sbin/bsdconfig/startup/rcconf | 16 +- usr.sbin/bsdconfig/startup/rcdelete | 16 +- usr.sbin/bsdconfig/startup/rcedit | 12 +- usr.sbin/bsdconfig/startup/rcvar | 16 +- usr.sbin/bsdconfig/startup/share/Makefile | 11 + usr.sbin/bsdconfig/startup/share/rcconf.subr | 466 +++++++ usr.sbin/bsdconfig/startup/share/rcedit.subr | 106 ++ usr.sbin/bsdconfig/startup/share/rcvar.subr | 206 +++ usr.sbin/bsdconfig/startup/startup | 10 +- usr.sbin/bsdconfig/timezone/Makefile | 2 +- usr.sbin/bsdconfig/timezone/include/Makefile | 3 +- .../bsdconfig/timezone/include/continents.subr | 138 -- usr.sbin/bsdconfig/timezone/include/countries.subr | 78 -- usr.sbin/bsdconfig/timezone/include/iso3166.subr | 197 --- usr.sbin/bsdconfig/timezone/include/menus.subr | 220 --- usr.sbin/bsdconfig/timezone/include/zones.subr | 670 --------- usr.sbin/bsdconfig/timezone/share/Makefile | 12 + usr.sbin/bsdconfig/timezone/share/continents.subr | 138 ++ usr.sbin/bsdconfig/timezone/share/countries.subr | 78 ++ usr.sbin/bsdconfig/timezone/share/iso3166.subr | 197 +++ usr.sbin/bsdconfig/timezone/share/menus.subr | 220 +++ usr.sbin/bsdconfig/timezone/share/zones.subr | 670 +++++++++ usr.sbin/bsdconfig/timezone/timezone | 24 +- usr.sbin/bsdconfig/ttys/ttys | 10 +- usr.sbin/bsdconfig/usermgmt/Makefile | 2 +- usr.sbin/bsdconfig/usermgmt/groupadd | 8 +- usr.sbin/bsdconfig/usermgmt/groupdel | 12 +- usr.sbin/bsdconfig/usermgmt/groupedit | 12 +- usr.sbin/bsdconfig/usermgmt/groupinput | 12 +- usr.sbin/bsdconfig/usermgmt/include/Makefile | 2 +- .../bsdconfig/usermgmt/include/group_input.subr | 437 ------ .../bsdconfig/usermgmt/include/user_input.subr | 1180 ---------------- usr.sbin/bsdconfig/usermgmt/share/Makefile | 11 + usr.sbin/bsdconfig/usermgmt/share/group_input.subr | 437 ++++++ usr.sbin/bsdconfig/usermgmt/share/user_input.subr | 1180 ++++++++++++++++ usr.sbin/bsdconfig/usermgmt/useradd | 8 +- usr.sbin/bsdconfig/usermgmt/userdel | 12 +- usr.sbin/bsdconfig/usermgmt/useredit | 12 +- usr.sbin/bsdconfig/usermgmt/userinput | 12 +- usr.sbin/bsdconfig/usermgmt/usermgmt | 10 +- 109 files changed, 9116 insertions(+), 9042 deletions(-) delete mode 100644 usr.sbin/bsdconfig/include/common.subr delete mode 100644 usr.sbin/bsdconfig/include/dialog.subr delete mode 100644 usr.sbin/bsdconfig/include/mustberoot.subr delete mode 100644 usr.sbin/bsdconfig/include/strings.subr delete mode 100644 usr.sbin/bsdconfig/include/sysrc.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/common.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/device.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/hostname.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/ipaddr.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/media.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/netmask.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/resolv.subr delete mode 100644 usr.sbin/bsdconfig/networking/include/routing.subr create mode 100644 usr.sbin/bsdconfig/networking/share/Makefile create mode 100644 usr.sbin/bsdconfig/networking/share/common.subr create mode 100644 usr.sbin/bsdconfig/networking/share/device.subr create mode 100644 usr.sbin/bsdconfig/networking/share/hostname.subr create mode 100644 usr.sbin/bsdconfig/networking/share/ipaddr.subr create mode 100644 usr.sbin/bsdconfig/networking/share/media.subr create mode 100644 usr.sbin/bsdconfig/networking/share/netmask.subr create mode 100644 usr.sbin/bsdconfig/networking/share/resolv.subr create mode 100644 usr.sbin/bsdconfig/networking/share/routing.subr delete mode 100644 usr.sbin/bsdconfig/password/include/password.subr create mode 100644 usr.sbin/bsdconfig/password/share/Makefile create mode 100644 usr.sbin/bsdconfig/password/share/password.subr create mode 100644 usr.sbin/bsdconfig/share/Makefile create mode 100644 usr.sbin/bsdconfig/share/common.subr create mode 100644 usr.sbin/bsdconfig/share/dialog.subr create mode 100644 usr.sbin/bsdconfig/share/mustberoot.subr create mode 100644 usr.sbin/bsdconfig/share/strings.subr create mode 100644 usr.sbin/bsdconfig/share/sysrc.subr delete mode 100644 usr.sbin/bsdconfig/startup/include/rcconf.subr delete mode 100644 usr.sbin/bsdconfig/startup/include/rcedit.subr delete mode 100644 usr.sbin/bsdconfig/startup/include/rcvar.subr create mode 100644 usr.sbin/bsdconfig/startup/share/Makefile create mode 100644 usr.sbin/bsdconfig/startup/share/rcconf.subr create mode 100644 usr.sbin/bsdconfig/startup/share/rcedit.subr create mode 100644 usr.sbin/bsdconfig/startup/share/rcvar.subr delete mode 100644 usr.sbin/bsdconfig/timezone/include/continents.subr delete mode 100644 usr.sbin/bsdconfig/timezone/include/countries.subr delete mode 100644 usr.sbin/bsdconfig/timezone/include/iso3166.subr delete mode 100644 usr.sbin/bsdconfig/timezone/include/menus.subr delete mode 100644 usr.sbin/bsdconfig/timezone/include/zones.subr create mode 100644 usr.sbin/bsdconfig/timezone/share/Makefile create mode 100644 usr.sbin/bsdconfig/timezone/share/continents.subr create mode 100644 usr.sbin/bsdconfig/timezone/share/countries.subr create mode 100644 usr.sbin/bsdconfig/timezone/share/iso3166.subr create mode 100644 usr.sbin/bsdconfig/timezone/share/menus.subr create mode 100644 usr.sbin/bsdconfig/timezone/share/zones.subr delete mode 100644 usr.sbin/bsdconfig/usermgmt/include/group_input.subr delete mode 100644 usr.sbin/bsdconfig/usermgmt/include/user_input.subr create mode 100644 usr.sbin/bsdconfig/usermgmt/share/Makefile create mode 100644 usr.sbin/bsdconfig/usermgmt/share/group_input.subr create mode 100644 usr.sbin/bsdconfig/usermgmt/share/user_input.subr (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/Makefile b/usr.sbin/bsdconfig/Makefile index 1ce8d9cb21e02..757619457397e 100644 --- a/usr.sbin/bsdconfig/Makefile +++ b/usr.sbin/bsdconfig/Makefile @@ -12,6 +12,7 @@ SUBDIR= console \ networking \ password \ security \ + share \ startup \ timezone \ ttys \ diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index 2e8eaaed14dbc..f3e385b82e3f8 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -29,11 +29,13 @@ # ############################################################ INCLUDES +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/strings.subr + BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/strings.subr f_include_lang $BSDCFG_LIBE/include/messages.subr ############################################################ FUNCTIONS diff --git a/usr.sbin/bsdconfig/console/console b/usr.sbin/bsdconfig/console/console index d4eeec8b337ac..eec65691ca47e 100755 --- a/usr.sbin/bsdconfig/console/console +++ b/usr.sbin/bsdconfig/console/console @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/console/font b/usr.sbin/bsdconfig/console/font index 5ea3e2d225289..074f025a2d337 100755 --- a/usr.sbin/bsdconfig/console/font +++ b/usr.sbin/bsdconfig/console/font @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/console/keymap b/usr.sbin/bsdconfig/console/keymap index 63592e7c3a5d7..ca0e92ec60260 100755 --- a/usr.sbin/bsdconfig/console/keymap +++ b/usr.sbin/bsdconfig/console/keymap @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/console/repeat b/usr.sbin/bsdconfig/console/repeat index 8f968fd87cdf3..87f1e3c840b0e 100755 --- a/usr.sbin/bsdconfig/console/repeat +++ b/usr.sbin/bsdconfig/console/repeat @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/console/saver b/usr.sbin/bsdconfig/console/saver index 8e803d2ab6895..f90c837ef5f67 100755 --- a/usr.sbin/bsdconfig/console/saver +++ b/usr.sbin/bsdconfig/console/saver @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/console/screenmap b/usr.sbin/bsdconfig/console/screenmap index 9e10c9fe433a0..ddcaec82b1ea4 100755 --- a/usr.sbin/bsdconfig/console/screenmap +++ b/usr.sbin/bsdconfig/console/screenmap @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys index 7502bece7e3c8..e95d2b27806ac 100755 --- a/usr.sbin/bsdconfig/console/ttys +++ b/usr.sbin/bsdconfig/console/ttys @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="080.console" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="080.console" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/diskmgmt/diskmgmt b/usr.sbin/bsdconfig/diskmgmt/diskmgmt index 6d3fbf0ff80d8..aab901f78b557 100755 --- a/usr.sbin/bsdconfig/diskmgmt/diskmgmt +++ b/usr.sbin/bsdconfig/diskmgmt/diskmgmt @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="050.diskmgmt" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="050.diskmgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/docsinstall/docsinstall b/usr.sbin/bsdconfig/docsinstall/docsinstall index c0e11b6bdaf28..d6fe2d2e515db 100755 --- a/usr.sbin/bsdconfig/docsinstall/docsinstall +++ b/usr.sbin/bsdconfig/docsinstall/docsinstall @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="020.docsinstall" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="020.docsinstall" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/dot/dot b/usr.sbin/bsdconfig/dot/dot index e1f0e1b6eb111..47df6b7c30422 100755 --- a/usr.sbin/bsdconfig/dot/dot +++ b/usr.sbin/bsdconfig/dot/dot @@ -28,11 +28,11 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include_lang $BSDCFG_LIBE/include/messages.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 -APP_DIR="dot" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="dot" +f_include_lang $BSDCFG_LIBE/include/messages.subr f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/include/Makefile b/usr.sbin/bsdconfig/include/Makefile index 898359a97e8b1..15d1725eb829c 100644 --- a/usr.sbin/bsdconfig/include/Makefile +++ b/usr.sbin/bsdconfig/include/Makefile @@ -3,8 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/include -FILES= common.subr dialog.subr messages.subr mustberoot.subr \ - strings.subr sysrc.subr +FILES= messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/include/common.subr b/usr.sbin/bsdconfig/include/common.subr deleted file mode 100644 index c724c62f67008..0000000000000 --- a/usr.sbin/bsdconfig/include/common.subr +++ /dev/null @@ -1,299 +0,0 @@ -if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1 -# -# Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012 Devin Teske -# 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. -# -# $FreeBSD$ -# -############################################################ GLOBALS - -# -# Program name -# -pgm="${0##*/}" - -# -# Program arguments -# -ARGC="$#" -ARGV="$@" - -# -# Global exit status variables -# -SUCCESS=0 -FAILURE=1 - -############################################################ FUNCTIONS - -# -# This is an empty function by default, to use it, copy -# /usr/share/examples/bsdconfig/bsdconfigrc to $HOME/.bsdconfigrc -# -f_dprintf() -{ - : this page intentionally left blank -} - -# f_err $fmt [ $opts ... ] -# -# Print a message to stderr (fd=2). -# -f_err() -{ - printf "$@" >&2 -} - -# f_quietly $command [ $arguments ... ] -# -# run a command quietly (quell any output to stdout or stderr) -# -f_quietly() -{ - "$@" > /dev/null 2>&1 -} - -# f_have $anything ... -# -# A wrapper to the `type' built-in. Returns true if argument is a valid shell -# built-in, keyword, or externally-tracked binary, otherwise false. -# -f_have() -{ - f_quietly type "$@" -} - -# f_die [ $status [ $fmt [ $opts ... ]]] -# -# Abruptly terminate due to an error optionally displaying a message in a -# dialog box using printf(1) syntax. -# -f_die() -{ - local status=$FAILURE - - # If there is at least one argument, take it as the status - if [ $# -gt 0 ]; then - status=$1 - shift 1 # status - fi - - # If there are still arguments left, pass them to f_show_msg - [ $# -gt 0 ] && f_show_msg "$@" - - # Optionally call f_clean_up() function if it exists - f_have f_clean_up && f_clean_up - - exit $status -} - -# f_interrupt -# -# Interrupt handler. -# -f_interrupt() -{ - exec 2>&1 # fix sh(1) bug where stderr gets lost within async-trap - f_die -} - -# f_show_msg $fmt [ $opts ... ] -# -# Display a message in a dialog box using printf(1) syntax. -# -f_show_msg() -{ - local msg - msg=$( printf "$@" ) - - # - # Use f_dialog_msgbox from dialog.subr if possible, otherwise fall - # back to dialog(1) (without options, making it obvious when using - # un-aided system dialog). - # - if f_have f_dialog_msgbox; then - f_dialog_msgbox "$msg" - else - dialog --msgbox "$msg" 0 0 - fi -} - -# f_include $file -# -# Include a shell subroutine file. -# -# If the subroutine file exists but returns error status during loading, exit -# is called and execution is prematurely terminated with the same error status. -# -f_include() -{ - local file="$1" - . "$file" || exit $? -} - -# f_include_lang $file -# -# Include a language file. Automatically takes $LANG and $LC_ALL into -# considerationg when including $file (suffix ".$LC_ALL" or ".$LANG" will -# automatically by added prior to loading the language file). -# -# No error is produced if (a) a language has been requested (by setting either -# $LANG or $LC_ALL in the environment) and (b) the language file does not -# exist -- in which case we will fall back to loading $file without-suffix. -# -# If the language file exists but returns error status during loading, exit -# is called and execution is prematurely terminated with the same error status. -# -f_include_lang() -{ - local file="$1" - local lang="${LANG:-$LC_ALL}" - - f_dprintf "lang=[$lang]" - if [ -f "$file.$lang" ]; then - . "$file.$lang" || exit $? - else - . "$file" || exit $? - fi -} - -# f_usage $file [ $key1 $value1 ... ] -# -# Display USAGE file with optional pre-processor macro definitions. The first -# argument is the template file containing the usage text to be displayed. If -# $LANG or $LC_ALL (in order of preference, respectively) is set, ".encoding" -# will automatically be appended as a suffix to the provided $file pathname. -# -# When processing $file, output begins at the first line containing that is -# (a) not a comment, (b) not empty, and (c) is not pure-whitespace. All lines -# appearing after this first-line are output, including (a) comments (b) empty -# lines, and (c) lines that are purely whitespace-only. -# -# If additional arguments appear after $file, substitutions are made while -# printing the contents of the USAGE file. The pre-processor macro syntax is in -# the style of autoconf(1), for example: -# -# f_usage $file "FOO" "BAR" -# -# Will cause instances of "@FOO@" appearing in $file to be replaced with the -# text "BAR" before bering printed to the screen. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_usage_awk=' -BEGIN { found = 0 } -{ - if ( !found && $0 ~ /^[[:space:]]*($|#)/ ) next - found = 1 - print -} -' -f_usage() -{ - local file="$1" - local lang="${LANG:-$LC_ALL}" - - f_dprintf "lang=[$lang]" - - shift 1 # file - - local usage - if [ -f "$file.$lang" ]; then - usage=$( awk "$f_usage_awk" "$file.$lang" ) || exit $FAILURE - else - usage=$( awk "$f_usage_awk" "$file" ) || exit $FAILURE - fi - - while [ $# -gt 0 ]; do - local key="$1" - export value="$2" - usage=$( echo "$usage" | awk \ - "{ gsub(/@$key@/, ENVIRON[\"value\"]); print }" ) - shift 2 - done - - f_err "%s\n" "$usage" - - exit $FAILURE -} - -# f_index_menu_selection $file $pgm -# -# Process $file looking for $menu_selection values that correspond to $pgm. -# This function is for internationalization (i18n) mapping of the on-disk -# scriptname ($pgm) into the localized language (given language-specific -# $file). If $LANG or $LC_ALL (in orderder of preference, respectively) is set, -# ".encoding" will automatically be appended as a suffix to the provided $file -# pathname. -# -# If, within $file, multiple $menu_selection values map to $pgm, only the first -# one will be returned. If no mapping can be made, the NULL string is returned. -# -# If $file does not exist, error status is returned along with the NULL string. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_index_menusel_awk=' -# Variables that should be defined on the invocation line: -# -v pgm="program_name" -# -( $0 ~ "^menu_selection=.*\\|" pgm "\"" ) { - sub(/\|.*/, "") - sub(/^menu_selection="/, "") - print - exit -} -' -f_index_menu_selection() -{ - local file="$1" pgm="$2" - local lang="${LANG:-$LC_ALL}" - - f_dprintf "lang=[$lang]" - - if [ -f "$file.$lang" ]; then - awk -v pgm="$pgm" "$f_index_menusel_awk" "$file.$lang" || - exit $FAILURE - elif [ -f "$file" ]; then - awk -v pgm="$pgm" "$f_index_menusel_awk" "$file" || - exit $FAILURE - else - return $FAILURE - fi -} - -############################################################ MAIN - -# -# Trap signals so we can recover gracefully -# -trap 'f_interrupt' SIGINT -trap 'f_die' SIGTERM SIGPIPE SIGXCPU SIGXFSZ \ - SIGFPE SIGTRAP SIGABRT SIGSEGV -trap '' SIGALRM SIGPROF SIGUSR1 SIGUSR2 SIGHUP SIGVTALRM - -fi # ! $_COMMON_SUBR diff --git a/usr.sbin/bsdconfig/include/dialog.subr b/usr.sbin/bsdconfig/include/dialog.subr deleted file mode 100644 index 35c71cbd88110..0000000000000 --- a/usr.sbin/bsdconfig/include/dialog.subr +++ /dev/null @@ -1,1441 +0,0 @@ -if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/strings.subr -f_include_lang $BSDCFG_LIBE/include/messages.subr - -############################################################ CONFIGURATION - -# -# Default directory to store dialog(1) temporary files -# -: ${DIALOG_TMPDIR:="/tmp"} - -############################################################ GLOBALS - -# -# Default name of dialog(1) utility -# NOTE: This is changed to "Xdialog" by the optional `-X' argument -# -DIALOG="dialog" - -# -# Default dialog(1) title and backtitle text -# -DIALOG_TITLE="$pgm" -DIALOG_BACKTITLE="bsdconfig" - -# -# Settings used while interacting with dialog(1) -# -DIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz" - -# -# Declare that we are fully-compliant with Xdialog(1) by unset'ing all -# compatibility settings. -# -unset XDIALOG_HIGH_DIALOG_COMPAT -unset XDIALOG_FORCE_AUTOSIZE -unset XDIALOG_INFOBOX_TIMEOUT - -# -# Default behavior is to call f_dialog_init() automatically if not already -# called manually by the time the first f_dialog_*() function is used. -# -: ${DIALOG_SELF_INITIALIZE=1} - -############################################################ GENERIC FUNCTIONS - -# f_dialog_title [$new_title] -# -# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1) -# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first -# argument is NULL, the current title is returned. -# -# Each time this function is called, a backup of the current values is made -# allowing a one-time (single-level) restoration of the previous title using the -# f_dialog_title_restore() function (below). -# -f_dialog_title() -{ - local new_title="$1" - - if [ "$new_title" ]; then - if [ "$USE_XDIALOG" ]; then - _DIALOG_BACKTITLE="$DIALOG_BACKTITLE" - DIALOG_BACKTITLE="$new_title" - else - _DIALOG_TITLE="$DIALOG_TITLE" - DIALOG_TITLE="$new_title" - fi - else - if [ "$USE_XDIALOG" ]; then - echo "$DIALOG_BACKTITLE" - else - echo "$DIALOG_TITLE" - fi - fi -} - -# f_dialog_title_restore -# -# Restore the previous title set by the last call to f_dialog_title(). -# Restoration is non-recursive and only works to restore the most-recent title. -# -f_dialog_title_restore() -{ - if [ "$USE_XDIALOG" ]; then - DIALOG_BACKTITLE="$_DIALOG_BACKTITLE" - else - DIALOG_TITLE="$_DIALOG_TITLE" - fi -} - -# f_dialog_backtitle [$new_backtitle] -# -# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of -# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the -# first argument is NULL, the current backtitle is returned. -# -f_dialog_backtitle() -{ - local new_backtitle="$1" - - if [ "$new_backtitle" ]; then - if [ "$USE_XDIALOG" ]; then - _DIALOG_TITLE="$DIALOG_TITLE" - DIALOG_TITLE="$new_backtitle" - else - _DIALOG_BACKTITLE="$DIALOG_BACKTITLE" - DIALOG_BACKTITLE="$new_backtitle" - fi - else - if [ "$USE_XDIALOG" ]; then - echo "$DIALOG_TITLE" - else - echo "$DIALOG_BACKTITLE" - fi - fi -} - -# f_dialog_backtitle_restore -# -# Restore the previous backtitle set by the last call to f_dialog_backtitle(). -# Restoration is non-recursive and only works to restore the most-recent -# backtitle. -# -f_dialog_backtitle_restore() -{ - if [ "$USE_XDIALOG" ]; then - DIALOG_TITLE="$_DIALOG_TITLE" - else - DIALOG_BACKTITLE="$_DIALOG_BACKTITLE" - fi -} - -############################################################ SIZE FUNCTIONS - -# f_dialog_infobox_size $title $backtitle $prompt [$hline] -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--infobox' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, and [optionally] hline returning -# the optimal width and height for the box (not exceeding the actual terminal -# width or height). -# -# Newline character sequences (``\n'') in $prompt are expanded as-is done by -# dialog(1). -# -# Output is in the format of "height width". -# -f_dialog_infobox_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 - local min_width max_size - - if [ "$USE_XDIALOG" ]; then - min_width=35 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_width=24 - max_size=$( stty size ) # usually "24 80" - fi - - local max_height="${max_size%%[$IFS]*}" - local max_width="${max_size##*[$IFS]}" - local height width=$min_width - - # - # Bump width for long titles (but don't exceed terminal width). - # - n=$(( ${#title} + 4 )) - if [ $n -gt $width -a $n -gt $min_width ]; then - # Add 16.6% width for Xdialog(1) - [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) - - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - - # - # For Xdialog(1), bump width for long backtitles (which appear within - # the window; don't exceed maximum width). - # - if [ "$USE_XDIALOG" ]; then - n=$(( ${#btitle} + 4 )) - n=$(( $n + $n / 6 )) - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # - # Bump width for long prompts (if not already at maximum width). - # - if [ $width -lt $max_width ]; then - n=$( echo "$prompt" | f_longest_line_length ) - n=$(( $n + 4 )) - - # Add 16.6% width for Xdialog(1) - [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) - - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # - # Bump width for long hlines (if not already at maximum width). - # NOTE: Though Xdialog(1) supports `--hline', it's not currently used. - # - if [ ! "$USE_XDIALOG" ]; then - if [ $width -lt $max_width ]; then - n=$(( ${#hline} + 10 )) - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - fi - - # - # Set height based on number of rows in prompt - # - height=$( echo "$prompt" | f_number_of_lines ) - height=$(( $height + 2 )) - - # - # For Xdialog(1) bump height if backtitle is enabled (displayed in the - # X11 window with a separator line between the backtitle and msg text) - # - if [ "$USE_XDIALOG" -a "$btitle" ]; then - n=$( echo "$btitle" | f_number_of_lines ) - height=$(( $height + $n + 2 )) - fi - - # Make sure height is less than maximum screen size - [ $height -le $max_height ] || height=$max_height - - # Return both - echo "$height $width" -} - -# f_dialog_buttonbox_size $title $backtitle $prompt [$hline] -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--msgbox' and `--yesno' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, and [optionally] hline returning -# the optimal width and height for the box (not exceeding the actual terminal -# width or height). -# -# Newline character sequences (``\n'') in $prompt are expanded as-is done by -# dialog(1). -# -# Output is in the format of "height width". -# -f_dialog_buttonbox_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" - local size="$( f_dialog_infobox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local height="${size%%[$IFS]*}" - local width="${size##*[$IFS]}" - - # Add height to accomodate the buttons - height=$(( $height + 2 )) - - # Adjust for clipping with Xdialog(1) on Linux/GTK2 - [ "$USE_XDIALOG" ] && height=$(( $height + 3 )) - - # - # Enforce maximum height regardless - # - local max_size - if [ "$USE_XDIALOG" ]; then - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - max_size=$( stty size ) # usually "24 80" - fi - local max_height="${max_size%%[$IFS]*}" - [ $height -le $max_height ] || height=$max_height - - # Return both - echo "$height $width" -} - -# f_dialog_inputbox_size $title $backtitle $prompt $init [$hline] -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--inputbox' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, initial text, and [optionally] -# hline returning the optimal width and height for the box (not exceeding the -# actual terminal width and height). -# -# Newline character sequences (``\n'') in $prompt are expanded as-is done by -# dialog(1). -# -# Output is in the format of "height width". -# -f_dialog_inputbox_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" init="$4" hline="$5" n - local size="$( f_dialog_buttonbox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local height="${size%%[$IFS]*}" - local width="${size##*[$IFS]}" - - local min_width max_size - if [ "$USE_XDIALOG" ]; then - min_width=35 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_width=24 - max_size=$( stty size ) # usually "24 80" - fi - local max_height="${max_size%%[$IFS]*}" - local max_width="${max_size##*[$IFS]}" - - # - # Add height to accomodate the input box - # - [ ! "$USE_XDIALOG" ] && height=$(( $height + 3 )) - [ $height -le $max_height ] || height=$max_height - - # - # Bump width for initial text (if not already at maximum width). - # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it! - # - if [ $width -lt $max_width ]; then - n=$(( ${#init} + 7 )) - - # Add 16.6% width for Xdialog(1) - [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) - - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # Return both - echo "$height $width" -} - -# f_xdialog_2inputsbox_size $title $backtitle $prompt \ -# $label1 $init1 $label2 $init2 -# -# Xdialog(1) does not perform auto-sizing of the width and height of -# `--2inputsbox' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, label for the first field, initial -# text for said field, label for the second field, and initial text for said -# field returning the optimal width and height for the box (not exceeding the -# actual terminal width and height). -# -# Newline character sequences (``\n'') in $prompt are expanded as-is done by -# Xdialog(1). -# -# Output is in the format of "height width". -# -f_xdialog_2inputsbox_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" - local label1="$4" init1="$5" label2="$6" init2="$7" n - local size="$( f_dialog_inputbox_size \ - "$title" "$btitle" "$prompt" "$init1" )" - local height="${size%%[$IFS]*}" - local width="${size##*[$IFS]}" - - local min_width=35 - local max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - local max_height="${max_size%%[$IFS]*}" - local max_width="${max_size##*[$IFS]}" - - # Add height for first label - height=$(( $height + 2 )) - - # - # Bump width for first label text (if not already at maximum width). - # - if [ $width -lt $max_width ]; then - n=$(( ${#label1} + 7 )) - - # Add 16.6% width for Xdialog(1) - n=$(( $n + $n / 6 )) - - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # Add height for second label - height=$(( $height + 2 )) - - # - # Bump width for second label text (if not already at maximum width). - # - if [ $width -lt $max_width ]; then - n=$(( ${#label2} + 7 )) - - # Add 16.6% width for Xdialog(1) - n=$(( $n + $n / 6 )) - - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # Add height for a second inputbox - height=$(( $height + 2 )) - - # - # Bump width for second initial text (if not already at maximum width). - # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it! - # - if [ $width -lt $max_width ]; then - n=$(( ${#init2} + 7 )) - - # Add 16.6% width for Xdialog(1) - n=$(( $n + $n / 6 )) - - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # Return both - echo "$height $width" -} - -# f_dialog_menu_size $title $backtitle $prompt $hline \ -# $tag1 $item1 $tag2 $item2 ... -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--menu' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, hline and list of tag/item pairs, -# returning the optimal width and height for the menu (not exceeding the actual -# terminal width or height). -# -# Output is in the format of "height width rows". -# -f_dialog_menu_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 - local min_width min_rows max_size - - if [ "$USE_XDIALOG" ]; then - min_width=35 - min_rows=1 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_width=24 - min_rows=0 - max_size=$( stty size ) # usually "24 80" - fi - - local max_width="${max_size##*[$IFS]}" - local max_height="${max_size%%[$IFS]*}" - local box_size="$( f_dialog_infobox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local box_height="${box_size%%[$IFS]*}" - local box_width="${box_size##*[$IFS]}" - local max_rows=$(( $max_height - 8 )) - local height width=$box_width rows=$min_rows - - shift 4 # title/btitle/prompt/hline - - # If there's no prompt, bump the max-rows by 1 - [ "$prompt" ] || max_rows=$(( $max_rows + 1 )) - - # - # The sum total between the longest tag-length and longest item-length - # should be used for the menu width (not to exceed terminal width). - # - # Also, calculate the number of rows (not to exceed terminal height). - # - local longest_tag=0 longest_item=0 - while [ $# -ge 2 ]; do - local tag="$1" item="$2" - shift 2 # tag/item - - [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} - [ ${#item} -gt $longest_item ] && longest_item=${#item} - [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) - done - - # Update width - n=$(( $longest_tag + $longest_item + 10 )) - [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - - # Fix rows and set height - [ $rows -gt 0 ] || rows=1 - if [ "$USE_XDIALOG" ]; then - height=$(( $rows + $box_height + 7 )) - else - height=$(( $rows + $box_height + 4 )) - fi - [ $height -le $max_height ] || height=$max_height - - # Return all three - echo "$height $width $rows" -} - -# f_dialog_menu_with_help_size $title $backtitle $prompt $hline \ -# $tag1 $item1 $help1 $tag2 $item2 $help2 ... -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--menu' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, hline and list of tag/item/help -# triplets, returning the optimal width and height for the menu (not exceeding -# the actual terminal width or height). -# -# Output is in the format of "height width rows". -# -f_dialog_menu_with_help_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 - local min_width min_rows max_size - - if [ "$USE_XDIALOG" ]; then - min_width=35 - min_rows=1 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_width=24 - min_rows=0 - max_size=$( stty size ) # usually "24 80" - fi - - local max_width="${max_size##*[$IFS]}" - local max_height="${max_size%%[$IFS]*}" - local box_size="$( f_dialog_infobox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local box_height="${box_size%%[$IFS]*}" - local box_width="${box_size##*[$IFS]}" - local max_rows=$(( $max_height - 8 )) - local height width=$box_width rows=$min_rows - - shift 4 # title/btitle/prompt/hline - - # If there's no prompt, bump the max-rows by 1 - [ "$prompt" ] || max_rows=$(( $max_rows + 1 )) - - # - # The sum total between the longest tag-length and longest item-length - # should be used for the menu width (not to exceed terminal width). - # - # Also, calculate the number of rows (not to exceed terminal height). - # - # Also, calculate the longest help while we're here. This will be used - # to influence the width of the menu if (and only-if) using Xdialog(1). - # - local longest_tag=0 longest_item=0 longest_help=0 - while [ $# -ge 3 ]; do - local tag="$1" item="$2" help="$3" - shift 3 # tag/item/help - - [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} - [ ${#item} -gt $longest_item ] && longest_item=${#item} - [ ${#help} -gt $longest_help ] && longest_help=${#help} - [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) - done - - # Update width - n=$(( $longest_tag + $longest_item + 10 )) - [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - - # Update width for help text if using Xdialog(1) - if [ "$USE_XDIALOG" ]; then - n=$(( $longest_help + 10 )) - n=$(( $n + $n / 6 )) # +16.6% - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - fi - - # Fix rows and set height - [ $rows -gt 0 ] || rows=1 - if [ "$USE_XDIALOG" ]; then - height=$(( $rows + $box_height + 8 )) - else - height=$(( $rows + $box_height + 4 )) - fi - [ $height -le $max_height ] || height=$max_height - - # Return all three - echo "$height $width $rows" -} - -# f_dialog_radiolist_size $title $backtitle $prompt $hline \ -# $tag1 $item1 $status1 $tag2 $item2 $status2 ... -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--radiolist' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, hline and list of tag/item/status -# triplets, returning the optimal width and height for the radiolist (not -# exceeding the actual terminal width or height). -# -# Output is in the format of "height width rows". -# -f_dialog_radiolist_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 - local min_width min_rows max_size - - if [ "$USE_XDIALOG" ]; then - min_width=35 - min_rows=1 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_width=24 - min_rows=0 - max_size=$( stty size ) # usually "24 80" - fi - - local max_width="${max_size##*[$IFS]}" - local max_height="${max_size%%[$IFS]*}" - local box_size="$( f_dialog_infobox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local box_height="${box_size%%[$IFS]*}" - local box_width="${box_size##*[$IFS]}" - local max_rows=$(( $max_height - 8 )) - local height width=$box_width rows=$min_rows - - shift 4 # title/btitle/prompt/hline - - # - # The sum total between the longest tag-length, longest item-length, - # and radio-button width should be used for the menu width (not to - # exceed terminal width). - # - # Also, calculate the number of rows (not to exceed terminal height). - # - local longest_tag=0 longest_item=0 - while [ $# -ge 2 ]; do - local tag="$1" item="$2" help="$3" - shift 3 # tag/item/status - - [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} - [ ${#item} -gt $longest_item ] && longest_item=${#item} - [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) - done - - # Update width - n=$(( $longest_tag + $longest_item + 13 )) - [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) - if [ $n -gt $width -a $n -gt $min_width ]; then - if [ $n -lt $max_width ]; then - width=$n - else - width=$max_width - fi - fi - - # Fix rows and set height - [ $rows -gt 0 ] || rows=1 - if [ "$USE_XDIALOG" ]; then - height=$(( $rows + $box_height + 7 )) - else - height=$(( $rows + $box_height + 4 )) - fi - [ $height -le $max_height ] || height=$max_height - - # Return all three - echo "$height $width $rows" -} - -# f_dialog_calendar_size $title $backtitle $prompt [$hline] -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--calendar' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, and [optionally] hline returning -# the optimal width and height for the box (not exceeding the actual terminal -# width and height). -# -# Newline character sequences (``\n'') in $prompt are expanded as-is done by -# dialog(1). -# -# Output is in the format of "height width". -# -f_dialog_calendar_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" n - local size="$( f_dialog_infobox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local height="${size%%[$IFS]*}" - local width="${size##*[$IFS]}" - - local min_width min_height max_size - if [ "$USE_XDIALOG" ]; then - min_height=15 - min_width=55 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_height=0 - min_width=40 - max_size=$( stty size ) # usually "24 80" - fi - local max_height="${max_size%%[$IFS]*}" - local max_width="${max_size##*[$IFS]}" - - # - # Enforce the minimum width for displaying the calendar - # - [ $width -ge $min_width ] || width=$min_width - - # - # When using dialog(1), the calendar box is unique from other dialog(1) - # boxes in-that the height passed should not accomodate the 15-lines - # required to display the calendar. This does not apply to Xdialog(1). - # - # When using Xdialog(1), the height must accomodate the 15-lines - # required to display the calendar. - # - # NOTE: Also under dialog(1), because we can't predict whether the user - # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract - # 16 rather than 15. This does not apply to Xdialog(1). - # - max_height=$(( $max_height - 16 )) - height=$( echo "$prompt" | f_number_of_lines ) - if [ "$USE_XDIALOG" ]; then - # Add height to accomodate for the embedded calendar widget - height=$(( $height + $min_height - 1 )) - - # Also, bump height if backtitle is enabled - if [ "$btitle" ]; then - local n="$( echo "$btitle" | f_number_of_lines )" - height=$(( $height + $n + 2 )) - fi - else - [ "$prompt" ] && height=$(( $height + 1 )) - fi - [ $height -le $max_height ] || height=$max_height - - # - # The calendar box refuses to display if too large. - # - max_width=$(( $max_width - 2 )) - [ $width -le $max_width ] || width=$max_width - - # Return both - echo "$height $width" -} - -# f_dialog_timebox_size $title $backtitle $prompt [$hline] -# -# Not all versions of dialog(1) perform auto-sizing of the width and height of -# `--timebox' boxes sensibly. -# -# This function helps solve this issue by taking as arguments (in order of -# appearance) the title, backtitle, prompt, and [optionally] hline returning -# the optimal width and height for the box (not exceeding the actual terminal -# width and height). -# -# Newline character sequences (``\n'') in $prompt are expanded as-is done by -# dialog(1). -# -# Output is in the format of "height width". -# -f_dialog_timebox_size() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local title="$1" btitle="$2" prompt="$3" hline="$4" n - local size="$( f_dialog_infobox_size \ - "$title" "$btitle" "$prompt" "$hline" )" - local height="${size%%[$IFS]*}" - local width="${size##*[$IFS]}" - - local min_width min_height max_size - if [ "$USE_XDIALOG" ]; then - min_width=40 - max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION - else - min_height=0 - min_width=20 - max_size=$( stty size ) # usually "24 80" - fi - local max_height="${max_size%%[$IFS]*}" - local max_width="${max_size##*[$IFS]}" - - # - # Enforce the minimum width for displaying the timebox - # - [ $width -ge $min_width ] || width=$min_width - - # - # When using dialog(1), the timebox box is unique from other dialog(1) - # boxes in-that the height passed should not accomodate the 6-lines - # required to display the timebox. This does not apply to Xdialog(1). - # - # When using Xdialog(1), the height seems to have no effect. All values - # provide the same results. - # - # NOTE: Also under dialog(1), because we can't predict whether the user - # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract - # 7 rather than 6. This does not apply to Xdialog(1). - # - if [ "$USE_XDIALOG" ]; then - height=0 # Autosize; all values produce same results - else - max_height=$(( $max_height - 7 )) - height=$( echo "$prompt" | f_number_of_lines ) - height=$(( $height + 1 )) - [ $height -le $max_height ] || height=$max_height - [ "$prompt" ] && height=$(( $height + 1 )) - fi - - # - # The timebox box refuses to display if too large. - # - max_width=$(( $max_width - 2 )) - [ $width -le $max_width ] || width=$max_width - - # Return both - echo "$height $width" -} - -############################################################ CLEAR FUNCTIONS - -# f_dialog_clear -# -# Clears any/all previous dialog(1) displays. -# -f_dialog_clear() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - $DIALOG --clear -} - -############################################################ INFO FUNCTIONS - -# f_dialog_info $info_text ... -# -# Throw up a dialog(1) infobox. The infobox remains until another dialog is -# displayed or `dialog --clear' (or dialog_clear) is called. -# -f_dialog_info() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local info_text="$*" - local size="$( f_dialog_infobox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$info_text" )" - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - ${USE_XDIALOG:+--ignore-eof} \ - ${USE_XDIALOG:+--no-buttons} \ - --infobox \"\$info_text\" $size -} - -# f_xdialog_info $info_text ... -# -# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces -# EOF. This implies that you must execute this either as an rvalue to a pipe, -# lvalue to indirection or in a sub-shell that provides data on stdin. -# -f_xdialog_info() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local info_text="$*" - local size="$( f_dialog_infobox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$info_text" )" - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --no-close --no-buttons \ - --infobox \"\$info_text\" $size \ - -1 # timeout of -1 means abort when EOF on stdin -} - -############################################################ MSGBOX FUNCTIONS - -# f_dialog_msgbox $msg_text ... -# -# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER -# or ESC, acknowledging the modal dialog. -# -# If the user presses ENTER, the exit status is zero (success), otherwise if -# the user presses ESC the exit status is 255. -# -f_dialog_msgbox() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local msg_text="$*" - local size="$( f_dialog_buttonbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg_text" )" - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$msg_text\" $size -} - -############################################################ YESNO FUNCTIONS - -# f_dialog_yesno $msg_text ... -# -# Display a dialog(1) Yes/No prompt to allow the user to make some decision. -# The yesno prompt remains until the user presses ENTER or ESC, acknowledging -# the modal dialog. -# -# If the user chooses YES the exit status is zero, or chooses NO the exit -# status is one, or presses ESC the exit status is 255. -# -f_dialog_yesno() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local msg_text="$*" - local hline="$hline_arrows_tab_enter" - local size="$( f_dialog_buttonbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg_text" \ - "$hline" )" - - if [ "$USE_XDIALOG" ]; then - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_yes\" \ - --cancel-label \"\$msg_no\" \ - --yesno \"\$msg_text\" $size - else - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --yes-label \"\$msg_yes\" \ - --no-label \"\$msg_no\" \ - --yesno \"\$msg_text\" $size - fi -} - -# f_dialog_noyes $msg_text ... -# -# Display a dialog(1) No/Yes prompt to allow the user to make some decision. -# The noyes prompt remains until the user presses ENTER or ESC, acknowledging -# the modal dialog. -# -# If the user chooses YES the exit status is zero, or chooses NO the exit -# status is one, or presses ESC the exit status is 255. -# -# NOTE: This is just like the f_dialog_yesno function except "No" is default. -# -f_dialog_noyes() -{ - [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init - - local msg_text="$*" - local hline="$hline_arrows_tab_enter" - local size="$( f_dialog_buttonbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg_text" \ - "$hline" )" - - if [ "$USE_XDIALOG" ]; then - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --default-no \ - --ok-label \"\$msg_yes\" \ - --cancel-label \"\$msg_no\" \ - --yesno \"\$msg_text\" $size - else - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --defaultno \ - --yes-label \"\$msg_yes\" \ - --no-label \"\$msg_no\" \ - --yesno \"\$msg_text\" $size - fi -} - -############################################################ INPUT FUNCTIONS - -# f_dialog_inputstr -# -# Obtain the inputstr entered by the user from the most recently displayed -# dialog(1) inputbox and clean up any temporary files. -# -f_dialog_inputstr() -{ - local tmpfile="$DIALOG_TMPDIR/dialog.inputbox.$$" - - [ -f "$tmpfile" ] || return $FAILURE - - # Skip warnings and trim leading/trailing whitespace from user input - awk ' - BEGIN { found = 0 } - { - if ( ! found ) - { - if ( $0 ~ /^$/ ) next - if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next - found = 1 - } - sub(/^[[:space:]]*/, "") - sub(/[[:space:]]*$/, "") - print - } - ' "$tmpfile" 2> /dev/null - f_quietly rm -f "$tmpfile" - - return $SUCCESS -} - -############################################################ MENU FUNCTIONS - -# f_dialog_menutag -# -# Obtain the menutag chosen by the user from the most recently displayed -# dialog(1) menu and clean up any temporary files. -# -f_dialog_menutag() -{ - local tmpfile="$DIALOG_TMPDIR/dialog.menu.$$" - - [ -f "$tmpfile" ] || return $FAILURE - - awk ' - BEGIN { found = 0 } - { - if ( found ) # ... just spew - { - print - next - } - if ( $0 ~ /^$/ ) next - if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next - found = 1 - print - } - ' "$tmpfile" 2> /dev/null - f_quietly rm -f "$tmpfile" - - return $SUCCESS -} - -# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ... -# -# To use the `--menu' option of dialog(1) you must pass an ordered list of -# tag/item pairs on the command-line. When the user selects a menu option the -# tag for that item is printed to stderr. -# -# This function allows you to dereference the tag chosen by the user back into -# the item associated with said tag. -# -# Pass the tag chosen by the user as the first argument, followed by the -# ordered list of tag/item pairs (HINT: use the same tag/item list as was -# passed to dialog(1) for consistency). -# -# If the tag cannot be found, NULL is returned. -# -f_dialog_menutag2item() -{ - local tag="$1" tagn item - shift 1 # tag - - while [ $# -gt 0 ]; do - tagn="$1" - item="$2" - shift 2 # tagn/item - - if [ "$tag" = "$tagn" ]; then - echo "$item" - return $SUCCESS - fi - done - return $FAILURE -} - -# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \ -# $tag2 $item2 $help2 ... -# -# To use the `--menu' option of dialog(1) with the `--item-help' option, you -# must pass an ordered list of tag/item/help triplets on the command-line. When -# the user selects a menu option the tag for that item is printed to stderr. -# -# This function allows you to dereference the tag chosen by the user back into -# the item associated with said tag (help is discarded/ignored). -# -# Pass the tag chosen by the user as the first argument, followed by the -# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list -# as was passed to dialog(1) for consistency). -# -# If the tag cannot be found, NULL is returned. -# -f_dialog_menutag2item_with_help() -{ - local tag="$1" tagn item - shift 1 # tag - - while [ $# -gt 0 ]; do - tagn="$1" - item="$2" - shift 3 # tagn/item/help - - if [ "$tag" = "$tagn" ]; then - echo "$item" - return $SUCCESS - fi - done - return $FAILURE -} - -# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ... -# -# To use the `--menu' option of dialog(1) you must pass an ordered list of -# tag/item pairs on the command-line. When the user selects a menu option the -# tag for that item is printed to stderr. -# -# This function allows you to dereference the tag chosen by the user back into -# the index associated with said tag. The index is the one-based tag/item pair -# array position within the ordered list of tag/item pairs passed to dialog(1). -# -# Pass the tag chosen by the user as the first argument, followed by the -# ordered list of tag/item pairs (HINT: use the same tag/item list as was -# passed to dialog(1) for consistency). -# -# If the tag cannot be found, NULL is returned. -# -f_dialog_menutag2index() -{ - local tag="$1" tagn n=1 - shift 1 # tag - - while [ $# -gt 0 ]; do - tagn="$1" - shift 2 # tagn/item - - if [ "$tag" = "$tagn" ]; then - echo $n - return $SUCCESS - fi - n=$(( $n + 1 )) - done - return $FAILURE -} - -# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \ -# $tag2 $item2 $help2 ... -# -# To use the `--menu' option of dialog(1) with the `--item-help' option, you -# must pass an ordered list of tag/item/help triplets on the command-line. When -# the user selects a menu option the tag for that item is printed to stderr. -# -# This function allows you to dereference the tag chosen by the user back into -# the index associated with said tag. The index is the one-based tag/item/help -# triplet array position within the ordered list of tag/item/help triplets -# passed to dialog(1). -# -# Pass the tag chosen by the user as the first argument, followed by the -# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list -# as was passed to dialog(1) for consistency). -# -# If the tag cannot be found, NULL is returned. -# -f_dialog_menutag2index_with_help() -{ - local tag="$1" tagn n=1 - shift 1 # tag - - while [ $# -gt 0 ]; do - tagn="$1" - shift 3 # tagn/item/help - - if [ "$tag" = "$tagn" ]; then - echo $n - return $SUCCESS - fi - n=$(( $n + 1 )) - done - return $FAILURE -} - -############################################################ INIT FUNCTIONS - -# f_dialog_init -# -# Initialize (or re-initialize) the dialog module after setting/changing any -# of the following environment variables: -# -# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate -# that Xdialog(1) should be used instead of dialog(1). -# -# SECURE Either NULL or Non-NULL. If given a value will indicate -# that (while running as root) sudo(8) authentication is -# required to proceed. -# -f_dialog_init() -{ - DIALOG_SELF_INITIALIZE= - - # - # Process stored command-line arguments - # - SECURE=$( set -- "$ARGV" - while getopts S flag > /dev/null; do - case "$flag" in - S) echo 1;; - \?) continue;; - esac - done - ) - USE_XDIALOG=$( set -- "$ARGV" - while getopts SX flag > /dev/null; do - case "$flag" in - S|X) echo 1;; - \?) continue;; - esac - done - ) - - # - # Process `-X' command-line option - # - [ "$USE_XDIALOG" ] && DIALOG=Xdialog - - # - # Sanity check, or die gracefully - # - if ! f_have $DIALOG; then - unset USE_XDIALOG - failed_dialog="$DIALOG" - DIALOG=dialog - f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog" - fi - - # - # If we're already running as root but we got there by way of sudo(8) - # and we have X11, we should merge the xauth(1) credentials from our - # original user. - # - if [ "$USE_XDIALOG" ] && - [ "$( id -u )" = "0" ] && - [ "$SUDO_USER" -a "$DISPLAY" ] - then - if ! f_have xauth; then - # Die gracefully, as we [likely] can't use Xdialog(1) - unset USE_XDIALOG - DIALOG=dialog - f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth" - fi - HOSTNAME=$(hostname) - displaynum="${DISPLAY#*:}" - eval xauth -if \~$SUDO_USER/.Xauthority extract - \ - \"\$HOSTNAME/unix:\$displaynum\" \ - \"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \ - ~root/.Xauthority merge - > /dev/null 2>&1' - fi - - # - # Probe Xdialog(1) for maximum height/width constraints, or die - # gracefully - # - if [ "$USE_XDIALOG" ]; then - if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 ) - then - # Xdialog(1) failed, fall back to dialog(1) - unset USE_XDIALOG - size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$maxsize" "" ) - eval dialog \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$maxsize\" $size - exit $FAILURE - fi - - XDIALOG_MAXSIZE=$( - set -- ${maxsize##*:} - - height=${1%,} - width=$2 - - echo $height $width - ) - unset maxsize - fi - - # - # If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE. - # The reason for this is because many dialog(1) applications use - # --backtitle for the program name (which is better suited as - # --title with Xdialog(1)). - # - if [ "$USE_XDIALOG" ]; then - _DIALOG_TITLE="$DIALOG_TITLE" - DIALOG_TITLE="$DIALOG_BACKTITLE" - DIALOG_BACKTITLE="$_DIALOG_TITLE" - unset _DIALOG_TITLE - fi -} - -############################################################ CLEAN-UP FUNCTIONS - -# f_clean_up -# -# Clean-up routines (run when script exits or is killed). -# -f_clean_up() -{ - f_quietly rm -f "$DIALOG_TMPDIR"/dialog.*.$$ -} - -############################################################ MAIN - -# -# Trap signals so we can recover gracefully -# -trap 'f_clean_up' EXIT - -fi # ! $_DIALOG_SUBR diff --git a/usr.sbin/bsdconfig/include/mustberoot.subr b/usr.sbin/bsdconfig/include/mustberoot.subr deleted file mode 100644 index c64653056fe04..0000000000000 --- a/usr.sbin/bsdconfig/include/mustberoot.subr +++ /dev/null @@ -1,360 +0,0 @@ -if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include_lang $BSDCFG_LIBE/include/messages.subr - -############################################################ CONFIGURATION -# NOTE: These are not able to be overridden/inherited for security purposes. - -# -# Number of tries a user gets to enter his/her password before we log the -# sudo(8) failure and exit. -# -PASSWD_TRIES=3 - -# -# While in SECURE mode, should authentication as `root' be allowed? Set to -# non-NULL to enable authentication as `root', otherwise disabled. -# -# WARNING: -# Unless using a custom sudo(8) configuration, user `root' should not be -# allowed because no password is required to become `root' when already `root' -# and therefore, any value entered as password will work. -# -SECURE_ALLOW_ROOT= - -# -# While in SECURE mode, should we divulge (through error message) when the -# requested authentication user does not exist? Set to non-NULL to enable, -# otherwise a non-existent user is treated like an invalid password. -# -SECURE_DIVULGE_UNKNOWN_USER= - -############################################################ FUNCTIONS - -# f_become_root_via_sudo -# -# If not running as root, prompt for sudo(8) credentials to become root. -# Re-execution of the current program via sudo is automatically handled. -# -# The following environment variables effect functionality: -# -# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate -# that Xdialog(1) should be used instead of dialog(1). -# -f_become_root_via_sudo() -{ - local msg hline size - - [ "$( id -u )" = "0" ] && return $SUCCESS - - f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" - - # - # Check sudo(8) access before prompting for password. - # - :| sudo -S -v 2> /dev/null - if [ $? -ne $SUCCESS ]; then - # - # sudo(8) access denied. Prompt for their password. - # - msg="$msg_please_enter_password" - hline="$hline_alnum_punc_tab_enter" - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$hline" ) - - # - # Continue prompting until they either Cancel, succeed - # or exceed the number of allowed failures. - # - local password nfailures=0 retval - while [ $nfailures -lt $PASSWD_TRIES ]; do - if [ "$USE_XDIALOG" ]; then - password=$( $DIALOG \ - --title "$DIALOG_TITLE" \ - --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ - --ok-label "$msg_ok" \ - --cancel-label "$msg_cancel" \ - --password --inputbox "$msg" $size \ - 2>&1 > /dev/null ) - retval=$? - - # Catch X11-related errors - [ $retval -eq 255 ] && - f_die $retval "$password" - else - $DIALOG \ - --title "$DIALOG_TITLE" \ - --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ - --ok-label "$msg_ok" \ - --cancel-label "$msg_cancel" \ - --insecure \ - --passwordbox "$msg" $size \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - retval=$? - password=$( f_dialog_inputstr ) - fi - - # Exit if the user cancelled. - [ $retval -eq $SUCCESS ] || exit $retval - - # - # Validate sudo(8) credentials - # - sudo -S -v 2> /dev/null <<-EOF - $password - EOF - retval=$? - unset password # scrub memory - if [ $retval -eq $SUCCESS ]; then - # Access granted... - break - else - # Access denied... - nfailures=$(( $nfailures + 1 )) - - # introduce a short delay - if [ $nfailures -lt $PASSWD_TRIES ]; then - f_dialog_info "$msg_sorry_try_again" - sleep 1 - fi - fi - done - - # - # If user exhausted number of allowed password tries, log - # the security event and exit immediately. - # - if [ $nfailures -ge $PASSWD_TRIES ]; then - msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) - logger -p auth.notice -t sudo " " \ - "$USER : $msg" \ - "; TTY=$(tty)" \ - "; PWD=$PWD" \ - "; USER=root" \ - "; COMMAND=$0" - f_die 1 "sudo: $msg" - fi - fi - - # Use xauth(1) to grant root the ability to use this X11/SSH session - if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then - f_have xauth || f_die 1 \ - "$msg_no_such_file_or_directory" "$pgm" "xauth" - local HOSTNAME displaynum - HOSTNAME=$(hostname) - displaynum="${DISPLAY#*:}" - xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ - $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ - ~root/.Xauthority merge - > /dev/null 2>&1' - fi - - # Re-execute ourselves with sudo(8) - if [ $ARGC -gt 0 ]; then - exec sudo "$0" $ARGV - else - exec sudo "$0" - fi - exit $? # Never reached unless error -} - -# f_authenticate_some_user -# -# Only used if running as root and requires X11 (see USE_XDIALOG below). -# Prompts the user to enter a username and password to be authenticated via -# sudo(8) to proceed. -# -# The following environment variables effect functionality: -# -# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate -# that Xdialog(1) should be used instead of dialog(1). -# -f_authenticate_some_user() -{ - local msg hline size width height - - f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" - - # - # Secure-mode has been requested. - # - - [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" - [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" - - # - # Prompt for sudo(8) credentials. - # - - msg="$msg_please_enter_username_password" - hline="$hline_alnum_punc_tab_enter" - size=$( f_xdialog_2inputsbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$field_username" "" \ - "$field_password" "" ) - width="${size##*[$IFS]}" - height="${size%%[$IFS]*}" - height=$(( $height + 2 )) # Add height for --password - - # - # Continue prompting until they either Cancel, succeed or exceed the - # number of allowed failures. - # - local user_pass nfailures=0 retval - while [ $nfailures -lt $PASSWD_TRIES ]; do - user_pass=$( $DIALOG \ - --title "$DIALOG_TITLE" \ - --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ - --ok-label "$msg_ok" \ - --cancel-label "$msg_cancel" \ - --password --2inputsbox "$msg" \ - $height $width \ - "$field_username" "" \ - "$field_password" "" \ - 2>&1 > /dev/null ) - retval=$? - - # Catch X11-related errors - [ $retval -eq 255 ] && f_die $retval "$user_pass" - - # Exit if the user cancelled. - [ $retval -eq $SUCCESS ] || exit $retval - - # - # Make sure the user exists and is non-root - # - local user password - user="${user_pass%%/*}" - password="${user_pass#*/}" - unset user_pass # scrub memory - if [ ! "$user" ]; then - nfailures=$(( $nfailures + 1 )) - f_dialog_msgbox "$msg_no_username" - continue - fi - if [ ! "$SECURE_ALLOW_ROOT" ]; then - case "$user" in - root|toor) - nfailures=$(( $nfailures + 1 )) - f_dialog_msgbox "$( printf \ - "$msg_user_disallowed" "$user" )" - continue - esac - fi - if ! f_quietly id "$user"; then - nfailures=$(( $nfailures + 1 )) - if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then - f_dialog_msgbox "$( printf \ - "$msg_unknown_user" "$user" )" - elif [ $nfailures -lt $PASSWD_TRIES ]; then - f_dialog_info "$msg_sorry_try_again" - sleep 1 - fi - continue - fi - - # - # Validate sudo(8) credentials for given user - # - su -m "$user" <<-EOF - sh < /dev/null < /dev/null 2>&1 -} - -fi # ! $_STRINGS_SUBR diff --git a/usr.sbin/bsdconfig/include/sysrc.subr b/usr.sbin/bsdconfig/include/sysrc.subr deleted file mode 100644 index 52e57bcde2aba..0000000000000 --- a/usr.sbin/bsdconfig/include/sysrc.subr +++ /dev/null @@ -1,616 +0,0 @@ -if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include_lang $BSDCFG_LIBE/include/messages.subr - -############################################################ CONFIGURATION - -# -# Standard pathnames (inherit values from shell if available) -# -: ${RC_DEFAULTS:="/etc/defaults/rc.conf"} - -############################################################ GLOBALS - -# -# Global exit status variables -# -SUCCESS=0 -FAILURE=1 - -############################################################ FUNCTIONS - -# f_clean_env [ --except $varname ... ] -# -# Unset all environment variables in the current scope. An optional list of -# arguments can be passed, indicating which variables to avoid unsetting; the -# `--except' is required to enable the exclusion-list as the remainder of -# positional arguments. -# -# Be careful not to call this in a shell that you still expect to perform -# $PATH expansion in, because this will blow $PATH away. This is best used -# within a sub-shell block "(...)" or "$(...)" or "`...`". -# -f_clean_env() -{ - local var arg except= - - # - # Should we process an exclusion-list? - # - if [ "$1" = "--except" ]; then - except=1 - shift 1 - fi - - # - # Loop over a list of variable names from set(1) built-in. - # - for var in $( set | awk -F= \ - '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \ - | grep -v '^except$' - ); do - # - # In POSIX bourne-shell, attempting to unset(1) OPTIND results - # in "unset: Illegal number:" and causes abrupt termination. - # - [ "$var" = OPTIND ] && continue - - # - # Process the exclusion-list? - # - if [ "$except" ]; then - for arg in "$@" ""; do - [ "$var" = "$arg" ] && break - done - [ "$arg" ] && continue - fi - - unset "$var" - done -} - -# f_sysrc_get $varname -# -# Get a system configuration setting from the collection of system- -# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf -# and /etc/rc.conf). -# -# NOTE: Additional shell parameter-expansion formats are supported. For -# example, passing an argument of "hostname%%.*" (properly quoted) will -# return the hostname up to (but not including) the first `.' (see sh(1), -# "Parameter Expansion" for more information on additional formats). -# -f_sysrc_get() -{ - # Sanity check - [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE - - # Taint-check variable name - case "$1" in - [0-9]*) - # Don't expand possible positional parameters - return $FAILURE;; - *) - [ "$1" ] || return $FAILURE - esac - - ( # Execute within sub-shell to protect parent environment - - # - # Clear the environment of all variables, preventing the - # expansion of normals such as `PS1', `TERM', etc. - # - f_clean_env --except RC_CONFS RC_DEFAULTS SUCCESS - - . "$RC_DEFAULTS" > /dev/null 2>&1 - - unset RC_DEFAULTS - # no longer needed - - # - # If the query is for `rc_conf_files' then store the value that - # we inherited from sourcing RC_DEFAULTS (above) so that we may - # conditionally restore this value after source_rc_confs in the - # event that RC_CONFS does not customize the value. - # - if [ "$1" = "rc_conf_files" ]; then - _rc_conf_files="$rc_conf_files" - fi - - # - # If RC_CONFS is defined, set $rc_conf_files to an explicit - # value, modifying the default behavior of source_rc_confs(). - # - ( : ${RC_CONFS?} ) > /dev/null 2>&1 - if [ $? -eq ${SUCCESS:-0} ]; then - rc_conf_files="$RC_CONFS" - _rc_confs_set=1 - fi - - unset SUCCESS - # no longer needed - - source_rc_confs > /dev/null 2>&1 - - # - # If the query was for `rc_conf_files' AND after calling - # source_rc_confs the vaue has not changed, then we should - # restore the value to the one inherited from RC_DEFAULTS - # before performing the final query (preventing us from - # returning what was set via RC_CONFS when the intent was - # instead to query the value from the file(s) specified). - # - if [ "$1" = "rc_conf_files" -a \ - "$_rc_confs_set" -a \ - "$rc_conf_files" = "$RC_CONFS" \ - ]; then - rc_conf_files="$_rc_conf_files" - unset _rc_conf_files - unset _rc_confs_set - fi - - unset RC_CONFS - # no longer needed - - # - # This must be the last functional line for both the sub-shell - # and the function to preserve the return status from formats - # such as "${varname?}" and "${varname:?}" (see "Parameter - # Expansion" in sh(1) for more information). - # - eval echo '"${'"$1"'}"' 2> /dev/null - ) -} - -# f_sysrc_get_default $varname -# -# Get a system configuration default setting from the default rc.conf(5) file -# (or whatever RC_DEFAULTS points at). -# -f_sysrc_get_default() -{ - # Sanity check - [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE - - # Taint-check variable name - case "$1" in - [0-9]*) - # Don't expand possible positional parameters - return $FAILURE;; - *) - [ "$1" ] || return $FAILURE - esac - - ( # Execute within sub-shell to protect parent environment - - # - # Clear the environment of all variables, preventing the - # expansion of normals such as `PS1', `TERM', etc. - # - f_clean_env --except RC_DEFAULTS - - . "$RC_DEFAULTS" > /dev/null 2>&1 - - unset RC_DEFAULTS - # no longer needed - - # - # This must be the last functional line for both the sub-shell - # and the function to preserve the return status from formats - # such as "${varname?}" and "${varname:?}" (see "Parameter - # Expansion" in sh(1) for more information). - # - eval echo '"${'"$1"'}"' 2> /dev/null - ) -} - -# f_sysrc_find $varname -# -# Find which file holds the effective last-assignment to a given variable -# within the rc.conf(5) file(s). -# -# If the variable is found in any of the rc.conf(5) files, the function prints -# the filename it was found in and then returns success. Otherwise output is -# NULL and the function returns with error status. -# -f_sysrc_find() -{ - local varname="$1" - local regex="^[[:space:]]*$varname=" - local rc_conf_files="$( f_sysrc_get rc_conf_files )" - local conf_files= - local file - - # Check parameters - [ "$varname" ] || return $FAILURE - - # - # If RC_CONFS is defined, set $rc_conf_files to an explicit - # value, modifying the default behavior of source_rc_confs(). - # - [ "$RC_CONFS" ] && rc_conf_files="$RC_CONFS" - - # - # Reverse the order of files in rc_conf_files (the boot process sources - # these in order, so we will search them in reverse-order to find the - # last-assignment -- the one that ultimately effects the environment). - # - for file in $rc_conf_files; do - conf_files="$file${conf_files:+ }$conf_files" - done - - # - # Append the defaults file (since directives in the defaults file - # indeed affect the boot process, we'll want to know when a directive - # is found there). - # - conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS" - - # - # Find which file matches assignment to the given variable name. - # - for file in $conf_files; do - [ -f "$file" -a -r "$file" ] || continue - if grep -Eq "$regex" $file; then - echo $file - return $SUCCESS - fi - done - - return $FAILURE # Not found -} - -# f_sysrc_desc $varname -# -# Attempts to return the comments associated with varname from the rc.conf(5) -# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to). -# -# Multi-line comments are joined together. Results are NULL if no description -# could be found. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_sysrc_desc_awk=' -# Variables that should be defined on the invocation line: -# -v varname="varname" -# -BEGIN { - regex = "^[[:space:]]*"varname"=" - found = 0 - buffer = "" -} -{ - if ( ! found ) - { - if ( ! match($0, regex) ) next - - found = 1 - sub(/^[^#]*(#[[:space:]]*)?/, "") - buffer = $0 - next - } - - if ( !/^[[:space:]]*#/ || - /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ || - /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ || - /^[[:space:]]*$/ ) exit - - sub(/(.*#)*[[:space:]]*/, "") - buffer = buffer" "$0 -} -END { - # Clean up the buffer - sub(/^[[:space:]]*/, "", buffer) - sub(/[[:space:]]*$/, "", buffer) - - print buffer - exit ! found -} -' -f_sysrc_desc() -{ - awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS" -} - -# f_sysrc_set $varname $new_value -# -# Change a setting in the system configuration files (edits the files in-place -# to change the value in the last assignment to the variable). If the variable -# does not appear in the source file, it is appended to the end of the primary -# system configuration file `/etc/rc.conf'. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_sysrc_set_awk=' -# Variables that should be defined on the invocation line: -# -v varname="varname" -# -v new_value="new_value" -# -BEGIN { - regex = "^[[:space:]]*"varname"=" - found = retval = 0 -} -{ - # If already found... just spew - if ( found ) { print; next } - - # Does this line match an assignment to our variable? - if ( ! match($0, regex) ) { print; next } - - # Save important match information - found = 1 - matchlen = RSTART + RLENGTH - 1 - - # Store the value text for later munging - value = substr($0, matchlen + 1, length($0) - matchlen) - - # Store the first character of the value - t1 = t2 = substr(value, 0, 1) - - # Assignment w/ back-ticks, expression, or misc. - # We ignore these since we did not generate them - # - if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next } - - # Assignment w/ single-quoted value - else if ( t1 == "'\''" ) { - sub(/^'\''[^'\'']*/, "", value) - if ( length(value) == 0 ) t2 = "" - sub(/^'\''/, "", value) - } - - # Assignment w/ double-quoted value - else if ( t1 == "\"" ) { - sub(/^"(.*\\\\+")*[^"]*/, "", value) - if ( length(value) == 0 ) t2 = "" - sub(/^"/, "", value) - } - - # Assignment w/ non-quoted value - else if ( t1 ~ /[^[:space:];]/ ) { - t1 = t2 = "\"" - sub(/^[^[:space:]]*/, "", value) - } - - # Null-assignment - else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" } - - printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \ - t1, new_value, t2, value -} -END { exit retval } -' -f_sysrc_set() -{ - local varname="$1" new_value="$2" - - # Check arguments - [ "$varname" ] || return $FAILURE - - # - # Find which rc.conf(5) file contains the last-assignment - # - local not_found= - local file="$( f_sysrc_find "$varname" )" - if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then - # - # We either got a null response (not found) or the variable - # was only found in the rc.conf(5) defaults. In either case, - # let's instead modify the first file from $rc_conf_files. - # - - not_found=1 - - # - # If RC_CONFS is defined, use $RC_CONFS - # rather than $rc_conf_files. - # - if [ "$RC_CONFS" ]; then - file="${RC_CONFS%%[$IFS]*}" - else - file=$( f_sysrc_get rc_conf_files ) - file="${file%%[$IFS]*}" - fi - fi - - # - # If not found, append new value to last file and return. - # - if [ "$not_found" ]; then - echo "$varname=\"$new_value\"" >> "$file" - return $? - fi - - # - # Perform sanity checks. - # - if [ ! -w "$file" ]; then - f_err "$msg_cannot_create_permission_denied\n" \ - "$pgm" "$file" - return $FAILURE - fi - - # - # Create a new temporary file to write to. - # - local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE - - # - # Fixup permissions (else we're in for a surprise, as mktemp(1) creates - # the temporary file with 0600 permissions, and if we simply mv(1) the - # temporary file over the destination, the destination will inherit the - # permissions from the temporary file). - # - local mode - mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) - f_quietly chmod "${mode:-0644}" "$tmpfile" - - # - # Fixup ownership. The destination file _is_ writable (we tested - # earlier above). However, this will fail if we don't have sufficient - # permissions (so we throw stderr into the bit-bucket). - # - local owner - owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) - f_quietly chown "${owner:-root:wheel}" "$tmpfile" - - # - # Operate on the matching file, replacing only the last occurrence. - # - local new_contents retval - new_contents=$( tail -r $file 2> /dev/null ) - new_contents=$( echo "$new_contents" | awk -v varname="$varname" \ - -v new_value="$new_value" "$f_sysrc_set_awk" ) - retval=$? - - # - # Write the temporary file contents. - # - echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE - if [ $retval -ne $SUCCESS ]; then - echo "$varname=\"$new_value\"" >> "$tmpfile" - fi - - # - # Taint-check our results. - # - if ! /bin/sh -n "$tmpfile"; then - f_err "$msg_previous_syntax_errors\n" "$pgm" "$file" - rm -f "$tmpfile" - return $FAILURE - fi - - # - # Finally, move the temporary file into place. - # - mv "$tmpfile" "$file" -} - -# f_sysrc_delete $varname -# -# Remove a setting from the system configuration files (edits files in-place). -# Deletes all assignments to the given variable in all config files. If the -# `-f file' option is passed, the removal is restricted to only those files -# specified, otherwise the system collection of rc_conf_files is used. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_sysrc_delete_awk=' -# Variables that should be defined on the invocation line: -# -v varname="varname" -# -BEGIN { - regex = "^[[:space:]]*"varname"=" - found = 0 -} -{ - if ( $0 ~ regex ) - found = 1 - else - print -} -END { exit ! found } -' -f_sysrc_delete() -{ - local varname="$1" - local file - - # Check arguments - [ "$varname" ] || return $FAILURE - - # - # Operate on each of the specified files - # - for file in ${RC_CONFS:-$( f_sysrc_get rc_conf_files )}; do - [ -e "$file" ] || continue - - # - # Create a new temporary file to write to. - # - local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE - - # - # Fixup permissions and ownership (mktemp(1) defaults to 0600 - # permissions) to instead match the destination file. - # - local mode owner - mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) - owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) - f_quietly chmod "${mode:-0644}" "$tmpfile" - f_quietly chown "${owner:-root:wheel}" "$tmpfile" - - # - # Operate on the file, removing all occurrences, saving the - # output in our temporary file. - # - awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \ - > "$tmpfile" - if [ $? -ne $SUCCESS ]; then - # The file didn't contain any assignments - rm -f "$tmpfile" - continue - fi - - # - # Taint-check our results. - # - if ! /bin/sh -n "$tmpfile"; then - f_err "$msg_previous_syntax_errors\n" \ - "$pgm" "$file" - rm -f "$tmpfile" - return $FAILURE - fi - - # - # Perform sanity checks - # - if [ ! -w "$file" ]; then - f_err "$msg_permission_denied\n" "$pgm" "$file" - rm -f "$tmpfile" - return $FAILURE - fi - - # - # Finally, move the temporary file into place. - # - mv "$tmpfile" "$file" - done -} - -fi # ! $_SYSRC_SUBR diff --git a/usr.sbin/bsdconfig/mouse/disable b/usr.sbin/bsdconfig/mouse/disable index a3101a77210e1..099cf31b1acb7 100755 --- a/usr.sbin/bsdconfig/mouse/disable +++ b/usr.sbin/bsdconfig/mouse/disable @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="110.mouse" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="110.mouse" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/mouse/enable b/usr.sbin/bsdconfig/mouse/enable index 72565e2c083e5..e1cf7962d6d29 100755 --- a/usr.sbin/bsdconfig/mouse/enable +++ b/usr.sbin/bsdconfig/mouse/enable @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="110.mouse" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="110.mouse" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/mouse/flags b/usr.sbin/bsdconfig/mouse/flags index ab57f214e7ab7..d763a6d9b20c9 100755 --- a/usr.sbin/bsdconfig/mouse/flags +++ b/usr.sbin/bsdconfig/mouse/flags @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="110.mouse" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="110.mouse" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/mouse/mouse b/usr.sbin/bsdconfig/mouse/mouse index f85c4989d0705..4c5d903f55bc2 100755 --- a/usr.sbin/bsdconfig/mouse/mouse +++ b/usr.sbin/bsdconfig/mouse/mouse @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="110.mouse" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="110.mouse" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/mouse/port b/usr.sbin/bsdconfig/mouse/port index c7e1ae153b0e4..aed191700ac92 100755 --- a/usr.sbin/bsdconfig/mouse/port +++ b/usr.sbin/bsdconfig/mouse/port @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="110.mouse" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="110.mouse" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/mouse/type b/usr.sbin/bsdconfig/mouse/type index 651324451ca36..83a712212c396 100755 --- a/usr.sbin/bsdconfig/mouse/type +++ b/usr.sbin/bsdconfig/mouse/type @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="110.mouse" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="110.mouse" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/networking/Makefile b/usr.sbin/bsdconfig/networking/Makefile index fcd0509437fc7..4b7d5283db4ab 100644 --- a/usr.sbin/bsdconfig/networking/Makefile +++ b/usr.sbin/bsdconfig/networking/Makefile @@ -2,7 +2,7 @@ NO_OBJ= -SUBDIR= include +SUBDIR= include share FILESDIR= ${LIBEXECDIR}/bsdconfig/120.networking FILES= INDEX USAGE diff --git a/usr.sbin/bsdconfig/networking/defaultrouter b/usr.sbin/bsdconfig/networking/defaultrouter index 78d647df96066..c2375b33c807a 100755 --- a/usr.sbin/bsdconfig/networking/defaultrouter +++ b/usr.sbin/bsdconfig/networking/defaultrouter @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/networking/routing.subr -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/routing.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/networking/devices b/usr.sbin/bsdconfig/networking/devices index a12f782ff30ca..c7c550fe21dae 100755 --- a/usr.sbin/bsdconfig/networking/devices +++ b/usr.sbin/bsdconfig/networking/devices @@ -28,17 +28,17 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/device.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/media.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/netmask.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/networking/device.subr +f_include $BSDCFG_SHARE/networking/ipaddr.subr +f_include $BSDCFG_SHARE/networking/media.subr +f_include $BSDCFG_SHARE/networking/netmask.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/networking/hostname b/usr.sbin/bsdconfig/networking/hostname index aec731d202cd7..32679b456d218 100755 --- a/usr.sbin/bsdconfig/networking/hostname +++ b/usr.sbin/bsdconfig/networking/hostname @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/networking/hostname.subr -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/hostname.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/networking/include/Makefile b/usr.sbin/bsdconfig/networking/include/Makefile index 858ceba0ea86d..c93d4866fe9f5 100644 --- a/usr.sbin/bsdconfig/networking/include/Makefile +++ b/usr.sbin/bsdconfig/networking/include/Makefile @@ -3,8 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/120.networking/include -FILES= common.subr device.subr hostname.subr ipaddr.subr media.subr \ - messages.subr netmask.subr resolv.subr routing.subr +FILES= messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/networking/include/common.subr b/usr.sbin/bsdconfig/networking/include/common.subr deleted file mode 100644 index 13e9795396636..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/common.subr +++ /dev/null @@ -1,65 +0,0 @@ -if [ ! "$_NETWORKING_COMMON_SUBR" ]; then _NETWORKING_COMMON_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 - -############################################################ FUNCTIONS - -# f_mounted $local_directory -# -# Return success if a filesystem is mounted on a particular directory. -# -f_mounted() -{ - local dir="$1" - [ -d "$dir" ] || return $FAILURE - mount | grep -Eq " on $dir \([^)]+\)$" -} - -# f_jailed -# -# Returns true if the current process is jail(8)ed. -# -f_jailed() -{ - ! f_quietly ps 1 -} - -# f_nfs_mounted -# -# Returns true if there are any NFS mounts currently active, otherwise false. -# -f_nfs_mounted() -{ - [ "$( df -t nfs )" ] -} - -fi # ! $_NETWORKING_COMMON_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/device.subr b/usr.sbin/bsdconfig/networking/include/device.subr deleted file mode 100644 index 530885407808e..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/device.subr +++ /dev/null @@ -1,489 +0,0 @@ -if [ ! "$_NETWORKING_DEVICE_SUBR" ]; then _NETWORKING_DEVICE_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/media.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/netmask.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/resolv.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/routing.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ GLOBALS - -# -# Settings used while interacting with various dialog(1) menus -# -: ${DIALOG_MENU_NETDEV_KICK_INTERFACES=1} -: ${DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK=3} - -############################################################ FUNCTIONS - -# f_device_desc $device_name -# -# Print a description for a device name (eg., `fxp0'). -# -f_device_desc() -{ - local device="$1" d="[1234567890]" desc="" - - # Check variables - [ "$device" ] || return $SUCCESS - - # - # Return sysctl MIB dev.NAME.UNIT.%desc if it exists, - # otherwise fall through to below static list. - # - if f_have sysctl; then - local devname devunit - devname="${device%%$d*}" - devunit="${device#$devname}" - devunit="${devunit%%[a-zA-Z_]*}" - sysctl -n "dev.$devname.$devunit.%desc" 2> /dev/null && - return $SUCCESS - fi - - case "$device" in - # Network devices - ae$d) desc="Attansic/Atheros L2 Fast Ethernet";; - age$d) desc="Attansic/Atheros L1 Gigabit Ethernet";; - alc$d) desc="Atheros AR8131/AR8132 PCIe Ethernet";; - ale$d) desc="Atheros AR8121/AR8113/AR8114 PCIe Ethernet";; - an$d) desc="Aironet 4500/4800 802.11 wireless adapter";; - ath$d) desc="Atheros IEEE 802.11 wireless adapter";; - aue$d) desc="ADMtek USB Ethernet adapter";; - axe$d) desc="ASIX Electronics USB Ethernet adapter";; - bce$d) desc="Broadcom NetXtreme II Gigabit Ethernet card";; - bfe$d) desc="Broadcom BCM440x PCI Ethernet card";; - bge$d) desc="Broadcom BCM570x PCI Gigabit Ethernet card";; - bm$d) desc="Apple BMAC Built-in Ethernet";; - bwn$d) desc="Broadcom BCM43xx IEEE 802.11 wireless adapter";; - cas$d) desc="Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet";; - cc3i$d) desc="SDL HSSI sync serial PCI card";; - cue$d) desc="CATC USB Ethernet adapter";; - cxgb$d) desc="Chelsio T3 10Gb Ethernet card";; - dc$d) desc="DEC/Intel 21143 (and clones) PCI Fast Ethernet card";; - de$d) desc="DEC DE435 PCI NIC or other DC21040-AA based card";; - disc$d) desc="Software discard network interface";; - ed$d) desc="Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA";; - el$d) desc="3Com 3C501 Ethernet card";; - em$d) desc="Intel(R) PRO/1000 Ethernet card";; - en$d) desc="Efficient Networks ATM PCI card";; - ep$d) desc="3Com 3C509 Ethernet card/3C589 PCMCIA";; - et$d) desc="Agere ET1310 based PCI Express Gigabit Ethernet card";; - ex$d) desc="Intel EtherExpress Pro/10 Ethernet card";; - fe$d) desc="Fujitsu MB86960A/MB86965A Ethernet card";; - fpa$d) desc="DEC DEFPA PCI FDDI card";; - fwe$d) desc="FireWire Ethernet emulation";; - fwip$d) desc="IP over FireWire";; - fxp$d) desc="Intel EtherExpress Pro/100B PCI Fast Ethernet card";; - gem$d) desc="Apple GMAC or Sun ERI/GEM Ethernet adapter";; - hme$d) desc="Sun HME (Happy Meal Ethernet) Ethernet adapter";; - ie$d) desc="AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210";; - igb$d) desc="Intel(R) PRO/1000 PCI Express Gigabit Ethernet card";; - ipw$d) desc="Intel PRO/Wireless 2100 IEEE 802.11 adapter";; - iwi$d) desc="Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter";; - iwn$d) desc="Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter";; - ix$d) desc="Intel Etherexpress Ethernet card";; - ixgb$d) desc="Intel(R) PRO/10Gb Ethernet card";; - ixgbe$d) desc="Intel(R) PRO/10Gb Ethernet card";; - jme$d) desc="JMicron JMC250 Gigabit/JMC260 Fast Ethernet";; - kue$d) desc="Kawasaki LSI USB Ethernet adapter";; - le$d) desc="AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter";; - lge$d) desc="Level 1 LXT1001 Gigabit Ethernet card";; - lnc$d) desc="Lance/PCnet (Isolan/Novell NE2100/NE32-VL) Ethernet";; - lp$d) desc="Parallel Port IP (PLIP) peer connection";; - lo$d) desc="Loop-back (local) network interface";; - malo$d) desc="Marvell Libertas 88W8335 802.11 wireless adapter";; - msk$d) desc="Marvell/SysKonnect Yukon II Gigabit Ethernet";; - mxge$d) desc="Myricom Myri10GE 10Gb Ethernet card";; - nfe$d) desc="NVIDIA nForce MCP Ethernet";; - ng${d}_*|ng$d${d}_*|ng$d$d${d}_*|ng$d$d$d${d}_*|ng$d$d$d$d${d}_*) - desc="Vimage netgraph(4) bridged Ethernet device";; - nge$d) desc="NatSemi PCI Gigabit Ethernet card";; - nve$d) desc="NVIDIA nForce MCP Ethernet";; - nxge$d) desc="Neterion Xframe 10GbE Server/Storage adapter";; - pcn$d) desc="AMD Am79c79x PCI Ethernet card";; - plip$d) desc="Parallel Port IP (PLIP) peer connection";; - ral$d) desc="Ralink Technology IEEE 802.11 wireless adapter";; - ray$d) desc="Raytheon Raylink 802.11 wireless adapter";; - re$d) desc="RealTek 8139C+/8169/8169S/8110S PCI Ethernet adapter";; - rl$d) desc="RealTek 8129/8139 PCI Ethernet card";; - rue$d) desc="RealTek USB Ethernet card";; - rum$d) desc="Ralink Technology USB IEEE 802.11 wireless adapter";; - sf$d) desc="Adaptec AIC-6915 PCI Ethernet card";; - sge$d) desc="Silicon Integrated Systems SiS190/191 Ethernet";; - sis$d) desc="SiS 900/SiS 7016 PCI Ethernet card";; - sk$d) desc="SysKonnect PCI Gigabit Ethernet card";; - sn$d) desc="SMC/Megahertz Ethernet card";; - snc$d) desc="SONIC Ethernet card";; - sr$d) desc="SDL T1/E1 sync serial PCI card";; - ste$d) desc="Sundance ST201 PCI Ethernet card";; - stge$d) desc="Sundance/Tamarack TC9021 Gigabit Ethernet";; - ti$d) desc="Alteon Networks PCI Gigabit Ethernet card";; - tl$d) desc="Texas Instruments ThunderLAN PCI Ethernet card";; - tx$d) desc="SMC 9432TX Ethernet card";; - txp$d) desc="3Com 3cR990 Ethernet card";; - uath$d) desc="Atheros AR5005UG and AR5005UX USB wireless adapter";; - upgt$d) desc="Conexant/Intersil PrismGT USB wireless adapter";; - ural$d) desc="Ralink Technology RT2500USB 802.11 wireless adapter";; - urtw$d) desc="Realtek 8187L USB wireless adapter";; - vge$d) desc="VIA VT612x PCI Gigabit Ethernet card";; - vlan$d|vlan$d$d|vlan$d$d$d|vlan$d$d$d$d|vlan$d$d$d$d$d) - desc="IEEE 802.1Q VLAN network interface";; - vr$d) desc="VIA VT3043/VT86C100A Rhine PCI Ethernet card";; - vx$d) desc="3COM 3c590 / 3c595 Ethernet card";; - wb$d) desc="Winbond W89C840F PCI Ethernet card";; - wi$d) desc="Lucent WaveLAN/IEEE 802.11 wireless adapter";; - wpi$d) desc="Intel 3945ABG IEEE 802.11 wireless adapter";; - wx$d) desc="Intel Gigabit Ethernet (82452) card";; - xe$d) desc="Xircom/Intel EtherExpress Pro100/16 Ethernet card";; - xl$d) desc="3COM 3c90x / 3c90xB PCI Ethernet card";; - zyd$d) desc="ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter";; - # Unknown device - *) desc="";; - esac - printf "%s\n" "$desc" -} - -# f_dialog_menu_netdev -# -# Display a list of network devices with descriptions. -# -f_dialog_menu_netdev() -{ - # - # Display a message to let the user know we're working... - # (message will remain until we throw up the next dialog) - # - f_dialog_info "$msg_probing_network_interfaces" - - # - # Get list of usable network interfaces - # - local d='[[:digit:]]+:' - local iflist="`echo "$(ifconfig -l):" | sed -E -e " - # Convert all spaces to colons - y/ /:/ - - # Prune unsavory interfaces - s/lo$d//g - s/ppp$d//g - s/sl$d//g - s/lp$d//g - s/fwe$d//g - s/faith$d//g - s/plip$d//g - - # Convert all colons back into spaces - y/:/ / - "`" - - # - # Optionally kick interfaces in the head to get them to accurately - # track the carrier status in realtime (required on FreeBSD). - # - if [ "$DIALOG_MENU_NETDEV_KICK_INTERFACES" ]; then - DIALOG_MENU_NETDEV_KICK_INTERFACES= - - local ifn - for ifn in $iflist; do - f_quietly ifconfig $ifn up - done - - if [ "$DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK" ]; then - # interfaces need time to update carrier status - sleep $DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK - fi - fi - - # - # Mark any "active" interfaces with an asterisk (*) - # to the right of the device name. - # - interfaces=$( - for ifn in $iflist; do - active=$( ifconfig $ifn | awk \ - ' - ( $1 == "status:" ) \ - { - if ( $2 == "active" ) { print 1; exit } - } - ' ) - printf "'%s%s' '%s'\n" \ - "$ifn" "${active:+*}" "$(f_device_desc "$ifn")" - done - ) - if [ ! "$interfaces" ]; then - f_dialog_msgbox "$msg_no_network_interfaces" - return $FAILURE - fi - - local hline="$hline_arrows_tab_enter" - - # - # Ask user to select an interface - # - local prompt size - prompt="$msg_select_network_interface" - size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $interfaces ) - eval $DIALOG \ - --clear --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$prompt\" $size \ - $interfaces \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" -} - -# f_dialog_menu_netdev_edit $interface $ipaddr $netmask $options $dhcp -# -# Allow a user to edit network interface settings. Current values are not -# probed but rather taken from the positional arguments. -# -f_dialog_menu_netdev_edit() -{ - local interface="$1" ipaddr="$2" netmask="$3" options="$4" dhcp="$5" - local prompt menu_list size - - # - # Create a duplicate set of variables for change-tracking... - # - local ipaddr_orig="$2" \ - netmask_orig="$3" \ - options_orig="$4" \ - dhcp_orig="$5" - - local hline="$hline_arrows_tab_enter" - prompt=$( printf "$msg_network_configuration" "$interface" ) - - # - # Loop forever until the user has finished configuring the different - # components of the network interface. - # - # To apply the settings, we need to know each of the following: - # - IP Address - # - Network subnet mask - # - Additional ifconfig(8) options - # - # It is only when we have all of the above values that we can make the - # changes effective because all three options must be specified at-once - # to ifconfig(8). - # - while :; do - local dhcp_status="$msg_disabled" - [ "$dhcp" ] && dhcp_status="$msg_enabled" - - # - # Display configuration-edit menu - # - menu_list=" - 'X $msg_save_exit' '$msg_return_to_previous_menu' - '2 $msg_dhcp' '$dhcp_status' - '3 $msg_ipaddr4' '$ipaddr' - '4 $msg_netmask' '$netmask' - '5 $msg_options' '$options' - " - size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list ) - eval $DIALOG \ - --clear --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$prompt\" $size \ - $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" - - local retval=$? - local tag="$( f_dialog_menutag )" - - # Return if "Cancel" was chosen (-1) or ESC was pressed (255) - [ $retval -eq $SUCCESS ] || return $retval - - # - # Call the below ``modifier functions'' whose job it is to take - # input from the user and assign the newly-acquired values back - # to the ipaddr, netmask, and options variables for us to re- - # read and display in the summary dialog. - # - case "$tag" in - X\ *) break;; - 2\ *) # - # Do not proceed if/when there are NFS-mounts currently - # active. If the network is changed while NFS-exported - # directories are mounted, the system may hang (if any - # NFS mounts are using that interface). - # - if f_nfs_mounted && ! f_jailed; then - local setting="$( printf "$msg_current_dhcp_status" \ - "$interface" "$dhcp_status" )" - local message="$( - printf "$msg_nfs_mounts_may_cause_hang" \ - "$setting" - )" - f_dialog_msgbox "$message" - continue - fi - - # - # Toggle DHCP status - # - if [ "$dhcp_status" = "$msg_enabled" ]; then - dhcp= - else - trap - SIGINT - ( # Execute within sub-shell to allow/catch Ctrl-C - trap 'exit $FAILURE' SIGINT - msg=$( printf "$msg_scanning_for_dhcp" "$interface" ) - if [ "$USE_XDIALOG" ]; then - ( - f_quietly ifconfig $interface delete - f_quietly dhclient $interface - ) | - f_xdialog_info "$msg" - else - f_dialog_info "$msg" - f_quietly ifconfig $interface delete - f_quietly dhclient $interface - fi - ) - retval=$? - trap 'interrupt' SIGINT - if [ $retval -eq $SUCCESS ]; then - dhcp=1 - ipaddr=$( f_ifconfig_inet $interface ) - netmask=$( f_ifconfig_netmask $interface ) - options= - - # Fixup search/domain in resolv.conf(5) - hostname=$( f_sysrc_get \ - 'hostname:-$(hostname)' ) - f_dialog_resolv_conf_update "$hostname" - fi - fi - ;; - 3\ *) f_dialog_input_ipaddr "$interface" "$ipaddr" - [ $? -eq $SUCCESS ] && dhcp=;; - 4\ *) f_dialog_input_netmask "$interface" "$netmask" - [ $? -eq $SUCCESS -a "$_netmask" ] && dhcp=;; - 5\ *) f_dialog_menu_media_options "$interface" "$options" - [ $? -eq $SUCCESS ] && dhcp=;; - esac - done - - # - # Save only if the user changed at least one feature of the interface - # - if [ "$ipaddr" != "$ipaddr_orig" -o \ - "$netmask" != "$netmask_orig" -o \ - "$options" != "$options_orig" -o \ - "$dhcp" != "$dhcp_orig" ] - then - f_dialog_info "$( printf "$msg_saving_network_interface" \ - "$interface" )" - - local value= - if [ "$dhcp" ]; then - f_sysrc_delete defaultrouter - value=DHCP - else - value="inet $ipaddr netmask $netmask" - value="$value${options:+ }$options" - fi - - f_sysrc_set ifconfig_$interface "$value" - fi - - # - # Re/Apply the settings if desired - # - if [ ! "$dhcp" ]; then - f_dialog_yesno "Would you like to bring the $interface" \ - "interface up right now?" - if [ $? -eq $SUCCESS ]; then - f_dialog_info "$( printf "$msg_bring_interface_up" \ - "$interface" )" - - local dr="$( f_sysrc_get defaultrouter )" err - if [ "$dr" = "NO" -o ! "$dr" ]; then - dr=$( f_route_get_default ) - [ "$dr" ] && f_sysrc_set defaultrouter "$dr" - fi - # - # Make a backup of resolv.conf(5) before using - # ifconfig(8) and then restore it afterward. This - # allows preservation of nameservers acquired via - # DHCP on FreeBSD-8.x (normally lost as ifconfig(8) - # usage causes dhclient(8) to exit which scrubs - # resolv.conf(5) by-default upon termination). - # - f_quietly cp -fp "$RESOLV_CONF" "$RESOLV_CONF.$$" - err=$( ifconfig $interface inet $ipaddr \ - netmask $netmask $options 2>&1 ) - if [ $? -eq $SUCCESS ]; then - if [ "$dr" -a "$dr" != "NO" ]; then - err=$( route add default "$dr" 2>&1 ) - [ $? -eq $SUCCESS ] || \ - dialog_msgbox "$err" - fi - else - dialog_msgbox "$err" - fi - if cmp -s "$RESOLV_CONF" "$RESOLV_CONF.$$"; then - f_quietly rm -f "$RESOLV_CONF.$$" - else - f_quietly mv -f "$RESOLV_CONF.$$" "$RESOLV_CONF" - fi - fi - fi - - return $SUCCESS -} - -fi # ! $_NETWORKING_DEVICE_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/hostname.subr b/usr.sbin/bsdconfig/networking/include/hostname.subr deleted file mode 100644 index 0f671fe2f56e4..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/hostname.subr +++ /dev/null @@ -1,211 +0,0 @@ -if [ ! "$_NETWORKING_HOSTNAME_SUBR" ]; then _NETWORKING_HOSTNAME_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/sysrc.subr -f_include $BSDCFG_LIBE/include/dialog.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/resolv.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_dialog_validate_hostname $hostname -# -# Returns zero if the given argument (a fully-qualified hostname) is compliant -# with standards set-forth in RFC's 952 and 1123 of the Network Working Group: -# -# RFC 952 - DoD Internet host table specification -# http://tools.ietf.org/html/rfc952 -# -# RFC 1123 - Requirements for Internet Hosts - Application and Support -# http://tools.ietf.org/html/rfc1123 -# -# See http://en.wikipedia.org/wiki/Hostname for a brief overview. -# -# The return status for invalid hostnames is one of: -# 255 Entire hostname exceeds the maximum length of 255 characters. -# 63 One or more individual labels within the hostname (separated by -# dots) exceeds the maximum of 63 characters. -# 1 One or more individual labels within the hostname contains one -# or more invalid characters. -# 2 One or more individual labels within the hostname starts or -# ends with a hyphen (hyphens are allowed, but a label cannot -# begin or end with a hyphen). -# 3 One or more individual labels within the hostname are null. -# -# If the hostname is determined to be invalid, the appropriate error will be -# displayed using the f_dialog_msgbox function. -# -f_dialog_validate_hostname() -{ - local fqhn="$1" - - ( # Operate within a sub-shell to protect the parent environment - - # Return error if the hostname exceeds 255 characters - [ ${#fqhn} -gt 255 ] && exit 255 - - IFS="." # Split on `dot' - for label in $fqhn; do - - # Return error if the label exceeds 63 characters - [ ${#label} -gt 63 ] && exit 63 - - # Return error if the label is null - [ "$label" ] || exit 3 - - # Return error if label begins/ends with dash - case "$label" in - -*|*-) exit 2 - esac - - # Return error if the label contains any invalid chars - echo "$label" | grep -q '^[[:alnum:]-]*$' || exit 1 - - done - ) - - # - # Produce an appropriate error message if necessary. - # - local retval=$? - case $retval in - 1) f_dialog_msgbox "$( printf \ - "$msg_hostname_label_contains_invalid_chars" "$fqhn" )";; - 2) f_dialog_msgbox "$( printf \ - "$msg_hostname_label_starts_or_ends_with_hyphen" "$fqhn" )";; - 3) f_dialog_msgbox "$( printf \ - "$msg_hostname_label_is_null" "$fqhn" )";; - 63) f_dialog_msgbox "$( printf \ - "$msg_hostname_label_exceeds_max_length" "$fqhn" )";; - 255) f_dialog_msgbox "$( printf \ - "$msg_hostname_exceeds_max_length" "$fqhn" )";; - esac - - return $retval -} - -# f_dialog_input_hostname -# -# Edits the current hostname. -# -f_dialog_input_hostname() -{ - local hostname="$( f_sysrc_get 'hostname:-$(hostname)' )" - local hostname_orig="$hostname" # for change-tracking - - local msg - if [ "$USE_XDIALOG" ]; then - msg="$xmsg_please_enter_fqhn" - else - msg="$msg_please_enter_fqhn" - fi - - local hline="$hline_alnum_punc_tab_enter" - - # - # Loop until the user provides taint-free input. - # - local size height width - while :; do - - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$hostname" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$hostname\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - - local retval=$? - hostname=$( f_dialog_inputstr ) - - [ $retval -eq $SUCCESS ] || return $retval - - # Taint-check the user's input - f_dialog_validate_hostname "$hostname" && break - - done - - # - # Save hostname only if the user changed the hostname. - # - if [ "$hostname" != "$hostname_orig" ]; then - f_dialog_info "$msg_saving_hostname" - f_sysrc_set hostname "$hostname" - fi - - # - # Update resolv.conf(5) search/domain directives - # - f_dialog_resolv_conf_update "$hostname" - - # - # Only ask to apply setting if the current hostname is different than - # the stored configuration (in rc.conf(5)). - # - if [ "$( hostname )" != "$( f_sysrc_get hostname )" ]; then - [ ! "$USE_XDIALOG" ] && dialog_clear - - # - # If connected via ssh(1) and performing X11-Forwarding, don't - # allow the hostname to be changed to prevent the fatal error - # "X11 connection rejected because of wrong authentication." - # - if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" ]; then - f_dialog_msgbox "$( - printf "$msg_activate_hostname_x11warning" \ - "$( hostname )" "$hostname" - )" - else - f_dialog_yesno "$( - printf "$msg_activate_hostname" \ - "$( hostname )" "$hostname" \ - )" \ - && hostname "$hostname" - fi - fi - - return $SUCCESS -} - -fi # ! $_NETWORKING_HOSTNAME_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/ipaddr.subr b/usr.sbin/bsdconfig/networking/include/ipaddr.subr deleted file mode 100644 index 7333302c98286..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/ipaddr.subr +++ /dev/null @@ -1,236 +0,0 @@ -if [ ! "$_NETWORKING_IPADDR_SUBR" ]; then _NETWORKING_IPADDR_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/sysrc.subr -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/strings.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_ifconfig_inet $interface -# -# Returns the IPv4 address associated with $interface. -# -f_ifconfig_inet() -{ - local interface="$1" - ifconfig "$interface" 2> /dev/null | awk \ - ' - BEGIN { found = 0 } - ( $1 == "inet" ) \ - { - print $2 - found = 1 - exit - } - END { exit ! found } - ' -} - -# f_dialog_validate_ipaddr $ipaddr -# -# Returns zero if the given argument (an IP address) is of the proper format. -# -# The return status for invalid IP address is one of: -# 1 One or more individual octets within the IP address (separated -# by dots) contains one or more invalid characters. -# 2 One or more individual octets within the IP address are null -# and/or missing. -# 3 One or more individual octets within the IP address exceeds the -# maximum of 255 (or 2^8, being an octet comprised of 8 bits). -# 4 The IP address has either too few or too many octets. -# -# If the IP address is determined to be invalid, the appropriate error will be -# displayed using the above dialog_msgbox function. -# -f_dialog_validate_ipaddr() -{ - local ip="$1" - - ( # Operate within a sub-shell to protect the parent environment - - # Track number of octets for error checking - noctets=0 - - IFS="." # Split on `dot' - for octet in $ip; do - - # Return error if the octet is null - [ "$octet" ] || exit 2 - - # Return error if not a whole integer - f_isinteger "$octet" || exit 1 - - # Return error if not a positive integer - [ $octet -ge 0 ] || exit 1 - - # Return error if the octet exceeds 255 - [ $octet -gt 255 ] && exit 3 - - noctets=$(( $noctets + 1 )) - - done - - [ $noctets -eq 4 ] || exit 4 - ) - - # - # Produce an appropriate error message if necessary. - # - local retval=$? - case $retval in - 1) f_dialog_msgbox "$( printf \ - "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip" )";; - 2) f_dialog_msgbox "$( printf \ - "$msg_ipv4_addr_octet_is_null" "$ip" )";; - 3) f_dialog_msgbox "$( printf \ - "$msg_ipv4_addr_octet_exceeds_max_value" "$ip" )";; - 4) f_dialog_msgbox "$( printf \ - "$msg_ipv4_addr_octet_missing_or_extra" "$ip" )";; - esac - - return $retval -} - -# f_dialog_input_ipaddr $interface $ipaddr -# -# Allows the user to edit a given IP address. If the user does not cancel or -# press ESC, the $ipaddr environment variable will hold the newly-configured -# value upon return. -# -# Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the -# netmask at the same time as the IP address. If such a format is entered by -# the user, the $netmask environment variable will hold the newly-configured -# netmask upon return. -# -f_dialog_input_ipaddr() -{ - local interface="$1" _ipaddr="$2" _input - - # - # Return with-error when there are NFS-mounts currently active. If the - # IP address is changed while NFS-exported directories are mounted, the - # system may hang (if any NFS mounts are using that interface). - # - if f_nfs_mounted && ! f_jailed; then - local setting="$( printf "$msg_current_ipaddr" \ - "$interface" "$_ipaddr" )" - local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ - "$setting" )" - f_dialog_msgbox "$message" - return $FAILURE - fi - - local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" - local hline="$hline_num_punc_tab_enter" - local size="$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_ipaddr" \ - "$hline" )" - - # - # Loop until the user provides taint-free input. - # - while :; do - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_ipaddr\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - - local retval=$? - _input=$( f_dialog_inputstr ) - - # - # Return error status if: - # - User has not made any changes to the given value - # - User has either pressed ESC or chosen Cancel/No - # - [ "$_ipaddr" = "$_input" ] && return $FAILURE - [ $retval -eq $SUCCESS ] || return $retval - - # Return success if NULL value was entered - [ "$_input" ] || return $SUCCESS - - # Take only the first "word" of the user's input - _ipaddr="$_input" - _ipaddr="${_ipaddr%%[$IFS]*}" - - # Taint-check the user's input - f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break - done - - # - # Support the syntax: IP_ADDRESS/NBITS - # - local _netmask="" - case "$_ipaddr" in - */*) - local nbits="${_ipaddr#*/}" n=0 - _ipaddr="${_ipaddr%%/*}" - - # - # Taint-check $nbits to be (a) a positive whole-integer, - # and (b) to be less than or equal to 32. Otherwise, set - # $n so that the below loop never executes. - # - ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \ - || n=4 - - while [ $n -lt 4 ]; do - _netmask="$_netmask${_netmask:+.}$(( - (65280 >> ($nbits - 8 * $n) & 255) - * ((8*$n) < $nbits & $nbits <= (8*($n+1))) - + 255 * ($nbits > (8*($n+1))) - ))" - n=$(( $n + 1 )) - done - ;; - esac - - ipaddr="$_ipaddr" - [ "$_netmask" ] && netmask="$_netmask" - - return $SUCCESS -} - -fi # ! $_NETWORKING_IPADDR_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/media.subr b/usr.sbin/bsdconfig/networking/include/media.subr deleted file mode 100644 index 2d8bd3bc23c8d..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/media.subr +++ /dev/null @@ -1,235 +0,0 @@ -if [ ! "$_NETWORKING_MEDIA_SUBR" ]; then _NETWORKING_MEDIA_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/strings.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_ifconfig_options $interface -# -# Returns any/all extra ifconfig(8) parameters associated with $interface. -# -f_ifconfig_options() -{ - local interface="$1" - [ "$interface" ] || return $SUCCESS - - # - # Loop over the options, removing what we don't want - # - ( - set -- $( f_sysrc_get ifconfig_$interface ) - - # - # Return if the interface is configured for DHCP - # - glob="[Dd][Hh][Cc][Pp]" - case "$*" in - $glob|[Ss][Yy][Nn][Cc]$glob|[Nn][Oo][Ss][Yy][Nn][Cc]$glob) - exit $SUCCESS - esac - - output= - while [ $# -gt 0 ]; do - case "$1" in - inet|netmask) shift 1;; - *) output="$output${output:+ }$1" - esac - shift 1 - done - echo "$output" - ) -} - -# f_ifconfig_media $interface -# -# Returns list of supported media for $interface. -# -f_ifconfig_media() -{ - local interface="$1" - ifconfig -m "$interface" 2> /dev/null | awk \ - ' - BEGIN { media_found = 0 } - { - if ( media_found == 1 ) { print; next } - } - ( $1 $2 == "supported" "media:" ) \ - { - media_found = 1 - next - } - END { exit ! media_found } - ' -} - -# f_dialog_input_options $interface -# -# Input custom interface options. -# -f_dialog_input_options() -{ - local interface="$1" - - # - # Return with-error when there are NFS-mounts currently active. If the - # options are changed while NFS-exported directories are mounted, - # the system may hang (if any NFS mounts are using that interface). - # - if f_nfs_mounted && ! f_jailed; then - local setting="$( printf "$msg_current_options" \ - "$interface" "$options" )" - local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ - "$setting" )" - f_dialog_msgbox "$message" - return $FAILURE - fi - - local msg="$( printf "$msg_please_enter_mediaopts" "$interface" )" - local hline="$hline_alnum_punc_tab_enter" - - $DIALOG \ - --title "$DIALOG_TITLE" \ - --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ - --ok-label "$msg_ok" \ - --cancel-label "$msg_cancel" \ - --inputbox "$msg" 9 70 \ - "$options" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - - local retval=$? - local _options="$( f_dialog_inputstr )" - - [ $retval -eq $SUCCESS ] && options="$_options" - - return $retval -} - -# f_dialog_menu_media_options $interface -# -# Display a menu of additional media options for the given network interface. -# -f_dialog_menu_media_options() -{ - local interface="$1" _options="$2" - # - # Not all network interfaces support additional media options, but - # when available we should prompt the user to select from a list - # of available options (or none, as is the first/default option). - # - - # - # Return with-error when there are NFS-mounts currently active. If the - # media options are changed while NFS-exported directories are mounted, - # the system may hang (if any NFS mounts are using that interface). - # - if f_nfs_mounted && ! f_jailed; then - local setting="$( printf "$msg_current_options" \ - "$interface" "$_options" )" - local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ - "$setting" )" - f_dialog_msgbox "$message" - return $FAILURE - fi - - # - # Build list of additional media options - # - local opt_none="$msg_no_options" - local opt_cust="$msg_custom" - local supported_media="$( - f_ifconfig_media $interface | \ - ( index=1 - - echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" - echo "'$opt_none'" - index=$(( $index + 1 )) - - echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" - echo "'$opt_cust'" - index=$(( $index + 1 )) - - while read media_options; do - [ $index -lt ${#DIALOG_MENU_TAGS} ] || break - echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" - echo "'$media_options'" - index=$(( $index + 1 )) - done - ) - )" - - local msg - if [ "$USE_XDIALOG" ]; then - msg=$( printf "$xmsg_supported_media_options" \ - "$interface" "$interface" ) - else - msg=$( printf "$msg_supported_media_options" \ - "$interface" "$interface" ) - fi - - local hline="$hline_arrows_tab_enter" - - eval $DIALOG \ - --clear --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$msg\" 21 60 12 \ - $supported_media \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" - - local retval=$? - if [ $retval -eq $SUCCESS ]; then - local tag="$( f_dialog_menutag )" - options=$( eval f_dialog_menutag2item \"\$tag\" \ - $supported_media ) - [ "$options" = "$opt_none" ] && options= - - if [ "$options" = "$opt_cust" ]; then - options="$_options" - f_dialog_input_options "$interface" - retval=$? - fi - fi - - return $retval -} - -fi # ! $_NETWORKING_MEDIA_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/netmask.subr b/usr.sbin/bsdconfig/networking/include/netmask.subr deleted file mode 100644 index 169acca007df4..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/netmask.subr +++ /dev/null @@ -1,215 +0,0 @@ -if [ ! "$_NETWORKING_NETMASK_SUBR" ]; then _NETWORKING_NETMASK_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/strings.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_ifconfig_netmask $interface -# -# Returns the IPv4 subnet mask associated with $interface. -# -f_ifconfig_netmask() -{ - local interface="$1" octets - octets=$( ifconfig "$interface" 2> /dev/null | awk \ - ' - BEGIN { found = 0 } - ( $1 == "inet" ) \ - { - printf "%s %s %s %s\n", - substr($4,3,2), - substr($4,5,2), - substr($4,7,2), - substr($4,9,2) - found = 1 - exit - } - END { exit ! found } - ' ) || return $FAILURE - - local octet netmask= - for octet in $octets; do - netmask="$netmask${netmask:+.}$( printf "%u" "0x$octet" )" - done - echo $netmask -} - -# f_dialog_validate_netmask $netmask -# -# Returns zero if the given argument (a subnet mask) is of the proper format. -# -# The return status for invalid IP address is one of: -# 1 One or more individual fields within the subnet mask (separated -# by dots) contains one or more invalid characters. -# 2 One or more individual fields within the subnet mask are null -# and/or missing. -# 3 One or more individual fields within the subnet mask exceeds -# the maximum of 255 (a full 8-bit register). -# 4 The subnet mask has either too few or too many fields. -# 5 One or more individual fields within the subnet mask is an -# invalid integer (only 0,128,192,224,240,248,252,254,255 are -# valid integers). -# -# If the subnet mask is determined to be invalid, the appropriate error will be -# displayed using the f_dialog_msgbox function. -# -f_dialog_validate_netmask() -{ - local mask="$1" - - ( # Operate within a sub-shell to protect the parent environment - - # Track number of fields for error checking - nfields=0 - - IFS="." # Split on `dot' - for field in $mask; do - - # Return error if the field is null - [ "$field" ] || exit 2 - - # Return error if not a whole positive integer - f_isinteger "$field" || exit 1 - - # Return error if the field exceeds 255 - [ $field -gt 255 ] && exit 3 - - # Return error if the field is an invalid integer - case "$field" in - 0|128|192|224|240|248|252|254|255) :;; - *) exit 5;; - esac - - nfields=$(( $nfields + 1 )) - - done - - [ $nfields -eq 4 ] || exit 4 - ) - - # - # Produce an appropriate error message if necessary. - # - local retval=$? - case $retval in - 1) f_dialog_msgbox "$( printf \ - "$msg_ipv4_mask_field_contains_invalid_chars" "$mask" )";; - 2) f_dialog_msgbox "$( printf \ - "$msg_ipv4_mask_field_is_null" "$mask" )";; - 3) f_dialog_msgbox "$( printf \ - "$msg_ipv4_mask_field_exceeds_max_value" "$mask" )";; - 4) f_dialog_msgbox "$( printf \ - "$msg_ipv4_mask_field_missing_or_extra" "$mask" )";; - 5) f_dialog_msgbox "$( printf \ - "$msg_ipv4_mask_field_invalid_value" "$mask" )";; - esac - - return $retval -} - -# f_dialog_input_netmask $interface $netmask -# -# Edits the IP netmask of the given interface. -# -f_dialog_input_netmask() -{ - local interface="$1" _netmask="$2" _input - - # - # Return with-error when there are NFS-mounts currently active. If the - # subnet mask is changed while NFS-exported directories are mounted, - # the system may hang (if any NFS mounts are using that interface). - # - if f_nfs_mounted && ! f_jailed; then - local setting="$( printf "$msg_current_subnet" \ - "$interface" "$_netmask" )" - local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ - "$setting" )" - f_dialog_msgbox "$message" - return $FAILURE - fi - - local msg="$( printf "$msg_please_enter_subnet_mask" "$interface" )" - local hline="$hline_num_punc_tab_enter" - local size="$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_netmask" \ - "$hline" )" - - # - # Loop until the user provides taint-free input. - # - while :; do - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_netmask\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - - local retval=$? - _input=$( f_dialog_inputstr ) - - # - # Return error status if: - # - User has not made any changes to the given value - # - User has either pressed ESC or chosen Cancel/No - # - [ "$_netmask" = "$_input" ] && return $FAILURE - [ $retval -eq $SUCCESS ] || return $retval - - # Return success if NULL value was entered - [ "$_input" ] || return $SUCCESS - - # Take only the first "word" of the user's input - _netmask="$_input" - _netmask="${_netmask%%[$IFS]*}" - - # Taint-check the user's input - f_dialog_validate_netmask "$_netmask" && break - done - - netmask="$_netmask" -} - -fi # ! $_NETWORKING_NETMASK_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/resolv.subr b/usr.sbin/bsdconfig/networking/include/resolv.subr deleted file mode 100644 index 8eab18e186a02..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/resolv.subr +++ /dev/null @@ -1,509 +0,0 @@ -if [ ! "$_NETWORKING_RESOLV_SUBR" ]; then _NETWORKING_RESOLV_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/strings.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ CONFIGURATION - -# -# Path to resolv.conf(5). -# -: ${RESOLV_CONF:="/etc/resolv.conf"} - -# -# When updating resolv.conf(5), should we populate the `search' directive with -# all possible sub-domains? In example, if the domain is "sub.domain.com", when -# the below option is set to 1, include both "sub.domain.com" and "domain.com" -# in the `search' directive, otherwise use only "sub.domain.com". -# -# When enabled (set to 1), specify the minimum number of dots required for each -# `search' domain by setting the second option below, `RESOLVER_SEARCH_NDOTS'. -# -: ${RESOLVER_SEARCH_DOMAINS_ALL:=1} -: ${RESOLVER_SEARCH_NDOTS:=1} - -############################################################ FUNCTIONS - -# f_resolv_conf_domain -# -# Returns the domain configured in resolv.conf(5). -# -f_resolv_conf_domain() -{ - tail -r "$RESOLV_CONF" 2> /dev/null | awk \ - ' - BEGIN { found = 0 } - ( tolower($1) == "domain" ) \ - { - print $2 - found = 1 - exit - } - END { exit ! found } - ' -} - -# f_resolv_conf_search -# -# Returns the search configured in resolv.conf(5). -# -f_resolv_conf_search() -{ - tail -r "$RESOLV_CONF" 2> /dev/null | awk \ - ' - BEGIN { found = 0 } - { - tl0 = tolower($0) - if ( match(tl0, /^[[:space:]]*search[[:space:]]+/) ) { - search = substr($0, RLENGTH + 1) - sub(/[[:space:]]*#.*$/, "", search) - gsub(/[[:space:]]+/, " ", search) - print search - found = 1 - exit - } - } - END { exit ! found } - ' -} - -# f_resolv_conf_nameservers -# -# Returns nameserver(s) configured in resolv.conf(5). -# -f_resolv_conf_nameservers() -{ - awk \ - ' - BEGIN { found = 0 } - ( $1 == "nameserver" ) \ - { - print $2 - found = 1 - } - END { exit ! found } - ' \ - "$RESOLV_CONF" 2> /dev/null -} - -# f_dialog_resolv_conf_update $hostname -# -# Updates the search/domain directives in resolv.conf(5) given a valid fully- -# qualified hostname. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_dialog_resolv_conf_update_awk=' -# Variables that should be defined on the invocation line: -# -v domain="domain" -# -v search_all="0|1" -# -v search_ndots="1+" -# -BEGIN { - domain_found = search_found = 0 - - if ( search_all ) { - search = "" - subdomain = domain - if ( search_ndots < 1 ) - search_ndots = 1 - - ndots = split(subdomain, labels, ".") - 1 - while ( ndots-- >= search_ndots ) { - if ( length(search) ) search = search " " - search = search subdomain - sub(/[^.]*\./, "", subdomain) - } - } - else search = domain -} -{ - if ( domain_found && search_found ) { print; next } - - tl0 = tolower($0) - if ( ! domain_found && \ - match(tl0, /^[[:space:]]*domain[[:space:]]+/) ) \ - { - if ( length(domain) ) { - printf "%s%s\n", substr($0, 0, RLENGTH), domain - domain_found = 1 - } - } - else if ( ! search_found && \ - match(tl0, /^[[:space:]]*search[[:space:]]+/) ) \ - { - if ( length(search) ) { - printf "%s%s\n", substr($0, 0, RLENGTH), search - search_found = 1 - } - } - else print -} -END { - if ( ! search_found && length(search) ) - printf "search\t%s\n", search - if ( ! domain_found && length(domain) ) - printf "domain\t%s\n", domain -} -' -f_dialog_resolv_conf_update() -{ - local hostname="$1" - - # - # Extrapolate the desired domain search parameter for resolv.conf(5) - # - local search ndots domain="${hostname#*.}" - if [ "$RESOLVER_SEARCH_DOMAINS_ALL" = "1" ]; then - search="" - ndots=$( IFS=.; set -- $domain; echo $(( $# - 1 )) ) - while [ $ndots -ge ${RESOLVER_SEARCH_NDOTS:-1} ]; do - search="$search${search:+ }$domain" - domain="${domain#*.}" - ndots=$(( $ndots - 1 )) - done - domain="${hostname#*.}" - else - search="$domain" - fi - - # - # Save domain/search information only if different from resolv.conf(5) - # - if [ "$domain" != "$( f_resolv_conf_domain )" -o \ - "$search" != "$( f_resolv_conf_search )" ] - then - f_dialog_info "Saving new domain/search settings" \ - "to resolv.conf(5)..." - - # - # Create a new temporary file to write our resolv.conf(5) - # update with our new `domain' and `search' directives. - # - local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE - - # - # Fixup permissions and ownership (mktemp(1) creates the - # temporary file with 0600 permissions -- change the - # permissions and ownership to match resolv.conf(5) before - # we write it out and mv(1) it into place). - # - local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null )" - local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null )" - f_quietly chmod "${mode:-0644}" "$tmpfile" - f_quietly chown "${owner:-root:wheel}" "$tmpfile" - - # - # Operate on resolv.conf(5), replacing only the last - # occurrences of `domain' and `search' directives (or add - # them to the top if not found), in strict-adherence to the - # following entry in resolver(5): - # - # The domain and search keywords are mutually exclusive. - # If more than one instance of these keywords is present, - # the last instance will override. - # - # NOTE: If RESOLVER_SEARCH_DOMAINS_ALL is set to `1' in the - # environment, all sub-domains will be added to the `search' - # directive, not just the FQDN. - # - local domain="${hostname#*.}" new_contents - [ "$domain" = "$hostname" ] && domain= - new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null ) - new_contents=$( echo "$new_contents" | awk \ - -v domain="$domain" \ - -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \ - -v search_ndots="${RESOLVER_SEARCH_NDOTS:-1}" \ - "$f_dialog_resolv_conf_update_awk" ) - - # - # Write the temporary file contents and move the temporary - # file into place. - # - echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE - f_quietly mv "$tmpfile" "$RESOLV_CONF" - - fi -} - -# f_dialog_input_nameserver [ $n $nameserver ] -# -# Allows the user to edit a given nameserver. The first argument is the -# resolv.conf(5) nameserver ``instance'' integer. For example, this will be one -# if editing the first nameserver instance, two if editing the second, three if -# the third, ad nauseum. If this argument is zero, null, or missing, the value -# entered by the user (if non-null) will be added to resolv.conf(5) as a new -# `nameserver' entry. The second argument is the IPv4 address of the nameserver -# to be edited -- this will be displayed as the initial value during the edit. -# -# Taint-checking is performed when editing an existing entry (when the second -# argument is one or higher) in that the first argument must match the current -# value of the Nth `nameserver' instance in resolv.conf(5) else an error is -# generated discarding any/all changes. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_dialog_input_nameserver_edit_awk=' -# Variables that should be defined on the invocation line: -# -v nsindex="1+" -# -v old_value="..." -# -v new_value="..." -# -BEGIN { - if ( nsindex < 1 ) exit 1 - found = n = 0 -} -{ - if ( found ) { print; next } - - if ( match(tolower($0), /^[[:space:]]*nameserver[[:space:]]+/)) { - if ( ++n == nsindex ) { - if ( $2 != old_value ) exit 2 - if ( new_value != "" ) printf "%s%s\n", \ - substr($0, 0, RLENGTH), new_value - found = 1 - } - else print - } - else print -} -END { if ( ! found ) exit 3 } -' -f_dialog_input_nameserver() -{ - local index="${1:-0}" old_ns="$2" new_ns - local ns="$old_ns" - - # - # Perform sanity checks - # - f_isinteger "$index" || return $FAILURE - [ $index -ge 0 ] || return $FAILURE - - local msg - if [ $index -gt 0 ]; then - if [ "$USE_XDIALOG" ]; then - msg="$xmsg_please_enter_nameserver_existing" - else - msg="$msg_please_enter_nameserver_existing" - fi - else - msg="$msg_please_enter_nameserver" - fi - - local hline="$hline_num_punc_tab_enter" - local size="$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$ns" \ - "$hline" )" - - # - # Loop until the user provides taint-free input. - # - while :; do - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$ns\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - - local retval=$? - new_ns=$( f_dialog_inputstr ) - - [ $retval -eq $SUCCESS ] || return $retval - - # Take only the first "word" of the user's input - new_ns="${new_ns%%[$IFS]*}" - - # Taint-check the user's input - [ "$new_ns" ] || break - f_dialog_validate_ipaddr "$new_ns" && break - - # Update prompt to allow user to re-edit previous entry - ns="$new_ns" - - done - - # - # Save only if the user changed the nameserver. - # - if [ $index -eq "0" -a "$new_ns" ]; then - f_dialog_info "$msg_saving_nameserver" - printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF" - return $SUCCESS - elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then - if [ "$new_ns" ]; then - msg="$msg_saving_nameserver_existing" - else - msg="$msg_removing_nameserver" - fi - f_dialog_info "$msg" - - # - # Create a new temporary file to write our new resolv.conf(5) - # - local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE - - # - # Quietly fixup permissions and ownership - # - local mode owner - mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null ) - owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null ) - f_quietly chmod "${mode:-0644}" "$tmpfile" - f_quietly chown "${owner:-root:wheel}" "$tmpfile" - - # - # Operate on resolv.conf(5) - # - local new_contents - new_contents=$( awk -v nsindex="$index" \ - -v old_value="$old_ns" \ - -v new_value="$new_ns" \ - "$f_dialog_input_nameserver_edit_awk" \ - "$RESOLV_CONF" ) - - # - # Produce an appropriate error message if necessary. - # - local retval=$? - case $retval in - 1) f_die 1 "$msg_internal_error_nsindex_value" "$nsindex";; - 2) f_dialog_msgbox "$msg_resolv_conf_changed_while_editing" - return $retval;; - 3) f_dialog_msgbox "$msg_resolv_conf_entry_no_longer_exists" - return $retval;; - esac - - # - # Write the temporary file contents and move the temporary - # file into place. - # - echo "$new_contents" > "$tmpfile" || return $FAILURE - f_quietly mv "$tmpfile" "$RESOLV_CONF" - fi -} - -# f_dialog_menu_nameservers -# -# Edit the nameservers in resolv.conf(5). -# -f_dialog_menu_nameservers() -{ - local opt_exit="$msg_return_to_previous_menu" - local opt_add="$msg_add_nameserver" - local hline="$hline_arrows_tab_enter" - local prompt size - - # - # Loop forever until the user has finished configuring nameservers - # - prompt="$msg_dns_configuration" - while :; do - # - # Re/Build list of nameservers - # - local nameservers="$( f_resolv_conf_nameservers )" - local menu_list="$( - index=1 - - echo "'X $msg_exit' '$opt_exit'" - index=$(( $index + 1 )) - - echo "'A $msg_add' '$opt_add'" - index=$(( $index + 1 )) - - for ns in $nameservers; do - [ $index -lt ${#DIALOG_MENU_TAGS} ] || break - tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) - echo "'$tag nameserver' '$ns'" - index=$(( $index + 1 )) - done - )" - - # - # Display configuration-edit menu - # - size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list ) - eval $DIALOG \ - --clear --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$prompt\" $size \ - $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" - - local retval=$? - local tag="$( f_dialog_menutag )" ns="" - - # Return if "Cancel" was chosen (-1) or ESC was pressed (255) - [ $retval -eq $SUCCESS ] || return $retval - - case "$tag" in - "X $msg_exit") break;; - "A $msg_add") - f_dialog_input_nameserver - ;; - *) - n=$( eval f_dialog_menutag2index \"\$tag\" $menu_list ) - ns=$( eval f_dialog_menutag2item \"\$tag\" $menu_list ) - f_dialog_input_nameserver $(( $n - 2 )) "$ns" - ;; - esac - done -} - -fi # ! $_NETWORKING_RESOLV_SUBR diff --git a/usr.sbin/bsdconfig/networking/include/routing.subr b/usr.sbin/bsdconfig/networking/include/routing.subr deleted file mode 100644 index f4c70176198a6..0000000000000 --- a/usr.sbin/bsdconfig/networking/include/routing.subr +++ /dev/null @@ -1,168 +0,0 @@ -if [ ! "$_NETWORKING_ROUTING_SUBR" ]; then _NETWORKING_ROUTING_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/sysrc.subr -f_include $BSDCFG_LIBE/include/dialog.subr - -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/common.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/ipaddr.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_route_get_default -# -# Returns the IP address of the currently active default router. -# -f_route_get_default() -{ - route -n get default 2> /dev/null | awk \ - ' - BEGIN { found = 0 } - ( $1 == "gateway:" ) \ - { - print $2 - found = 1 - exit - } - END { exit ! found } - ' -} - -# f_dialog_input_defaultrouter -# -# Edits the default router. -# -f_dialog_input_defaultrouter() -{ - # - # Get the defaultrouter. When this is not configured, the default is - # "NO", however we don't ever want to present this default to the user - # in the following dialog. If the current value is "NO", then try to - # obtain the value from the running system using route(8). - # - # NOTE: Our `f_route_get_default' function will return NULL if the - # system does not have an active default router set (which is what we - # want). - # - local defaultrouter="$( f_sysrc_get 'defaultrouter:-NO' )" - local defaultrouter_orig="$defaultrouter" # for change-tracking - case "$defaultrouter" in - [Nn][Oo]) - defaultrouter=$( f_route_get_default ) - ;; - esac - - # - # Return with-error when there are NFS-mounts currently active. If the - # default router/gateway is changed while NFS-exported directories are - # mounted, the system will hang. - # - if f_nfs_mounted && ! f_jailed; then - local setting="$( printf "$msg_current_default_router" \ - "$defaultrouter" )" - local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ - "$setting" )" - f_dialog_msgbox "$message" - return $FAILURE - fi - - local msg="$msg_please_enter_default_router" - local hline="$hline_num_punc_tab_enter" - local size="$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$defaultrouter" \ - "$hline" )" - - # - # Loop until the user provides taint-free input. - # - while :; do - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$defaultrouter\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" - - local retval=$? - defaultrouter=$( f_dialog_inputstr ) - - [ "$defaultrouter" ] || return $SUCCESS - [ $retval -eq $SUCCESS ] || return $retval - - # Taint-check the user's input - f_dialog_validate_ipaddr "$defaultrouter" && break - done - - # - # Save only if the user changed the default router/gateway. - # - if [ "$defaultrouter" != "$defaultrouter_orig" ]; then - f_dialog_info "$msg_saving_default_router" - - # Save the default router/gateway - f_sysrc_set defaultrouter "$defaultrouter" - fi - - # - # Only ask to apply setting if the current defaultrouter is different - # than the stored configuration (in rc.conf(5)). - # - if [ "$( f_route_get_default )" != "$defaultrouter" ]; then - f_dialog_clear - f_dialog_yesno "$( - printf "$msg_activate_default_router" \ - "$( f_route_get_default )" "$defaultrouter" - )" - - if [ $? -eq $SUCCESS ]; then - local err - - # Apply the default router/gateway - f_quietly route delete default - err=$( route add default "$defaultrouter" 2>&1 ) - if [ $? -ne $SUCCESS ]; then - f_dialog_msgbox "$err" - return $FAILURE - fi - fi - fi -} - -fi # ! $_NETWORKING_ROUTING_SUBR diff --git a/usr.sbin/bsdconfig/networking/nameservers b/usr.sbin/bsdconfig/networking/nameservers index ae8e812e342e4..13cdf517fb830 100755 --- a/usr.sbin/bsdconfig/networking/nameservers +++ b/usr.sbin/bsdconfig/networking/nameservers @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/networking/resolv.subr -APP_DIR="120.networking" -f_include $BSDCFG_LIBE/$APP_DIR/include/resolv.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/networking/networking b/usr.sbin/bsdconfig/networking/networking index 8b9cc8fab5f7b..bbaf2e5f3183a 100755 --- a/usr.sbin/bsdconfig/networking/networking +++ b/usr.sbin/bsdconfig/networking/networking @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="120.networking" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) @@ -122,7 +122,7 @@ while :; do # Make subsequent uses of this menu faster by not performing # "ifconfig up" (limiting the pain one must endure). See also - # `$BSDCFG_LIBE/$APP_DIR/include/device.subr'. + # `$BSDCFG_SHARE/networking/device.subr'. # export DIALOG_MENU_NETDEV_KICK_INTERFACES= ;; diff --git a/usr.sbin/bsdconfig/networking/share/Makefile b/usr.sbin/bsdconfig/networking/share/Makefile new file mode 100644 index 0000000000000..6bd594c83b63c --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/bsdconfig/networking +FILES= common.subr device.subr hostname.subr ipaddr.subr media.subr \ + netmask.subr resolv.subr routing.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/networking/share/common.subr b/usr.sbin/bsdconfig/networking/share/common.subr new file mode 100644 index 0000000000000..e8970d04def8d --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/common.subr @@ -0,0 +1,65 @@ +if [ ! "$_NETWORKING_COMMON_SUBR" ]; then _NETWORKING_COMMON_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 + +############################################################ FUNCTIONS + +# f_mounted $local_directory +# +# Return success if a filesystem is mounted on a particular directory. +# +f_mounted() +{ + local dir="$1" + [ -d "$dir" ] || return $FAILURE + mount | grep -Eq " on $dir \([^)]+\)$" +} + +# f_jailed +# +# Returns true if the current process is jail(8)ed. +# +f_jailed() +{ + ! f_quietly ps 1 +} + +# f_nfs_mounted +# +# Returns true if there are any NFS mounts currently active, otherwise false. +# +f_nfs_mounted() +{ + [ "$( df -t nfs )" ] +} + +fi # ! $_NETWORKING_COMMON_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr new file mode 100644 index 0000000000000..e74a579863072 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -0,0 +1,489 @@ +if [ ! "$_NETWORKING_DEVICE_SUBR" ]; then _NETWORKING_DEVICE_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/networking/common.subr +f_include $BSDCFG_SHARE/networking/ipaddr.subr +f_include $BSDCFG_SHARE/networking/media.subr +f_include $BSDCFG_SHARE/networking/netmask.subr +f_include $BSDCFG_SHARE/networking/resolv.subr +f_include $BSDCFG_SHARE/networking/routing.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ GLOBALS + +# +# Settings used while interacting with various dialog(1) menus +# +: ${DIALOG_MENU_NETDEV_KICK_INTERFACES=1} +: ${DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK=3} + +############################################################ FUNCTIONS + +# f_device_desc $device_name +# +# Print a description for a device name (eg., `fxp0'). +# +f_device_desc() +{ + local device="$1" d="[1234567890]" desc="" + + # Check variables + [ "$device" ] || return $SUCCESS + + # + # Return sysctl MIB dev.NAME.UNIT.%desc if it exists, + # otherwise fall through to below static list. + # + if f_have sysctl; then + local devname devunit + devname="${device%%$d*}" + devunit="${device#$devname}" + devunit="${devunit%%[a-zA-Z_]*}" + sysctl -n "dev.$devname.$devunit.%desc" 2> /dev/null && + return $SUCCESS + fi + + case "$device" in + # Network devices + ae$d) desc="Attansic/Atheros L2 Fast Ethernet";; + age$d) desc="Attansic/Atheros L1 Gigabit Ethernet";; + alc$d) desc="Atheros AR8131/AR8132 PCIe Ethernet";; + ale$d) desc="Atheros AR8121/AR8113/AR8114 PCIe Ethernet";; + an$d) desc="Aironet 4500/4800 802.11 wireless adapter";; + ath$d) desc="Atheros IEEE 802.11 wireless adapter";; + aue$d) desc="ADMtek USB Ethernet adapter";; + axe$d) desc="ASIX Electronics USB Ethernet adapter";; + bce$d) desc="Broadcom NetXtreme II Gigabit Ethernet card";; + bfe$d) desc="Broadcom BCM440x PCI Ethernet card";; + bge$d) desc="Broadcom BCM570x PCI Gigabit Ethernet card";; + bm$d) desc="Apple BMAC Built-in Ethernet";; + bwn$d) desc="Broadcom BCM43xx IEEE 802.11 wireless adapter";; + cas$d) desc="Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet";; + cc3i$d) desc="SDL HSSI sync serial PCI card";; + cue$d) desc="CATC USB Ethernet adapter";; + cxgb$d) desc="Chelsio T3 10Gb Ethernet card";; + dc$d) desc="DEC/Intel 21143 (and clones) PCI Fast Ethernet card";; + de$d) desc="DEC DE435 PCI NIC or other DC21040-AA based card";; + disc$d) desc="Software discard network interface";; + ed$d) desc="Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA";; + el$d) desc="3Com 3C501 Ethernet card";; + em$d) desc="Intel(R) PRO/1000 Ethernet card";; + en$d) desc="Efficient Networks ATM PCI card";; + ep$d) desc="3Com 3C509 Ethernet card/3C589 PCMCIA";; + et$d) desc="Agere ET1310 based PCI Express Gigabit Ethernet card";; + ex$d) desc="Intel EtherExpress Pro/10 Ethernet card";; + fe$d) desc="Fujitsu MB86960A/MB86965A Ethernet card";; + fpa$d) desc="DEC DEFPA PCI FDDI card";; + fwe$d) desc="FireWire Ethernet emulation";; + fwip$d) desc="IP over FireWire";; + fxp$d) desc="Intel EtherExpress Pro/100B PCI Fast Ethernet card";; + gem$d) desc="Apple GMAC or Sun ERI/GEM Ethernet adapter";; + hme$d) desc="Sun HME (Happy Meal Ethernet) Ethernet adapter";; + ie$d) desc="AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210";; + igb$d) desc="Intel(R) PRO/1000 PCI Express Gigabit Ethernet card";; + ipw$d) desc="Intel PRO/Wireless 2100 IEEE 802.11 adapter";; + iwi$d) desc="Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter";; + iwn$d) desc="Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter";; + ix$d) desc="Intel Etherexpress Ethernet card";; + ixgb$d) desc="Intel(R) PRO/10Gb Ethernet card";; + ixgbe$d) desc="Intel(R) PRO/10Gb Ethernet card";; + jme$d) desc="JMicron JMC250 Gigabit/JMC260 Fast Ethernet";; + kue$d) desc="Kawasaki LSI USB Ethernet adapter";; + le$d) desc="AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter";; + lge$d) desc="Level 1 LXT1001 Gigabit Ethernet card";; + lnc$d) desc="Lance/PCnet (Isolan/Novell NE2100/NE32-VL) Ethernet";; + lp$d) desc="Parallel Port IP (PLIP) peer connection";; + lo$d) desc="Loop-back (local) network interface";; + malo$d) desc="Marvell Libertas 88W8335 802.11 wireless adapter";; + msk$d) desc="Marvell/SysKonnect Yukon II Gigabit Ethernet";; + mxge$d) desc="Myricom Myri10GE 10Gb Ethernet card";; + nfe$d) desc="NVIDIA nForce MCP Ethernet";; + ng${d}_*|ng$d${d}_*|ng$d$d${d}_*|ng$d$d$d${d}_*|ng$d$d$d$d${d}_*) + desc="Vimage netgraph(4) bridged Ethernet device";; + nge$d) desc="NatSemi PCI Gigabit Ethernet card";; + nve$d) desc="NVIDIA nForce MCP Ethernet";; + nxge$d) desc="Neterion Xframe 10GbE Server/Storage adapter";; + pcn$d) desc="AMD Am79c79x PCI Ethernet card";; + plip$d) desc="Parallel Port IP (PLIP) peer connection";; + ral$d) desc="Ralink Technology IEEE 802.11 wireless adapter";; + ray$d) desc="Raytheon Raylink 802.11 wireless adapter";; + re$d) desc="RealTek 8139C+/8169/8169S/8110S PCI Ethernet adapter";; + rl$d) desc="RealTek 8129/8139 PCI Ethernet card";; + rue$d) desc="RealTek USB Ethernet card";; + rum$d) desc="Ralink Technology USB IEEE 802.11 wireless adapter";; + sf$d) desc="Adaptec AIC-6915 PCI Ethernet card";; + sge$d) desc="Silicon Integrated Systems SiS190/191 Ethernet";; + sis$d) desc="SiS 900/SiS 7016 PCI Ethernet card";; + sk$d) desc="SysKonnect PCI Gigabit Ethernet card";; + sn$d) desc="SMC/Megahertz Ethernet card";; + snc$d) desc="SONIC Ethernet card";; + sr$d) desc="SDL T1/E1 sync serial PCI card";; + ste$d) desc="Sundance ST201 PCI Ethernet card";; + stge$d) desc="Sundance/Tamarack TC9021 Gigabit Ethernet";; + ti$d) desc="Alteon Networks PCI Gigabit Ethernet card";; + tl$d) desc="Texas Instruments ThunderLAN PCI Ethernet card";; + tx$d) desc="SMC 9432TX Ethernet card";; + txp$d) desc="3Com 3cR990 Ethernet card";; + uath$d) desc="Atheros AR5005UG and AR5005UX USB wireless adapter";; + upgt$d) desc="Conexant/Intersil PrismGT USB wireless adapter";; + ural$d) desc="Ralink Technology RT2500USB 802.11 wireless adapter";; + urtw$d) desc="Realtek 8187L USB wireless adapter";; + vge$d) desc="VIA VT612x PCI Gigabit Ethernet card";; + vlan$d|vlan$d$d|vlan$d$d$d|vlan$d$d$d$d|vlan$d$d$d$d$d) + desc="IEEE 802.1Q VLAN network interface";; + vr$d) desc="VIA VT3043/VT86C100A Rhine PCI Ethernet card";; + vx$d) desc="3COM 3c590 / 3c595 Ethernet card";; + wb$d) desc="Winbond W89C840F PCI Ethernet card";; + wi$d) desc="Lucent WaveLAN/IEEE 802.11 wireless adapter";; + wpi$d) desc="Intel 3945ABG IEEE 802.11 wireless adapter";; + wx$d) desc="Intel Gigabit Ethernet (82452) card";; + xe$d) desc="Xircom/Intel EtherExpress Pro100/16 Ethernet card";; + xl$d) desc="3COM 3c90x / 3c90xB PCI Ethernet card";; + zyd$d) desc="ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter";; + # Unknown device + *) desc="";; + esac + printf "%s\n" "$desc" +} + +# f_dialog_menu_netdev +# +# Display a list of network devices with descriptions. +# +f_dialog_menu_netdev() +{ + # + # Display a message to let the user know we're working... + # (message will remain until we throw up the next dialog) + # + f_dialog_info "$msg_probing_network_interfaces" + + # + # Get list of usable network interfaces + # + local d='[[:digit:]]+:' + local iflist="`echo "$(ifconfig -l):" | sed -E -e " + # Convert all spaces to colons + y/ /:/ + + # Prune unsavory interfaces + s/lo$d//g + s/ppp$d//g + s/sl$d//g + s/lp$d//g + s/fwe$d//g + s/faith$d//g + s/plip$d//g + + # Convert all colons back into spaces + y/:/ / + "`" + + # + # Optionally kick interfaces in the head to get them to accurately + # track the carrier status in realtime (required on FreeBSD). + # + if [ "$DIALOG_MENU_NETDEV_KICK_INTERFACES" ]; then + DIALOG_MENU_NETDEV_KICK_INTERFACES= + + local ifn + for ifn in $iflist; do + f_quietly ifconfig $ifn up + done + + if [ "$DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK" ]; then + # interfaces need time to update carrier status + sleep $DIALOG_MENU_NETDEV_SLEEP_AFTER_KICK + fi + fi + + # + # Mark any "active" interfaces with an asterisk (*) + # to the right of the device name. + # + interfaces=$( + for ifn in $iflist; do + active=$( ifconfig $ifn | awk \ + ' + ( $1 == "status:" ) \ + { + if ( $2 == "active" ) { print 1; exit } + } + ' ) + printf "'%s%s' '%s'\n" \ + "$ifn" "${active:+*}" "$(f_device_desc "$ifn")" + done + ) + if [ ! "$interfaces" ]; then + f_dialog_msgbox "$msg_no_network_interfaces" + return $FAILURE + fi + + local hline="$hline_arrows_tab_enter" + + # + # Ask user to select an interface + # + local prompt size + prompt="$msg_select_network_interface" + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $interfaces ) + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $interfaces \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_menu_netdev_edit $interface $ipaddr $netmask $options $dhcp +# +# Allow a user to edit network interface settings. Current values are not +# probed but rather taken from the positional arguments. +# +f_dialog_menu_netdev_edit() +{ + local interface="$1" ipaddr="$2" netmask="$3" options="$4" dhcp="$5" + local prompt menu_list size + + # + # Create a duplicate set of variables for change-tracking... + # + local ipaddr_orig="$2" \ + netmask_orig="$3" \ + options_orig="$4" \ + dhcp_orig="$5" + + local hline="$hline_arrows_tab_enter" + prompt=$( printf "$msg_network_configuration" "$interface" ) + + # + # Loop forever until the user has finished configuring the different + # components of the network interface. + # + # To apply the settings, we need to know each of the following: + # - IP Address + # - Network subnet mask + # - Additional ifconfig(8) options + # + # It is only when we have all of the above values that we can make the + # changes effective because all three options must be specified at-once + # to ifconfig(8). + # + while :; do + local dhcp_status="$msg_disabled" + [ "$dhcp" ] && dhcp_status="$msg_enabled" + + # + # Display configuration-edit menu + # + menu_list=" + 'X $msg_save_exit' '$msg_return_to_previous_menu' + '2 $msg_dhcp' '$dhcp_status' + '3 $msg_ipaddr4' '$ipaddr' + '4 $msg_netmask' '$netmask' + '5 $msg_options' '$options' + " + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + local tag="$( f_dialog_menutag )" + + # Return if "Cancel" was chosen (-1) or ESC was pressed (255) + [ $retval -eq $SUCCESS ] || return $retval + + # + # Call the below ``modifier functions'' whose job it is to take + # input from the user and assign the newly-acquired values back + # to the ipaddr, netmask, and options variables for us to re- + # read and display in the summary dialog. + # + case "$tag" in + X\ *) break;; + 2\ *) # + # Do not proceed if/when there are NFS-mounts currently + # active. If the network is changed while NFS-exported + # directories are mounted, the system may hang (if any + # NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_dhcp_status" \ + "$interface" "$dhcp_status" )" + local message="$( + printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" + )" + f_dialog_msgbox "$message" + continue + fi + + # + # Toggle DHCP status + # + if [ "$dhcp_status" = "$msg_enabled" ]; then + dhcp= + else + trap - SIGINT + ( # Execute within sub-shell to allow/catch Ctrl-C + trap 'exit $FAILURE' SIGINT + msg=$( printf "$msg_scanning_for_dhcp" "$interface" ) + if [ "$USE_XDIALOG" ]; then + ( + f_quietly ifconfig $interface delete + f_quietly dhclient $interface + ) | + f_xdialog_info "$msg" + else + f_dialog_info "$msg" + f_quietly ifconfig $interface delete + f_quietly dhclient $interface + fi + ) + retval=$? + trap 'interrupt' SIGINT + if [ $retval -eq $SUCCESS ]; then + dhcp=1 + ipaddr=$( f_ifconfig_inet $interface ) + netmask=$( f_ifconfig_netmask $interface ) + options= + + # Fixup search/domain in resolv.conf(5) + hostname=$( f_sysrc_get \ + 'hostname:-$(hostname)' ) + f_dialog_resolv_conf_update "$hostname" + fi + fi + ;; + 3\ *) f_dialog_input_ipaddr "$interface" "$ipaddr" + [ $? -eq $SUCCESS ] && dhcp=;; + 4\ *) f_dialog_input_netmask "$interface" "$netmask" + [ $? -eq $SUCCESS -a "$_netmask" ] && dhcp=;; + 5\ *) f_dialog_menu_media_options "$interface" "$options" + [ $? -eq $SUCCESS ] && dhcp=;; + esac + done + + # + # Save only if the user changed at least one feature of the interface + # + if [ "$ipaddr" != "$ipaddr_orig" -o \ + "$netmask" != "$netmask_orig" -o \ + "$options" != "$options_orig" -o \ + "$dhcp" != "$dhcp_orig" ] + then + f_dialog_info "$( printf "$msg_saving_network_interface" \ + "$interface" )" + + local value= + if [ "$dhcp" ]; then + f_sysrc_delete defaultrouter + value=DHCP + else + value="inet $ipaddr netmask $netmask" + value="$value${options:+ }$options" + fi + + f_sysrc_set ifconfig_$interface "$value" + fi + + # + # Re/Apply the settings if desired + # + if [ ! "$dhcp" ]; then + f_dialog_yesno "Would you like to bring the $interface" \ + "interface up right now?" + if [ $? -eq $SUCCESS ]; then + f_dialog_info "$( printf "$msg_bring_interface_up" \ + "$interface" )" + + local dr="$( f_sysrc_get defaultrouter )" err + if [ "$dr" = "NO" -o ! "$dr" ]; then + dr=$( f_route_get_default ) + [ "$dr" ] && f_sysrc_set defaultrouter "$dr" + fi + # + # Make a backup of resolv.conf(5) before using + # ifconfig(8) and then restore it afterward. This + # allows preservation of nameservers acquired via + # DHCP on FreeBSD-8.x (normally lost as ifconfig(8) + # usage causes dhclient(8) to exit which scrubs + # resolv.conf(5) by-default upon termination). + # + f_quietly cp -fp "$RESOLV_CONF" "$RESOLV_CONF.$$" + err=$( ifconfig $interface inet $ipaddr \ + netmask $netmask $options 2>&1 ) + if [ $? -eq $SUCCESS ]; then + if [ "$dr" -a "$dr" != "NO" ]; then + err=$( route add default "$dr" 2>&1 ) + [ $? -eq $SUCCESS ] || \ + dialog_msgbox "$err" + fi + else + dialog_msgbox "$err" + fi + if cmp -s "$RESOLV_CONF" "$RESOLV_CONF.$$"; then + f_quietly rm -f "$RESOLV_CONF.$$" + else + f_quietly mv -f "$RESOLV_CONF.$$" "$RESOLV_CONF" + fi + fi + fi + + return $SUCCESS +} + +fi # ! $_NETWORKING_DEVICE_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/hostname.subr b/usr.sbin/bsdconfig/networking/share/hostname.subr new file mode 100644 index 0000000000000..54264439149c1 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/hostname.subr @@ -0,0 +1,211 @@ +if [ ! "$_NETWORKING_HOSTNAME_SUBR" ]; then _NETWORKING_HOSTNAME_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/networking/common.subr +f_include $BSDCFG_SHARE/networking/resolv.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_validate_hostname $hostname +# +# Returns zero if the given argument (a fully-qualified hostname) is compliant +# with standards set-forth in RFC's 952 and 1123 of the Network Working Group: +# +# RFC 952 - DoD Internet host table specification +# http://tools.ietf.org/html/rfc952 +# +# RFC 1123 - Requirements for Internet Hosts - Application and Support +# http://tools.ietf.org/html/rfc1123 +# +# See http://en.wikipedia.org/wiki/Hostname for a brief overview. +# +# The return status for invalid hostnames is one of: +# 255 Entire hostname exceeds the maximum length of 255 characters. +# 63 One or more individual labels within the hostname (separated by +# dots) exceeds the maximum of 63 characters. +# 1 One or more individual labels within the hostname contains one +# or more invalid characters. +# 2 One or more individual labels within the hostname starts or +# ends with a hyphen (hyphens are allowed, but a label cannot +# begin or end with a hyphen). +# 3 One or more individual labels within the hostname are null. +# +# If the hostname is determined to be invalid, the appropriate error will be +# displayed using the f_dialog_msgbox function. +# +f_dialog_validate_hostname() +{ + local fqhn="$1" + + ( # Operate within a sub-shell to protect the parent environment + + # Return error if the hostname exceeds 255 characters + [ ${#fqhn} -gt 255 ] && exit 255 + + IFS="." # Split on `dot' + for label in $fqhn; do + + # Return error if the label exceeds 63 characters + [ ${#label} -gt 63 ] && exit 63 + + # Return error if the label is null + [ "$label" ] || exit 3 + + # Return error if label begins/ends with dash + case "$label" in + -*|*-) exit 2 + esac + + # Return error if the label contains any invalid chars + echo "$label" | grep -q '^[[:alnum:]-]*$' || exit 1 + + done + ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_contains_invalid_chars" "$fqhn" )";; + 2) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_starts_or_ends_with_hyphen" "$fqhn" )";; + 3) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_is_null" "$fqhn" )";; + 63) f_dialog_msgbox "$( printf \ + "$msg_hostname_label_exceeds_max_length" "$fqhn" )";; + 255) f_dialog_msgbox "$( printf \ + "$msg_hostname_exceeds_max_length" "$fqhn" )";; + esac + + return $retval +} + +# f_dialog_input_hostname +# +# Edits the current hostname. +# +f_dialog_input_hostname() +{ + local hostname="$( f_sysrc_get 'hostname:-$(hostname)' )" + local hostname_orig="$hostname" # for change-tracking + + local msg + if [ "$USE_XDIALOG" ]; then + msg="$xmsg_please_enter_fqhn" + else + msg="$msg_please_enter_fqhn" + fi + + local hline="$hline_alnum_punc_tab_enter" + + # + # Loop until the user provides taint-free input. + # + local size height width + while :; do + + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hostname" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$hostname\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + hostname=$( f_dialog_inputstr ) + + [ $retval -eq $SUCCESS ] || return $retval + + # Taint-check the user's input + f_dialog_validate_hostname "$hostname" && break + + done + + # + # Save hostname only if the user changed the hostname. + # + if [ "$hostname" != "$hostname_orig" ]; then + f_dialog_info "$msg_saving_hostname" + f_sysrc_set hostname "$hostname" + fi + + # + # Update resolv.conf(5) search/domain directives + # + f_dialog_resolv_conf_update "$hostname" + + # + # Only ask to apply setting if the current hostname is different than + # the stored configuration (in rc.conf(5)). + # + if [ "$( hostname )" != "$( f_sysrc_get hostname )" ]; then + [ ! "$USE_XDIALOG" ] && dialog_clear + + # + # If connected via ssh(1) and performing X11-Forwarding, don't + # allow the hostname to be changed to prevent the fatal error + # "X11 connection rejected because of wrong authentication." + # + if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" ]; then + f_dialog_msgbox "$( + printf "$msg_activate_hostname_x11warning" \ + "$( hostname )" "$hostname" + )" + else + f_dialog_yesno "$( + printf "$msg_activate_hostname" \ + "$( hostname )" "$hostname" \ + )" \ + && hostname "$hostname" + fi + fi + + return $SUCCESS +} + +fi # ! $_NETWORKING_HOSTNAME_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/ipaddr.subr b/usr.sbin/bsdconfig/networking/share/ipaddr.subr new file mode 100644 index 0000000000000..06571ca0ae2bc --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/ipaddr.subr @@ -0,0 +1,236 @@ +if [ ! "$_NETWORKING_IPADDR_SUBR" ]; then _NETWORKING_IPADDR_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/networking/common.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_ifconfig_inet $interface +# +# Returns the IPv4 address associated with $interface. +# +f_ifconfig_inet() +{ + local interface="$1" + ifconfig "$interface" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( $1 == "inet" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_dialog_validate_ipaddr $ipaddr +# +# Returns zero if the given argument (an IP address) is of the proper format. +# +# The return status for invalid IP address is one of: +# 1 One or more individual octets within the IP address (separated +# by dots) contains one or more invalid characters. +# 2 One or more individual octets within the IP address are null +# and/or missing. +# 3 One or more individual octets within the IP address exceeds the +# maximum of 255 (or 2^8, being an octet comprised of 8 bits). +# 4 The IP address has either too few or too many octets. +# +# If the IP address is determined to be invalid, the appropriate error will be +# displayed using the above dialog_msgbox function. +# +f_dialog_validate_ipaddr() +{ + local ip="$1" + + ( # Operate within a sub-shell to protect the parent environment + + # Track number of octets for error checking + noctets=0 + + IFS="." # Split on `dot' + for octet in $ip; do + + # Return error if the octet is null + [ "$octet" ] || exit 2 + + # Return error if not a whole integer + f_isinteger "$octet" || exit 1 + + # Return error if not a positive integer + [ $octet -ge 0 ] || exit 1 + + # Return error if the octet exceeds 255 + [ $octet -gt 255 ] && exit 3 + + noctets=$(( $noctets + 1 )) + + done + + [ $noctets -eq 4 ] || exit 4 + ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip" )";; + 2) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_is_null" "$ip" )";; + 3) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_exceeds_max_value" "$ip" )";; + 4) f_dialog_msgbox "$( printf \ + "$msg_ipv4_addr_octet_missing_or_extra" "$ip" )";; + esac + + return $retval +} + +# f_dialog_input_ipaddr $interface $ipaddr +# +# Allows the user to edit a given IP address. If the user does not cancel or +# press ESC, the $ipaddr environment variable will hold the newly-configured +# value upon return. +# +# Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the +# netmask at the same time as the IP address. If such a format is entered by +# the user, the $netmask environment variable will hold the newly-configured +# netmask upon return. +# +f_dialog_input_ipaddr() +{ + local interface="$1" _ipaddr="$2" _input + + # + # Return with-error when there are NFS-mounts currently active. If the + # IP address is changed while NFS-exported directories are mounted, the + # system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_ipaddr" \ + "$interface" "$_ipaddr" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_ipaddr" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_ipaddr\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + _input=$( f_dialog_inputstr ) + + # + # Return error status if: + # - User has not made any changes to the given value + # - User has either pressed ESC or chosen Cancel/No + # + [ "$_ipaddr" = "$_input" ] && return $FAILURE + [ $retval -eq $SUCCESS ] || return $retval + + # Return success if NULL value was entered + [ "$_input" ] || return $SUCCESS + + # Take only the first "word" of the user's input + _ipaddr="$_input" + _ipaddr="${_ipaddr%%[$IFS]*}" + + # Taint-check the user's input + f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break + done + + # + # Support the syntax: IP_ADDRESS/NBITS + # + local _netmask="" + case "$_ipaddr" in + */*) + local nbits="${_ipaddr#*/}" n=0 + _ipaddr="${_ipaddr%%/*}" + + # + # Taint-check $nbits to be (a) a positive whole-integer, + # and (b) to be less than or equal to 32. Otherwise, set + # $n so that the below loop never executes. + # + ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \ + || n=4 + + while [ $n -lt 4 ]; do + _netmask="$_netmask${_netmask:+.}$(( + (65280 >> ($nbits - 8 * $n) & 255) + * ((8*$n) < $nbits & $nbits <= (8*($n+1))) + + 255 * ($nbits > (8*($n+1))) + ))" + n=$(( $n + 1 )) + done + ;; + esac + + ipaddr="$_ipaddr" + [ "$_netmask" ] && netmask="$_netmask" + + return $SUCCESS +} + +fi # ! $_NETWORKING_IPADDR_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/media.subr b/usr.sbin/bsdconfig/networking/share/media.subr new file mode 100644 index 0000000000000..c546220365a28 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/media.subr @@ -0,0 +1,235 @@ +if [ ! "$_NETWORKING_MEDIA_SUBR" ]; then _NETWORKING_MEDIA_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/networking/common.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_ifconfig_options $interface +# +# Returns any/all extra ifconfig(8) parameters associated with $interface. +# +f_ifconfig_options() +{ + local interface="$1" + [ "$interface" ] || return $SUCCESS + + # + # Loop over the options, removing what we don't want + # + ( + set -- $( f_sysrc_get ifconfig_$interface ) + + # + # Return if the interface is configured for DHCP + # + glob="[Dd][Hh][Cc][Pp]" + case "$*" in + $glob|[Ss][Yy][Nn][Cc]$glob|[Nn][Oo][Ss][Yy][Nn][Cc]$glob) + exit $SUCCESS + esac + + output= + while [ $# -gt 0 ]; do + case "$1" in + inet|netmask) shift 1;; + *) output="$output${output:+ }$1" + esac + shift 1 + done + echo "$output" + ) +} + +# f_ifconfig_media $interface +# +# Returns list of supported media for $interface. +# +f_ifconfig_media() +{ + local interface="$1" + ifconfig -m "$interface" 2> /dev/null | awk \ + ' + BEGIN { media_found = 0 } + { + if ( media_found == 1 ) { print; next } + } + ( $1 $2 == "supported" "media:" ) \ + { + media_found = 1 + next + } + END { exit ! media_found } + ' +} + +# f_dialog_input_options $interface +# +# Input custom interface options. +# +f_dialog_input_options() +{ + local interface="$1" + + # + # Return with-error when there are NFS-mounts currently active. If the + # options are changed while NFS-exported directories are mounted, + # the system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_options" \ + "$interface" "$options" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$( printf "$msg_please_enter_mediaopts" "$interface" )" + local hline="$hline_alnum_punc_tab_enter" + + $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --inputbox "$msg" 9 70 \ + "$options" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + local _options="$( f_dialog_inputstr )" + + [ $retval -eq $SUCCESS ] && options="$_options" + + return $retval +} + +# f_dialog_menu_media_options $interface +# +# Display a menu of additional media options for the given network interface. +# +f_dialog_menu_media_options() +{ + local interface="$1" _options="$2" + # + # Not all network interfaces support additional media options, but + # when available we should prompt the user to select from a list + # of available options (or none, as is the first/default option). + # + + # + # Return with-error when there are NFS-mounts currently active. If the + # media options are changed while NFS-exported directories are mounted, + # the system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_options" \ + "$interface" "$_options" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + # + # Build list of additional media options + # + local opt_none="$msg_no_options" + local opt_cust="$msg_custom" + local supported_media="$( + f_ifconfig_media $interface | \ + ( index=1 + + echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" + echo "'$opt_none'" + index=$(( $index + 1 )) + + echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" + echo "'$opt_cust'" + index=$(( $index + 1 )) + + while read media_options; do + [ $index -lt ${#DIALOG_MENU_TAGS} ] || break + echo "'$( f_substr "$DIALOG_MENU_TAGS" $index 1 )'" + echo "'$media_options'" + index=$(( $index + 1 )) + done + ) + )" + + local msg + if [ "$USE_XDIALOG" ]; then + msg=$( printf "$xmsg_supported_media_options" \ + "$interface" "$interface" ) + else + msg=$( printf "$msg_supported_media_options" \ + "$interface" "$interface" ) + fi + + local hline="$hline_arrows_tab_enter" + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" 21 60 12 \ + $supported_media \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + if [ $retval -eq $SUCCESS ]; then + local tag="$( f_dialog_menutag )" + options=$( eval f_dialog_menutag2item \"\$tag\" \ + $supported_media ) + [ "$options" = "$opt_none" ] && options= + + if [ "$options" = "$opt_cust" ]; then + options="$_options" + f_dialog_input_options "$interface" + retval=$? + fi + fi + + return $retval +} + +fi # ! $_NETWORKING_MEDIA_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/netmask.subr b/usr.sbin/bsdconfig/networking/share/netmask.subr new file mode 100644 index 0000000000000..4fa5be30e9719 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/netmask.subr @@ -0,0 +1,215 @@ +if [ ! "$_NETWORKING_NETMASK_SUBR" ]; then _NETWORKING_NETMASK_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/networking/common.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_ifconfig_netmask $interface +# +# Returns the IPv4 subnet mask associated with $interface. +# +f_ifconfig_netmask() +{ + local interface="$1" octets + octets=$( ifconfig "$interface" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( $1 == "inet" ) \ + { + printf "%s %s %s %s\n", + substr($4,3,2), + substr($4,5,2), + substr($4,7,2), + substr($4,9,2) + found = 1 + exit + } + END { exit ! found } + ' ) || return $FAILURE + + local octet netmask= + for octet in $octets; do + netmask="$netmask${netmask:+.}$( printf "%u" "0x$octet" )" + done + echo $netmask +} + +# f_dialog_validate_netmask $netmask +# +# Returns zero if the given argument (a subnet mask) is of the proper format. +# +# The return status for invalid IP address is one of: +# 1 One or more individual fields within the subnet mask (separated +# by dots) contains one or more invalid characters. +# 2 One or more individual fields within the subnet mask are null +# and/or missing. +# 3 One or more individual fields within the subnet mask exceeds +# the maximum of 255 (a full 8-bit register). +# 4 The subnet mask has either too few or too many fields. +# 5 One or more individual fields within the subnet mask is an +# invalid integer (only 0,128,192,224,240,248,252,254,255 are +# valid integers). +# +# If the subnet mask is determined to be invalid, the appropriate error will be +# displayed using the f_dialog_msgbox function. +# +f_dialog_validate_netmask() +{ + local mask="$1" + + ( # Operate within a sub-shell to protect the parent environment + + # Track number of fields for error checking + nfields=0 + + IFS="." # Split on `dot' + for field in $mask; do + + # Return error if the field is null + [ "$field" ] || exit 2 + + # Return error if not a whole positive integer + f_isinteger "$field" || exit 1 + + # Return error if the field exceeds 255 + [ $field -gt 255 ] && exit 3 + + # Return error if the field is an invalid integer + case "$field" in + 0|128|192|224|240|248|252|254|255) :;; + *) exit 5;; + esac + + nfields=$(( $nfields + 1 )) + + done + + [ $nfields -eq 4 ] || exit 4 + ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_contains_invalid_chars" "$mask" )";; + 2) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_is_null" "$mask" )";; + 3) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_exceeds_max_value" "$mask" )";; + 4) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_missing_or_extra" "$mask" )";; + 5) f_dialog_msgbox "$( printf \ + "$msg_ipv4_mask_field_invalid_value" "$mask" )";; + esac + + return $retval +} + +# f_dialog_input_netmask $interface $netmask +# +# Edits the IP netmask of the given interface. +# +f_dialog_input_netmask() +{ + local interface="$1" _netmask="$2" _input + + # + # Return with-error when there are NFS-mounts currently active. If the + # subnet mask is changed while NFS-exported directories are mounted, + # the system may hang (if any NFS mounts are using that interface). + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_subnet" \ + "$interface" "$_netmask" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$( printf "$msg_please_enter_subnet_mask" "$interface" )" + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_netmask" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_netmask\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + _input=$( f_dialog_inputstr ) + + # + # Return error status if: + # - User has not made any changes to the given value + # - User has either pressed ESC or chosen Cancel/No + # + [ "$_netmask" = "$_input" ] && return $FAILURE + [ $retval -eq $SUCCESS ] || return $retval + + # Return success if NULL value was entered + [ "$_input" ] || return $SUCCESS + + # Take only the first "word" of the user's input + _netmask="$_input" + _netmask="${_netmask%%[$IFS]*}" + + # Taint-check the user's input + f_dialog_validate_netmask "$_netmask" && break + done + + netmask="$_netmask" +} + +fi # ! $_NETWORKING_NETMASK_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/resolv.subr b/usr.sbin/bsdconfig/networking/share/resolv.subr new file mode 100644 index 0000000000000..b635cc1e20625 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/resolv.subr @@ -0,0 +1,509 @@ +if [ ! "$_NETWORKING_RESOLV_SUBR" ]; then _NETWORKING_RESOLV_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/networking/common.subr +f_include $BSDCFG_SHARE/networking/ipaddr.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Path to resolv.conf(5). +# +: ${RESOLV_CONF:="/etc/resolv.conf"} + +# +# When updating resolv.conf(5), should we populate the `search' directive with +# all possible sub-domains? In example, if the domain is "sub.domain.com", when +# the below option is set to 1, include both "sub.domain.com" and "domain.com" +# in the `search' directive, otherwise use only "sub.domain.com". +# +# When enabled (set to 1), specify the minimum number of dots required for each +# `search' domain by setting the second option below, `RESOLVER_SEARCH_NDOTS'. +# +: ${RESOLVER_SEARCH_DOMAINS_ALL:=1} +: ${RESOLVER_SEARCH_NDOTS:=1} + +############################################################ FUNCTIONS + +# f_resolv_conf_domain +# +# Returns the domain configured in resolv.conf(5). +# +f_resolv_conf_domain() +{ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( tolower($1) == "domain" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_resolv_conf_search +# +# Returns the search configured in resolv.conf(5). +# +f_resolv_conf_search() +{ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + { + tl0 = tolower($0) + if ( match(tl0, /^[[:space:]]*search[[:space:]]+/) ) { + search = substr($0, RLENGTH + 1) + sub(/[[:space:]]*#.*$/, "", search) + gsub(/[[:space:]]+/, " ", search) + print search + found = 1 + exit + } + } + END { exit ! found } + ' +} + +# f_resolv_conf_nameservers +# +# Returns nameserver(s) configured in resolv.conf(5). +# +f_resolv_conf_nameservers() +{ + awk \ + ' + BEGIN { found = 0 } + ( $1 == "nameserver" ) \ + { + print $2 + found = 1 + } + END { exit ! found } + ' \ + "$RESOLV_CONF" 2> /dev/null +} + +# f_dialog_resolv_conf_update $hostname +# +# Updates the search/domain directives in resolv.conf(5) given a valid fully- +# qualified hostname. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_dialog_resolv_conf_update_awk=' +# Variables that should be defined on the invocation line: +# -v domain="domain" +# -v search_all="0|1" +# -v search_ndots="1+" +# +BEGIN { + domain_found = search_found = 0 + + if ( search_all ) { + search = "" + subdomain = domain + if ( search_ndots < 1 ) + search_ndots = 1 + + ndots = split(subdomain, labels, ".") - 1 + while ( ndots-- >= search_ndots ) { + if ( length(search) ) search = search " " + search = search subdomain + sub(/[^.]*\./, "", subdomain) + } + } + else search = domain +} +{ + if ( domain_found && search_found ) { print; next } + + tl0 = tolower($0) + if ( ! domain_found && \ + match(tl0, /^[[:space:]]*domain[[:space:]]+/) ) \ + { + if ( length(domain) ) { + printf "%s%s\n", substr($0, 0, RLENGTH), domain + domain_found = 1 + } + } + else if ( ! search_found && \ + match(tl0, /^[[:space:]]*search[[:space:]]+/) ) \ + { + if ( length(search) ) { + printf "%s%s\n", substr($0, 0, RLENGTH), search + search_found = 1 + } + } + else print +} +END { + if ( ! search_found && length(search) ) + printf "search\t%s\n", search + if ( ! domain_found && length(domain) ) + printf "domain\t%s\n", domain +} +' +f_dialog_resolv_conf_update() +{ + local hostname="$1" + + # + # Extrapolate the desired domain search parameter for resolv.conf(5) + # + local search ndots domain="${hostname#*.}" + if [ "$RESOLVER_SEARCH_DOMAINS_ALL" = "1" ]; then + search="" + ndots=$( IFS=.; set -- $domain; echo $(( $# - 1 )) ) + while [ $ndots -ge ${RESOLVER_SEARCH_NDOTS:-1} ]; do + search="$search${search:+ }$domain" + domain="${domain#*.}" + ndots=$(( $ndots - 1 )) + done + domain="${hostname#*.}" + else + search="$domain" + fi + + # + # Save domain/search information only if different from resolv.conf(5) + # + if [ "$domain" != "$( f_resolv_conf_domain )" -o \ + "$search" != "$( f_resolv_conf_search )" ] + then + f_dialog_info "Saving new domain/search settings" \ + "to resolv.conf(5)..." + + # + # Create a new temporary file to write our resolv.conf(5) + # update with our new `domain' and `search' directives. + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions and ownership (mktemp(1) creates the + # temporary file with 0600 permissions -- change the + # permissions and ownership to match resolv.conf(5) before + # we write it out and mv(1) it into place). + # + local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null )" + local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null )" + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on resolv.conf(5), replacing only the last + # occurrences of `domain' and `search' directives (or add + # them to the top if not found), in strict-adherence to the + # following entry in resolver(5): + # + # The domain and search keywords are mutually exclusive. + # If more than one instance of these keywords is present, + # the last instance will override. + # + # NOTE: If RESOLVER_SEARCH_DOMAINS_ALL is set to `1' in the + # environment, all sub-domains will be added to the `search' + # directive, not just the FQDN. + # + local domain="${hostname#*.}" new_contents + [ "$domain" = "$hostname" ] && domain= + new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null ) + new_contents=$( echo "$new_contents" | awk \ + -v domain="$domain" \ + -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \ + -v search_ndots="${RESOLVER_SEARCH_NDOTS:-1}" \ + "$f_dialog_resolv_conf_update_awk" ) + + # + # Write the temporary file contents and move the temporary + # file into place. + # + echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE + f_quietly mv "$tmpfile" "$RESOLV_CONF" + + fi +} + +# f_dialog_input_nameserver [ $n $nameserver ] +# +# Allows the user to edit a given nameserver. The first argument is the +# resolv.conf(5) nameserver ``instance'' integer. For example, this will be one +# if editing the first nameserver instance, two if editing the second, three if +# the third, ad nauseum. If this argument is zero, null, or missing, the value +# entered by the user (if non-null) will be added to resolv.conf(5) as a new +# `nameserver' entry. The second argument is the IPv4 address of the nameserver +# to be edited -- this will be displayed as the initial value during the edit. +# +# Taint-checking is performed when editing an existing entry (when the second +# argument is one or higher) in that the first argument must match the current +# value of the Nth `nameserver' instance in resolv.conf(5) else an error is +# generated discarding any/all changes. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_dialog_input_nameserver_edit_awk=' +# Variables that should be defined on the invocation line: +# -v nsindex="1+" +# -v old_value="..." +# -v new_value="..." +# +BEGIN { + if ( nsindex < 1 ) exit 1 + found = n = 0 +} +{ + if ( found ) { print; next } + + if ( match(tolower($0), /^[[:space:]]*nameserver[[:space:]]+/)) { + if ( ++n == nsindex ) { + if ( $2 != old_value ) exit 2 + if ( new_value != "" ) printf "%s%s\n", \ + substr($0, 0, RLENGTH), new_value + found = 1 + } + else print + } + else print +} +END { if ( ! found ) exit 3 } +' +f_dialog_input_nameserver() +{ + local index="${1:-0}" old_ns="$2" new_ns + local ns="$old_ns" + + # + # Perform sanity checks + # + f_isinteger "$index" || return $FAILURE + [ $index -ge 0 ] || return $FAILURE + + local msg + if [ $index -gt 0 ]; then + if [ "$USE_XDIALOG" ]; then + msg="$xmsg_please_enter_nameserver_existing" + else + msg="$msg_please_enter_nameserver_existing" + fi + else + msg="$msg_please_enter_nameserver" + fi + + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$ns" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$ns\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + new_ns=$( f_dialog_inputstr ) + + [ $retval -eq $SUCCESS ] || return $retval + + # Take only the first "word" of the user's input + new_ns="${new_ns%%[$IFS]*}" + + # Taint-check the user's input + [ "$new_ns" ] || break + f_dialog_validate_ipaddr "$new_ns" && break + + # Update prompt to allow user to re-edit previous entry + ns="$new_ns" + + done + + # + # Save only if the user changed the nameserver. + # + if [ $index -eq "0" -a "$new_ns" ]; then + f_dialog_info "$msg_saving_nameserver" + printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF" + return $SUCCESS + elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then + if [ "$new_ns" ]; then + msg="$msg_saving_nameserver_existing" + else + msg="$msg_removing_nameserver" + fi + f_dialog_info "$msg" + + # + # Create a new temporary file to write our new resolv.conf(5) + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Quietly fixup permissions and ownership + # + local mode owner + mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null ) + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on resolv.conf(5) + # + local new_contents + new_contents=$( awk -v nsindex="$index" \ + -v old_value="$old_ns" \ + -v new_value="$new_ns" \ + "$f_dialog_input_nameserver_edit_awk" \ + "$RESOLV_CONF" ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_die 1 "$msg_internal_error_nsindex_value" "$nsindex";; + 2) f_dialog_msgbox "$msg_resolv_conf_changed_while_editing" + return $retval;; + 3) f_dialog_msgbox "$msg_resolv_conf_entry_no_longer_exists" + return $retval;; + esac + + # + # Write the temporary file contents and move the temporary + # file into place. + # + echo "$new_contents" > "$tmpfile" || return $FAILURE + f_quietly mv "$tmpfile" "$RESOLV_CONF" + fi +} + +# f_dialog_menu_nameservers +# +# Edit the nameservers in resolv.conf(5). +# +f_dialog_menu_nameservers() +{ + local opt_exit="$msg_return_to_previous_menu" + local opt_add="$msg_add_nameserver" + local hline="$hline_arrows_tab_enter" + local prompt size + + # + # Loop forever until the user has finished configuring nameservers + # + prompt="$msg_dns_configuration" + while :; do + # + # Re/Build list of nameservers + # + local nameservers="$( f_resolv_conf_nameservers )" + local menu_list="$( + index=1 + + echo "'X $msg_exit' '$opt_exit'" + index=$(( $index + 1 )) + + echo "'A $msg_add' '$opt_add'" + index=$(( $index + 1 )) + + for ns in $nameservers; do + [ $index -lt ${#DIALOG_MENU_TAGS} ] || break + tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) + echo "'$tag nameserver' '$ns'" + index=$(( $index + 1 )) + done + )" + + # + # Display configuration-edit menu + # + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + local tag="$( f_dialog_menutag )" ns="" + + # Return if "Cancel" was chosen (-1) or ESC was pressed (255) + [ $retval -eq $SUCCESS ] || return $retval + + case "$tag" in + "X $msg_exit") break;; + "A $msg_add") + f_dialog_input_nameserver + ;; + *) + n=$( eval f_dialog_menutag2index \"\$tag\" $menu_list ) + ns=$( eval f_dialog_menutag2item \"\$tag\" $menu_list ) + f_dialog_input_nameserver $(( $n - 2 )) "$ns" + ;; + esac + done +} + +fi # ! $_NETWORKING_RESOLV_SUBR diff --git a/usr.sbin/bsdconfig/networking/share/routing.subr b/usr.sbin/bsdconfig/networking/share/routing.subr new file mode 100644 index 0000000000000..c6b6aedf9f397 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/share/routing.subr @@ -0,0 +1,168 @@ +if [ ! "$_NETWORKING_ROUTING_SUBR" ]; then _NETWORKING_ROUTING_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/networking/common.subr +f_include $BSDCFG_SHARE/networking/ipaddr.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_route_get_default +# +# Returns the IP address of the currently active default router. +# +f_route_get_default() +{ + route -n get default 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( $1 == "gateway:" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_dialog_input_defaultrouter +# +# Edits the default router. +# +f_dialog_input_defaultrouter() +{ + # + # Get the defaultrouter. When this is not configured, the default is + # "NO", however we don't ever want to present this default to the user + # in the following dialog. If the current value is "NO", then try to + # obtain the value from the running system using route(8). + # + # NOTE: Our `f_route_get_default' function will return NULL if the + # system does not have an active default router set (which is what we + # want). + # + local defaultrouter="$( f_sysrc_get 'defaultrouter:-NO' )" + local defaultrouter_orig="$defaultrouter" # for change-tracking + case "$defaultrouter" in + [Nn][Oo]) + defaultrouter=$( f_route_get_default ) + ;; + esac + + # + # Return with-error when there are NFS-mounts currently active. If the + # default router/gateway is changed while NFS-exported directories are + # mounted, the system will hang. + # + if f_nfs_mounted && ! f_jailed; then + local setting="$( printf "$msg_current_default_router" \ + "$defaultrouter" )" + local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ + "$setting" )" + f_dialog_msgbox "$message" + return $FAILURE + fi + + local msg="$msg_please_enter_default_router" + local hline="$hline_num_punc_tab_enter" + local size="$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$defaultrouter" \ + "$hline" )" + + # + # Loop until the user provides taint-free input. + # + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$defaultrouter\" \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + + local retval=$? + defaultrouter=$( f_dialog_inputstr ) + + [ "$defaultrouter" ] || return $SUCCESS + [ $retval -eq $SUCCESS ] || return $retval + + # Taint-check the user's input + f_dialog_validate_ipaddr "$defaultrouter" && break + done + + # + # Save only if the user changed the default router/gateway. + # + if [ "$defaultrouter" != "$defaultrouter_orig" ]; then + f_dialog_info "$msg_saving_default_router" + + # Save the default router/gateway + f_sysrc_set defaultrouter "$defaultrouter" + fi + + # + # Only ask to apply setting if the current defaultrouter is different + # than the stored configuration (in rc.conf(5)). + # + if [ "$( f_route_get_default )" != "$defaultrouter" ]; then + f_dialog_clear + f_dialog_yesno "$( + printf "$msg_activate_default_router" \ + "$( f_route_get_default )" "$defaultrouter" + )" + + if [ $? -eq $SUCCESS ]; then + local err + + # Apply the default router/gateway + f_quietly route delete default + err=$( route add default "$defaultrouter" 2>&1 ) + if [ $? -ne $SUCCESS ]; then + f_dialog_msgbox "$err" + return $FAILURE + fi + fi + fi +} + +fi # ! $_NETWORKING_ROUTING_SUBR diff --git a/usr.sbin/bsdconfig/password/Makefile b/usr.sbin/bsdconfig/password/Makefile index 499090708544e..7330c318ee514 100644 --- a/usr.sbin/bsdconfig/password/Makefile +++ b/usr.sbin/bsdconfig/password/Makefile @@ -2,7 +2,7 @@ NO_OBJ= -SUBDIR= include +SUBDIR= include share FILESDIR= ${LIBEXECDIR}/bsdconfig/040.password FILES= INDEX USAGE diff --git a/usr.sbin/bsdconfig/password/include/Makefile b/usr.sbin/bsdconfig/password/include/Makefile index 1c1d0e1eabb34..27ad607ed98d5 100644 --- a/usr.sbin/bsdconfig/password/include/Makefile +++ b/usr.sbin/bsdconfig/password/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/040.password/include -FILES= messages.subr password.subr +FILES= messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/password/include/password.subr b/usr.sbin/bsdconfig/password/include/password.subr deleted file mode 100644 index 846b76b54fa99..0000000000000 --- a/usr.sbin/bsdconfig/password/include/password.subr +++ /dev/null @@ -1,122 +0,0 @@ -if [ ! "$_PASSWORD_PASSWORD_SUBR" ]; then _PASSWORD_PASSWORD_SUBR=1 -# -# Copyright (c) 2012 Devin Teske -# 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. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr - -APP_DIR="040.password" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_dialog_input_password -# -# Prompt the user to enter a password (twice). If the user does not cancel or -# press ESC, the $pw_password environment variable will hold the password. -# -f_dialog_input_password() -{ - local hline="$hline_alnum_punc_tab_enter" - local msg size rmsg rsize - - msg=$( printf "$msg_password" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "" \ - "$hline" ) - - rmsg=$( printf "$msg_reenter_password" ) - rsize=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$rmsg" \ - "" \ - "$hline" ) - - # - # Loop until the user provides taint-free/valid input - # - local retval _password1 _password2 - while :; do - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --insecure \ - --passwordbox \"\$msg\" $size \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _password1=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --insecure \ - --passwordbox \"\$rmsg\" $rsize \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _password2=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Check for NULL entry - if ! [ "$_password1" -o "$_password2" ]; then - f_show_msg "$msg_password_is_empty" - continue - fi - - # Check for password mismatch - if [ "$_password1" != "$_password2" ]; then - f_show_msg "$msg_passwords_do_not_match" - continue - fi - - pw_password="$_password1" - break - done - - return $SUCCESS -} - -fi # ! $_PASSWORD_PASSWORD_SUBR diff --git a/usr.sbin/bsdconfig/password/password b/usr.sbin/bsdconfig/password/password index 2cbd54c8a8f5b..7a439e62341d3 100755 --- a/usr.sbin/bsdconfig/password/password +++ b/usr.sbin/bsdconfig/password/password @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/password/password.subr -APP_DIR="040.password" -f_include $BSDCFG_LIBE/$APP_DIR/include/password.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="040.password" ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" diff --git a/usr.sbin/bsdconfig/password/share/Makefile b/usr.sbin/bsdconfig/password/share/Makefile new file mode 100644 index 0000000000000..ceba51074f5a5 --- /dev/null +++ b/usr.sbin/bsdconfig/password/share/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/bsdconfig/password +FILES= password.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/password/share/password.subr b/usr.sbin/bsdconfig/password/share/password.subr new file mode 100644 index 0000000000000..550d2aa7c6568 --- /dev/null +++ b/usr.sbin/bsdconfig/password/share/password.subr @@ -0,0 +1,122 @@ +if [ ! "$_PASSWORD_PASSWORD_SUBR" ]; then _PASSWORD_PASSWORD_SUBR=1 +# +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="040.password" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_input_password +# +# Prompt the user to enter a password (twice). If the user does not cancel or +# press ESC, the $pw_password environment variable will hold the password. +# +f_dialog_input_password() +{ + local hline="$hline_alnum_punc_tab_enter" + local msg size rmsg rsize + + msg=$( printf "$msg_password" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "" \ + "$hline" ) + + rmsg=$( printf "$msg_reenter_password" ) + rsize=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$rmsg" \ + "" \ + "$hline" ) + + # + # Loop until the user provides taint-free/valid input + # + local retval _password1 _password2 + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$msg\" $size \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password1=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$rmsg\" $rsize \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password2=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for NULL entry + if ! [ "$_password1" -o "$_password2" ]; then + f_show_msg "$msg_password_is_empty" + continue + fi + + # Check for password mismatch + if [ "$_password1" != "$_password2" ]; then + f_show_msg "$msg_passwords_do_not_match" + continue + fi + + pw_password="$_password1" + break + done + + return $SUCCESS +} + +fi # ! $_PASSWORD_PASSWORD_SUBR diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index f457e942e4c75..b284592822807 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="130.security" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="130.security" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/security/security b/usr.sbin/bsdconfig/security/security index 25754a569830b..facdbea64535d 100755 --- a/usr.sbin/bsdconfig/security/security +++ b/usr.sbin/bsdconfig/security/security @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="130.security" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="130.security" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/share/Makefile b/usr.sbin/bsdconfig/share/Makefile new file mode 100644 index 0000000000000..7f299ec9c3ee1 --- /dev/null +++ b/usr.sbin/bsdconfig/share/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/bsdconfig +FILES= common.subr dialog.subr mustberoot.subr strings.subr sysrc.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr new file mode 100644 index 0000000000000..c724c62f67008 --- /dev/null +++ b/usr.sbin/bsdconfig/share/common.subr @@ -0,0 +1,299 @@ +if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ GLOBALS + +# +# Program name +# +pgm="${0##*/}" + +# +# Program arguments +# +ARGC="$#" +ARGV="$@" + +# +# Global exit status variables +# +SUCCESS=0 +FAILURE=1 + +############################################################ FUNCTIONS + +# +# This is an empty function by default, to use it, copy +# /usr/share/examples/bsdconfig/bsdconfigrc to $HOME/.bsdconfigrc +# +f_dprintf() +{ + : this page intentionally left blank +} + +# f_err $fmt [ $opts ... ] +# +# Print a message to stderr (fd=2). +# +f_err() +{ + printf "$@" >&2 +} + +# f_quietly $command [ $arguments ... ] +# +# run a command quietly (quell any output to stdout or stderr) +# +f_quietly() +{ + "$@" > /dev/null 2>&1 +} + +# f_have $anything ... +# +# A wrapper to the `type' built-in. Returns true if argument is a valid shell +# built-in, keyword, or externally-tracked binary, otherwise false. +# +f_have() +{ + f_quietly type "$@" +} + +# f_die [ $status [ $fmt [ $opts ... ]]] +# +# Abruptly terminate due to an error optionally displaying a message in a +# dialog box using printf(1) syntax. +# +f_die() +{ + local status=$FAILURE + + # If there is at least one argument, take it as the status + if [ $# -gt 0 ]; then + status=$1 + shift 1 # status + fi + + # If there are still arguments left, pass them to f_show_msg + [ $# -gt 0 ] && f_show_msg "$@" + + # Optionally call f_clean_up() function if it exists + f_have f_clean_up && f_clean_up + + exit $status +} + +# f_interrupt +# +# Interrupt handler. +# +f_interrupt() +{ + exec 2>&1 # fix sh(1) bug where stderr gets lost within async-trap + f_die +} + +# f_show_msg $fmt [ $opts ... ] +# +# Display a message in a dialog box using printf(1) syntax. +# +f_show_msg() +{ + local msg + msg=$( printf "$@" ) + + # + # Use f_dialog_msgbox from dialog.subr if possible, otherwise fall + # back to dialog(1) (without options, making it obvious when using + # un-aided system dialog). + # + if f_have f_dialog_msgbox; then + f_dialog_msgbox "$msg" + else + dialog --msgbox "$msg" 0 0 + fi +} + +# f_include $file +# +# Include a shell subroutine file. +# +# If the subroutine file exists but returns error status during loading, exit +# is called and execution is prematurely terminated with the same error status. +# +f_include() +{ + local file="$1" + . "$file" || exit $? +} + +# f_include_lang $file +# +# Include a language file. Automatically takes $LANG and $LC_ALL into +# considerationg when including $file (suffix ".$LC_ALL" or ".$LANG" will +# automatically by added prior to loading the language file). +# +# No error is produced if (a) a language has been requested (by setting either +# $LANG or $LC_ALL in the environment) and (b) the language file does not +# exist -- in which case we will fall back to loading $file without-suffix. +# +# If the language file exists but returns error status during loading, exit +# is called and execution is prematurely terminated with the same error status. +# +f_include_lang() +{ + local file="$1" + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + if [ -f "$file.$lang" ]; then + . "$file.$lang" || exit $? + else + . "$file" || exit $? + fi +} + +# f_usage $file [ $key1 $value1 ... ] +# +# Display USAGE file with optional pre-processor macro definitions. The first +# argument is the template file containing the usage text to be displayed. If +# $LANG or $LC_ALL (in order of preference, respectively) is set, ".encoding" +# will automatically be appended as a suffix to the provided $file pathname. +# +# When processing $file, output begins at the first line containing that is +# (a) not a comment, (b) not empty, and (c) is not pure-whitespace. All lines +# appearing after this first-line are output, including (a) comments (b) empty +# lines, and (c) lines that are purely whitespace-only. +# +# If additional arguments appear after $file, substitutions are made while +# printing the contents of the USAGE file. The pre-processor macro syntax is in +# the style of autoconf(1), for example: +# +# f_usage $file "FOO" "BAR" +# +# Will cause instances of "@FOO@" appearing in $file to be replaced with the +# text "BAR" before bering printed to the screen. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_usage_awk=' +BEGIN { found = 0 } +{ + if ( !found && $0 ~ /^[[:space:]]*($|#)/ ) next + found = 1 + print +} +' +f_usage() +{ + local file="$1" + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + + shift 1 # file + + local usage + if [ -f "$file.$lang" ]; then + usage=$( awk "$f_usage_awk" "$file.$lang" ) || exit $FAILURE + else + usage=$( awk "$f_usage_awk" "$file" ) || exit $FAILURE + fi + + while [ $# -gt 0 ]; do + local key="$1" + export value="$2" + usage=$( echo "$usage" | awk \ + "{ gsub(/@$key@/, ENVIRON[\"value\"]); print }" ) + shift 2 + done + + f_err "%s\n" "$usage" + + exit $FAILURE +} + +# f_index_menu_selection $file $pgm +# +# Process $file looking for $menu_selection values that correspond to $pgm. +# This function is for internationalization (i18n) mapping of the on-disk +# scriptname ($pgm) into the localized language (given language-specific +# $file). If $LANG or $LC_ALL (in orderder of preference, respectively) is set, +# ".encoding" will automatically be appended as a suffix to the provided $file +# pathname. +# +# If, within $file, multiple $menu_selection values map to $pgm, only the first +# one will be returned. If no mapping can be made, the NULL string is returned. +# +# If $file does not exist, error status is returned along with the NULL string. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_index_menusel_awk=' +# Variables that should be defined on the invocation line: +# -v pgm="program_name" +# +( $0 ~ "^menu_selection=.*\\|" pgm "\"" ) { + sub(/\|.*/, "") + sub(/^menu_selection="/, "") + print + exit +} +' +f_index_menu_selection() +{ + local file="$1" pgm="$2" + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + + if [ -f "$file.$lang" ]; then + awk -v pgm="$pgm" "$f_index_menusel_awk" "$file.$lang" || + exit $FAILURE + elif [ -f "$file" ]; then + awk -v pgm="$pgm" "$f_index_menusel_awk" "$file" || + exit $FAILURE + else + return $FAILURE + fi +} + +############################################################ MAIN + +# +# Trap signals so we can recover gracefully +# +trap 'f_interrupt' SIGINT +trap 'f_die' SIGTERM SIGPIPE SIGXCPU SIGXFSZ \ + SIGFPE SIGTRAP SIGABRT SIGSEGV +trap '' SIGALRM SIGPROF SIGUSR1 SIGUSR2 SIGHUP SIGVTALRM + +fi # ! $_COMMON_SUBR diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr new file mode 100644 index 0000000000000..cced388af2e04 --- /dev/null +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -0,0 +1,1443 @@ +if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION + +# +# Default directory to store dialog(1) temporary files +# +: ${DIALOG_TMPDIR:="/tmp"} + +############################################################ GLOBALS + +# +# Default name of dialog(1) utility +# NOTE: This is changed to "Xdialog" by the optional `-X' argument +# +DIALOG="dialog" + +# +# Default dialog(1) title and backtitle text +# +DIALOG_TITLE="$pgm" +DIALOG_BACKTITLE="bsdconfig" + +# +# Settings used while interacting with dialog(1) +# +DIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz" + +# +# Declare that we are fully-compliant with Xdialog(1) by unset'ing all +# compatibility settings. +# +unset XDIALOG_HIGH_DIALOG_COMPAT +unset XDIALOG_FORCE_AUTOSIZE +unset XDIALOG_INFOBOX_TIMEOUT + +# +# Default behavior is to call f_dialog_init() automatically if not already +# called manually by the time the first f_dialog_*() function is used. +# +: ${DIALOG_SELF_INITIALIZE=1} + +############################################################ GENERIC FUNCTIONS + +# f_dialog_title [$new_title] +# +# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1) +# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first +# argument is NULL, the current title is returned. +# +# Each time this function is called, a backup of the current values is made +# allowing a one-time (single-level) restoration of the previous title using the +# f_dialog_title_restore() function (below). +# +f_dialog_title() +{ + local new_title="$1" + + if [ "$new_title" ]; then + if [ "$USE_XDIALOG" ]; then + _DIALOG_BACKTITLE="$DIALOG_BACKTITLE" + DIALOG_BACKTITLE="$new_title" + else + _DIALOG_TITLE="$DIALOG_TITLE" + DIALOG_TITLE="$new_title" + fi + else + if [ "$USE_XDIALOG" ]; then + echo "$DIALOG_BACKTITLE" + else + echo "$DIALOG_TITLE" + fi + fi +} + +# f_dialog_title_restore +# +# Restore the previous title set by the last call to f_dialog_title(). +# Restoration is non-recursive and only works to restore the most-recent title. +# +f_dialog_title_restore() +{ + if [ "$USE_XDIALOG" ]; then + DIALOG_BACKTITLE="$_DIALOG_BACKTITLE" + else + DIALOG_TITLE="$_DIALOG_TITLE" + fi +} + +# f_dialog_backtitle [$new_backtitle] +# +# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of +# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the +# first argument is NULL, the current backtitle is returned. +# +f_dialog_backtitle() +{ + local new_backtitle="$1" + + if [ "$new_backtitle" ]; then + if [ "$USE_XDIALOG" ]; then + _DIALOG_TITLE="$DIALOG_TITLE" + DIALOG_TITLE="$new_backtitle" + else + _DIALOG_BACKTITLE="$DIALOG_BACKTITLE" + DIALOG_BACKTITLE="$new_backtitle" + fi + else + if [ "$USE_XDIALOG" ]; then + echo "$DIALOG_TITLE" + else + echo "$DIALOG_BACKTITLE" + fi + fi +} + +# f_dialog_backtitle_restore +# +# Restore the previous backtitle set by the last call to f_dialog_backtitle(). +# Restoration is non-recursive and only works to restore the most-recent +# backtitle. +# +f_dialog_backtitle_restore() +{ + if [ "$USE_XDIALOG" ]; then + DIALOG_TITLE="$_DIALOG_TITLE" + else + DIALOG_BACKTITLE="$_DIALOG_BACKTITLE" + fi +} + +############################################################ SIZE FUNCTIONS + +# f_dialog_infobox_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--infobox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width or height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_infobox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + max_size=$( stty size ) # usually "24 80" + fi + + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + local height width=$min_width + + # + # Bump width for long titles (but don't exceed terminal width). + # + n=$(( ${#title} + 4 )) + if [ $n -gt $width -a $n -gt $min_width ]; then + # Add 16.6% width for Xdialog(1) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) + + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # + # For Xdialog(1), bump width for long backtitles (which appear within + # the window; don't exceed maximum width). + # + if [ "$USE_XDIALOG" ]; then + n=$(( ${#btitle} + 4 )) + n=$(( $n + $n / 6 )) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # + # Bump width for long prompts (if not already at maximum width). + # + if [ $width -lt $max_width ]; then + n=$( echo "$prompt" | f_longest_line_length ) + n=$(( $n + 4 )) + + # Add 16.6% width for Xdialog(1) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # + # Bump width for long hlines (if not already at maximum width). + # NOTE: Though Xdialog(1) supports `--hline', it's not currently used. + # + if [ ! "$USE_XDIALOG" ]; then + if [ $width -lt $max_width ]; then + n=$(( ${#hline} + 10 )) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + fi + + # + # Set height based on number of rows in prompt + # + height=$( echo "$prompt" | f_number_of_lines ) + height=$(( $height + 2 )) + + # + # For Xdialog(1) bump height if backtitle is enabled (displayed in the + # X11 window with a separator line between the backtitle and msg text) + # + if [ "$USE_XDIALOG" -a "$btitle" ]; then + n=$( echo "$btitle" | f_number_of_lines ) + height=$(( $height + $n + 2 )) + fi + + # Make sure height is less than maximum screen size + [ $height -le $max_height ] || height=$max_height + + # Return both + echo "$height $width" +} + +# f_dialog_buttonbox_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--msgbox' and `--yesno' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width or height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_buttonbox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" + local size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + # Add height to accomodate the buttons + height=$(( $height + 2 )) + + # Adjust for clipping with Xdialog(1) on Linux/GTK2 + [ "$USE_XDIALOG" ] && height=$(( $height + 3 )) + + # + # Enforce maximum height regardless + # + local max_size + if [ "$USE_XDIALOG" ]; then + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + [ $height -le $max_height ] || height=$max_height + + # Return both + echo "$height $width" +} + +# f_dialog_inputbox_size $title $backtitle $prompt $init [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--inputbox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, initial text, and [optionally] +# hline returning the optimal width and height for the box (not exceeding the +# actual terminal width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_inputbox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" init="$4" hline="$5" n + local size="$( f_dialog_buttonbox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width max_size + if [ "$USE_XDIALOG" ]; then + min_width=35 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # + # Add height to accomodate the input box + # + [ ! "$USE_XDIALOG" ] && height=$(( $height + 3 )) + [ $height -le $max_height ] || height=$max_height + + # + # Bump width for initial text (if not already at maximum width). + # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it! + # + if [ $width -lt $max_width ]; then + n=$(( ${#init} + 7 )) + + # Add 16.6% width for Xdialog(1) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Return both + echo "$height $width" +} + +# f_xdialog_2inputsbox_size $title $backtitle $prompt \ +# $label1 $init1 $label2 $init2 +# +# Xdialog(1) does not perform auto-sizing of the width and height of +# `--2inputsbox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, label for the first field, initial +# text for said field, label for the second field, and initial text for said +# field returning the optimal width and height for the box (not exceeding the +# actual terminal width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# Xdialog(1). +# +# Output is in the format of "height width". +# +f_xdialog_2inputsbox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" + local label1="$4" init1="$5" label2="$6" init2="$7" n + local size="$( f_dialog_inputbox_size \ + "$title" "$btitle" "$prompt" "$init1" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width=35 + local max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # Add height for first label + height=$(( $height + 2 )) + + # + # Bump width for first label text (if not already at maximum width). + # + if [ $width -lt $max_width ]; then + n=$(( ${#label1} + 7 )) + + # Add 16.6% width for Xdialog(1) + n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Add height for second label + height=$(( $height + 2 )) + + # + # Bump width for second label text (if not already at maximum width). + # + if [ $width -lt $max_width ]; then + n=$(( ${#label2} + 7 )) + + # Add 16.6% width for Xdialog(1) + n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Add height for a second inputbox + height=$(( $height + 2 )) + + # + # Bump width for second initial text (if not already at maximum width). + # NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it! + # + if [ $width -lt $max_width ]; then + n=$(( ${#init2} + 7 )) + + # Add 16.6% width for Xdialog(1) + n=$(( $n + $n / 6 )) + + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Return both + echo "$height $width" +} + +# f_dialog_menu_size $title $backtitle $prompt $hline \ +# $tag1 $item1 $tag2 $item2 ... +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--menu' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, hline and list of tag/item pairs, +# returning the optimal width and height for the menu (not exceeding the actual +# terminal width or height). +# +# Output is in the format of "height width rows". +# +f_dialog_menu_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width min_rows max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + min_rows=1 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + min_rows=0 + max_size=$( stty size ) # usually "24 80" + fi + + local max_width="${max_size##*[$IFS]}" + local max_height="${max_size%%[$IFS]*}" + local box_size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local box_height="${box_size%%[$IFS]*}" + local box_width="${box_size##*[$IFS]}" + local max_rows=$(( $max_height - 8 )) + local height width=$box_width rows=$min_rows + + shift 4 # title/btitle/prompt/hline + + # If there's no prompt, bump the max-rows by 1 + [ "$prompt" ] || max_rows=$(( $max_rows + 1 )) + + # + # The sum total between the longest tag-length and longest item-length + # should be used for the menu width (not to exceed terminal width). + # + # Also, calculate the number of rows (not to exceed terminal height). + # + local longest_tag=0 longest_item=0 + while [ $# -ge 2 ]; do + local tag="$1" item="$2" + shift 2 # tag/item + + [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} + [ ${#item} -gt $longest_item ] && longest_item=${#item} + [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) + done + + # Update width + n=$(( $longest_tag + $longest_item + 10 )) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # Fix rows and set height + [ $rows -gt 0 ] || rows=1 + if [ "$USE_XDIALOG" ]; then + height=$(( $rows + $box_height + 7 )) + else + height=$(( $rows + $box_height + 4 )) + fi + [ $height -le $max_height ] || height=$max_height + + # Return all three + echo "$height $width $rows" +} + +# f_dialog_menu_with_help_size $title $backtitle $prompt $hline \ +# $tag1 $item1 $help1 $tag2 $item2 $help2 ... +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--menu' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, hline and list of tag/item/help +# triplets, returning the optimal width and height for the menu (not exceeding +# the actual terminal width or height). +# +# Output is in the format of "height width rows". +# +f_dialog_menu_with_help_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width min_rows max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + min_rows=1 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + min_rows=0 + max_size=$( stty size ) # usually "24 80" + fi + + local max_width="${max_size##*[$IFS]}" + local max_height="${max_size%%[$IFS]*}" + local box_size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local box_height="${box_size%%[$IFS]*}" + local box_width="${box_size##*[$IFS]}" + local max_rows=$(( $max_height - 8 )) + local height width=$box_width rows=$min_rows + + shift 4 # title/btitle/prompt/hline + + # If there's no prompt, bump the max-rows by 1 + [ "$prompt" ] || max_rows=$(( $max_rows + 1 )) + + # + # The sum total between the longest tag-length and longest item-length + # should be used for the menu width (not to exceed terminal width). + # + # Also, calculate the number of rows (not to exceed terminal height). + # + # Also, calculate the longest help while we're here. This will be used + # to influence the width of the menu if (and only-if) using Xdialog(1). + # + local longest_tag=0 longest_item=0 longest_help=0 + while [ $# -ge 3 ]; do + local tag="$1" item="$2" help="$3" + shift 3 # tag/item/help + + [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} + [ ${#item} -gt $longest_item ] && longest_item=${#item} + [ ${#help} -gt $longest_help ] && longest_help=${#help} + [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) + done + + # Update width + n=$(( $longest_tag + $longest_item + 10 )) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # Update width for help text if using Xdialog(1) + if [ "$USE_XDIALOG" ]; then + n=$(( $longest_help + 10 )) + n=$(( $n + $n / 6 )) # +16.6% + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + fi + + # Fix rows and set height + [ $rows -gt 0 ] || rows=1 + if [ "$USE_XDIALOG" ]; then + height=$(( $rows + $box_height + 8 )) + else + height=$(( $rows + $box_height + 4 )) + fi + [ $height -le $max_height ] || height=$max_height + + # Return all three + echo "$height $width $rows" +} + +# f_dialog_radiolist_size $title $backtitle $prompt $hline \ +# $tag1 $item1 $status1 $tag2 $item2 $status2 ... +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--radiolist' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, hline and list of tag/item/status +# triplets, returning the optimal width and height for the radiolist (not +# exceeding the actual terminal width or height). +# +# Output is in the format of "height width rows". +# +f_dialog_radiolist_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n=0 + local min_width min_rows max_size + + if [ "$USE_XDIALOG" ]; then + min_width=35 + min_rows=1 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_width=24 + min_rows=0 + max_size=$( stty size ) # usually "24 80" + fi + + local max_width="${max_size##*[$IFS]}" + local max_height="${max_size%%[$IFS]*}" + local box_size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local box_height="${box_size%%[$IFS]*}" + local box_width="${box_size##*[$IFS]}" + local max_rows=$(( $max_height - 8 )) + local height width=$box_width rows=$min_rows + + shift 4 # title/btitle/prompt/hline + + # + # The sum total between the longest tag-length, longest item-length, + # and radio-button width should be used for the menu width (not to + # exceed terminal width). + # + # Also, calculate the number of rows (not to exceed terminal height). + # + local longest_tag=0 longest_item=0 + while [ $# -ge 2 ]; do + local tag="$1" item="$2" help="$3" + shift 3 # tag/item/status + + [ ${#tag} -gt $longest_tag ] && longest_tag=${#tag} + [ ${#item} -gt $longest_item ] && longest_item=${#item} + [ $rows -lt $max_rows ] && rows=$(( $rows + 1 )) + done + + # Update width + n=$(( $longest_tag + $longest_item + 13 )) + [ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1) + if [ $n -gt $width -a $n -gt $min_width ]; then + if [ $n -lt $max_width ]; then + width=$n + else + width=$max_width + fi + fi + + # Fix rows and set height + [ $rows -gt 0 ] || rows=1 + if [ "$USE_XDIALOG" ]; then + height=$(( $rows + $box_height + 7 )) + else + height=$(( $rows + $box_height + 4 )) + fi + [ $height -le $max_height ] || height=$max_height + + # Return all three + echo "$height $width $rows" +} + +# f_dialog_calendar_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--calendar' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_calendar_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n + local size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width min_height max_size + if [ "$USE_XDIALOG" ]; then + min_height=15 + min_width=55 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_height=0 + min_width=40 + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # + # Enforce the minimum width for displaying the calendar + # + [ $width -ge $min_width ] || width=$min_width + + # + # When using dialog(1), the calendar box is unique from other dialog(1) + # boxes in-that the height passed should not accomodate the 15-lines + # required to display the calendar. This does not apply to Xdialog(1). + # + # When using Xdialog(1), the height must accomodate the 15-lines + # required to display the calendar. + # + # NOTE: Also under dialog(1), because we can't predict whether the user + # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract + # 16 rather than 15. This does not apply to Xdialog(1). + # + max_height=$(( $max_height - 16 )) + height=$( echo "$prompt" | f_number_of_lines ) + if [ "$USE_XDIALOG" ]; then + # Add height to accomodate for the embedded calendar widget + height=$(( $height + $min_height - 1 )) + + # Also, bump height if backtitle is enabled + if [ "$btitle" ]; then + local n="$( echo "$btitle" | f_number_of_lines )" + height=$(( $height + $n + 2 )) + fi + else + [ "$prompt" ] && height=$(( $height + 1 )) + fi + [ $height -le $max_height ] || height=$max_height + + # + # The calendar box refuses to display if too large. + # + max_width=$(( $max_width - 2 )) + [ $width -le $max_width ] || width=$max_width + + # Return both + echo "$height $width" +} + +# f_dialog_timebox_size $title $backtitle $prompt [$hline] +# +# Not all versions of dialog(1) perform auto-sizing of the width and height of +# `--timebox' boxes sensibly. +# +# This function helps solve this issue by taking as arguments (in order of +# appearance) the title, backtitle, prompt, and [optionally] hline returning +# the optimal width and height for the box (not exceeding the actual terminal +# width and height). +# +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# +# Output is in the format of "height width". +# +f_dialog_timebox_size() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local title="$1" btitle="$2" prompt="$3" hline="$4" n + local size="$( f_dialog_infobox_size \ + "$title" "$btitle" "$prompt" "$hline" )" + local height="${size%%[$IFS]*}" + local width="${size##*[$IFS]}" + + local min_width min_height max_size + if [ "$USE_XDIALOG" ]; then + min_width=40 + max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION + else + min_height=0 + min_width=20 + max_size=$( stty size ) # usually "24 80" + fi + local max_height="${max_size%%[$IFS]*}" + local max_width="${max_size##*[$IFS]}" + + # + # Enforce the minimum width for displaying the timebox + # + [ $width -ge $min_width ] || width=$min_width + + # + # When using dialog(1), the timebox box is unique from other dialog(1) + # boxes in-that the height passed should not accomodate the 6-lines + # required to display the timebox. This does not apply to Xdialog(1). + # + # When using Xdialog(1), the height seems to have no effect. All values + # provide the same results. + # + # NOTE: Also under dialog(1), because we can't predict whether the user + # has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract + # 7 rather than 6. This does not apply to Xdialog(1). + # + if [ "$USE_XDIALOG" ]; then + height=0 # Autosize; all values produce same results + else + max_height=$(( $max_height - 7 )) + height=$( echo "$prompt" | f_number_of_lines ) + height=$(( $height + 1 )) + [ $height -le $max_height ] || height=$max_height + [ "$prompt" ] && height=$(( $height + 1 )) + fi + + # + # The timebox box refuses to display if too large. + # + max_width=$(( $max_width - 2 )) + [ $width -le $max_width ] || width=$max_width + + # Return both + echo "$height $width" +} + +############################################################ CLEAR FUNCTIONS + +# f_dialog_clear +# +# Clears any/all previous dialog(1) displays. +# +f_dialog_clear() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + $DIALOG --clear +} + +############################################################ INFO FUNCTIONS + +# f_dialog_info $info_text ... +# +# Throw up a dialog(1) infobox. The infobox remains until another dialog is +# displayed or `dialog --clear' (or dialog_clear) is called. +# +f_dialog_info() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local info_text="$*" + local size="$( f_dialog_infobox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$info_text" )" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + ${USE_XDIALOG:+--ignore-eof} \ + ${USE_XDIALOG:+--no-buttons} \ + --infobox \"\$info_text\" $size +} + +# f_xdialog_info $info_text ... +# +# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces +# EOF. This implies that you must execute this either as an rvalue to a pipe, +# lvalue to indirection or in a sub-shell that provides data on stdin. +# +f_xdialog_info() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local info_text="$*" + local size="$( f_dialog_infobox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$info_text" )" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --no-close --no-buttons \ + --infobox \"\$info_text\" $size \ + -1 # timeout of -1 means abort when EOF on stdin +} + +############################################################ MSGBOX FUNCTIONS + +# f_dialog_msgbox $msg_text ... +# +# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER +# or ESC, acknowledging the modal dialog. +# +# If the user presses ENTER, the exit status is zero (success), otherwise if +# the user presses ESC the exit status is 255. +# +f_dialog_msgbox() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local size="$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg_text" )" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg_text\" $size +} + +############################################################ YESNO FUNCTIONS + +# f_dialog_yesno $msg_text ... +# +# Display a dialog(1) Yes/No prompt to allow the user to make some decision. +# The yesno prompt remains until the user presses ENTER or ESC, acknowledging +# the modal dialog. +# +# If the user chooses YES the exit status is zero, or chooses NO the exit +# status is one, or presses ESC the exit status is 255. +# +f_dialog_yesno() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local hline="$hline_arrows_tab_enter" + local size="$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg_text" \ + "$hline" )" + + if [ "$USE_XDIALOG" ]; then + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_yes\" \ + --cancel-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + else + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --yes-label \"\$msg_yes\" \ + --no-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + fi +} + +# f_dialog_noyes $msg_text ... +# +# Display a dialog(1) No/Yes prompt to allow the user to make some decision. +# The noyes prompt remains until the user presses ENTER or ESC, acknowledging +# the modal dialog. +# +# If the user chooses YES the exit status is zero, or chooses NO the exit +# status is one, or presses ESC the exit status is 255. +# +# NOTE: This is just like the f_dialog_yesno function except "No" is default. +# +f_dialog_noyes() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local hline="$hline_arrows_tab_enter" + local size="$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg_text" \ + "$hline" )" + + if [ "$USE_XDIALOG" ]; then + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --default-no \ + --ok-label \"\$msg_yes\" \ + --cancel-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + else + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --defaultno \ + --yes-label \"\$msg_yes\" \ + --no-label \"\$msg_no\" \ + --yesno \"\$msg_text\" $size + fi +} + +############################################################ INPUT FUNCTIONS + +# f_dialog_inputstr +# +# Obtain the inputstr entered by the user from the most recently displayed +# dialog(1) inputbox and clean up any temporary files. +# +f_dialog_inputstr() +{ + local tmpfile="$DIALOG_TMPDIR/dialog.inputbox.$$" + + [ -f "$tmpfile" ] || return $FAILURE + + # Skip warnings and trim leading/trailing whitespace from user input + awk ' + BEGIN { found = 0 } + { + if ( ! found ) + { + if ( $0 ~ /^$/ ) next + if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next + found = 1 + } + sub(/^[[:space:]]*/, "") + sub(/[[:space:]]*$/, "") + print + } + ' "$tmpfile" 2> /dev/null + f_quietly rm -f "$tmpfile" + + return $SUCCESS +} + +############################################################ MENU FUNCTIONS + +# f_dialog_menutag +# +# Obtain the menutag chosen by the user from the most recently displayed +# dialog(1) menu and clean up any temporary files. +# +f_dialog_menutag() +{ + local tmpfile="$DIALOG_TMPDIR/dialog.menu.$$" + + [ -f "$tmpfile" ] || return $FAILURE + + awk ' + BEGIN { found = 0 } + { + if ( found ) # ... just spew + { + print + next + } + if ( $0 ~ /^$/ ) next + if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next + found = 1 + print + } + ' "$tmpfile" 2> /dev/null + f_quietly rm -f "$tmpfile" + + return $SUCCESS +} + +# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ... +# +# To use the `--menu' option of dialog(1) you must pass an ordered list of +# tag/item pairs on the command-line. When the user selects a menu option the +# tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the item associated with said tag. +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item pairs (HINT: use the same tag/item list as was +# passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2item() +{ + local tag="$1" tagn item + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + item="$2" + shift 2 # tagn/item + + if [ "$tag" = "$tagn" ]; then + echo "$item" + return $SUCCESS + fi + done + return $FAILURE +} + +# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \ +# $tag2 $item2 $help2 ... +# +# To use the `--menu' option of dialog(1) with the `--item-help' option, you +# must pass an ordered list of tag/item/help triplets on the command-line. When +# the user selects a menu option the tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the item associated with said tag (help is discarded/ignored). +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list +# as was passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2item_with_help() +{ + local tag="$1" tagn item + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + item="$2" + shift 3 # tagn/item/help + + if [ "$tag" = "$tagn" ]; then + echo "$item" + return $SUCCESS + fi + done + return $FAILURE +} + +# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ... +# +# To use the `--menu' option of dialog(1) you must pass an ordered list of +# tag/item pairs on the command-line. When the user selects a menu option the +# tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the index associated with said tag. The index is the one-based tag/item pair +# array position within the ordered list of tag/item pairs passed to dialog(1). +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item pairs (HINT: use the same tag/item list as was +# passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2index() +{ + local tag="$1" tagn n=1 + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + shift 2 # tagn/item + + if [ "$tag" = "$tagn" ]; then + echo $n + return $SUCCESS + fi + n=$(( $n + 1 )) + done + return $FAILURE +} + +# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \ +# $tag2 $item2 $help2 ... +# +# To use the `--menu' option of dialog(1) with the `--item-help' option, you +# must pass an ordered list of tag/item/help triplets on the command-line. When +# the user selects a menu option the tag for that item is printed to stderr. +# +# This function allows you to dereference the tag chosen by the user back into +# the index associated with said tag. The index is the one-based tag/item/help +# triplet array position within the ordered list of tag/item/help triplets +# passed to dialog(1). +# +# Pass the tag chosen by the user as the first argument, followed by the +# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list +# as was passed to dialog(1) for consistency). +# +# If the tag cannot be found, NULL is returned. +# +f_dialog_menutag2index_with_help() +{ + local tag="$1" tagn n=1 + shift 1 # tag + + while [ $# -gt 0 ]; do + tagn="$1" + shift 3 # tagn/item/help + + if [ "$tag" = "$tagn" ]; then + echo $n + return $SUCCESS + fi + n=$(( $n + 1 )) + done + return $FAILURE +} + +############################################################ INIT FUNCTIONS + +# f_dialog_init +# +# Initialize (or re-initialize) the dialog module after setting/changing any +# of the following environment variables: +# +# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate +# that Xdialog(1) should be used instead of dialog(1). +# +# SECURE Either NULL or Non-NULL. If given a value will indicate +# that (while running as root) sudo(8) authentication is +# required to proceed. +# +f_dialog_init() +{ + DIALOG_SELF_INITIALIZE= + + # + # Process stored command-line arguments + # + SECURE=$( set -- "$ARGV" + while getopts S flag > /dev/null; do + case "$flag" in + S) echo 1;; + \?) continue;; + esac + done + ) + USE_XDIALOG=$( set -- "$ARGV" + while getopts SX flag > /dev/null; do + case "$flag" in + S|X) echo 1;; + \?) continue;; + esac + done + ) + + # + # Process `-X' command-line option + # + [ "$USE_XDIALOG" ] && DIALOG=Xdialog + + # + # Sanity check, or die gracefully + # + if ! f_have $DIALOG; then + unset USE_XDIALOG + failed_dialog="$DIALOG" + DIALOG=dialog + f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog" + fi + + # + # If we're already running as root but we got there by way of sudo(8) + # and we have X11, we should merge the xauth(1) credentials from our + # original user. + # + if [ "$USE_XDIALOG" ] && + [ "$( id -u )" = "0" ] && + [ "$SUDO_USER" -a "$DISPLAY" ] + then + if ! f_have xauth; then + # Die gracefully, as we [likely] can't use Xdialog(1) + unset USE_XDIALOG + DIALOG=dialog + f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth" + fi + HOSTNAME=$(hostname) + displaynum="${DISPLAY#*:}" + eval xauth -if \~$SUDO_USER/.Xauthority extract - \ + \"\$HOSTNAME/unix:\$displaynum\" \ + \"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \ + ~root/.Xauthority merge - > /dev/null 2>&1' + fi + + # + # Probe Xdialog(1) for maximum height/width constraints, or die + # gracefully + # + if [ "$USE_XDIALOG" ]; then + if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 ) + then + # Xdialog(1) failed, fall back to dialog(1) + unset USE_XDIALOG + size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$maxsize" "" ) + eval dialog \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$maxsize\" $size + exit $FAILURE + fi + + XDIALOG_MAXSIZE=$( + set -- ${maxsize##*:} + + height=${1%,} + width=$2 + + echo $height $width + ) + unset maxsize + fi + + # + # If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE. + # The reason for this is because many dialog(1) applications use + # --backtitle for the program name (which is better suited as + # --title with Xdialog(1)). + # + if [ "$USE_XDIALOG" ]; then + _DIALOG_TITLE="$DIALOG_TITLE" + DIALOG_TITLE="$DIALOG_BACKTITLE" + DIALOG_BACKTITLE="$_DIALOG_TITLE" + unset _DIALOG_TITLE + fi +} + +############################################################ CLEAN-UP FUNCTIONS + +# f_clean_up +# +# Clean-up routines (run when script exits or is killed). +# +f_clean_up() +{ + f_quietly rm -f "$DIALOG_TMPDIR"/dialog.*.$$ +} + +############################################################ MAIN + +# +# Trap signals so we can recover gracefully +# +trap 'f_clean_up' EXIT + +fi # ! $_DIALOG_SUBR diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr new file mode 100644 index 0000000000000..ec3e16a3547f3 --- /dev/null +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -0,0 +1,362 @@ +if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION +# NOTE: These are not able to be overridden/inherited for security purposes. + +# +# Number of tries a user gets to enter his/her password before we log the +# sudo(8) failure and exit. +# +PASSWD_TRIES=3 + +# +# While in SECURE mode, should authentication as `root' be allowed? Set to +# non-NULL to enable authentication as `root', otherwise disabled. +# +# WARNING: +# Unless using a custom sudo(8) configuration, user `root' should not be +# allowed because no password is required to become `root' when already `root' +# and therefore, any value entered as password will work. +# +SECURE_ALLOW_ROOT= + +# +# While in SECURE mode, should we divulge (through error message) when the +# requested authentication user does not exist? Set to non-NULL to enable, +# otherwise a non-existent user is treated like an invalid password. +# +SECURE_DIVULGE_UNKNOWN_USER= + +############################################################ FUNCTIONS + +# f_become_root_via_sudo +# +# If not running as root, prompt for sudo(8) credentials to become root. +# Re-execution of the current program via sudo is automatically handled. +# +# The following environment variables effect functionality: +# +# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate +# that Xdialog(1) should be used instead of dialog(1). +# +f_become_root_via_sudo() +{ + local msg hline size + + [ "$( id -u )" = "0" ] && return $SUCCESS + + f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" + + # + # Check sudo(8) access before prompting for password. + # + :| sudo -S -v 2> /dev/null + if [ $? -ne $SUCCESS ]; then + # + # sudo(8) access denied. Prompt for their password. + # + msg="$msg_please_enter_password" + hline="$hline_alnum_punc_tab_enter" + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + + # + # Continue prompting until they either Cancel, succeed + # or exceed the number of allowed failures. + # + local password nfailures=0 retval + while [ $nfailures -lt $PASSWD_TRIES ]; do + if [ "$USE_XDIALOG" ]; then + password=$( $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --password --inputbox "$msg" $size \ + 2>&1 > /dev/null ) + retval=$? + + # Catch X11-related errors + [ $retval -eq 255 ] && + f_die $retval "$password" + else + $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --insecure \ + --passwordbox "$msg" $size \ + 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + retval=$? + password=$( f_dialog_inputstr ) + fi + + # Exit if the user cancelled. + [ $retval -eq $SUCCESS ] || exit $retval + + # + # Validate sudo(8) credentials + # + sudo -S -v 2> /dev/null <<-EOF + $password + EOF + retval=$? + unset password # scrub memory + if [ $retval -eq $SUCCESS ]; then + # Access granted... + break + else + # Access denied... + nfailures=$(( $nfailures + 1 )) + + # introduce a short delay + if [ $nfailures -lt $PASSWD_TRIES ]; then + f_dialog_info "$msg_sorry_try_again" + sleep 1 + fi + fi + done + + # + # If user exhausted number of allowed password tries, log + # the security event and exit immediately. + # + if [ $nfailures -ge $PASSWD_TRIES ]; then + msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) + logger -p auth.notice -t sudo " " \ + "$USER : $msg" \ + "; TTY=$(tty)" \ + "; PWD=$PWD" \ + "; USER=root" \ + "; COMMAND=$0" + f_die 1 "sudo: $msg" + fi + fi + + # Use xauth(1) to grant root the ability to use this X11/SSH session + if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then + f_have xauth || f_die 1 \ + "$msg_no_such_file_or_directory" "$pgm" "xauth" + local HOSTNAME displaynum + HOSTNAME=$(hostname) + displaynum="${DISPLAY#*:}" + xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ + $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ + ~root/.Xauthority merge - > /dev/null 2>&1' + fi + + # Re-execute ourselves with sudo(8) + if [ $ARGC -gt 0 ]; then + exec sudo "$0" $ARGV + else + exec sudo "$0" + fi + exit $? # Never reached unless error +} + +# f_authenticate_some_user +# +# Only used if running as root and requires X11 (see USE_XDIALOG below). +# Prompts the user to enter a username and password to be authenticated via +# sudo(8) to proceed. +# +# The following environment variables effect functionality: +# +# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate +# that Xdialog(1) should be used instead of dialog(1). +# +f_authenticate_some_user() +{ + local msg hline size width height + + f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" + + # + # Secure-mode has been requested. + # + + [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" + [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" + + # + # Prompt for sudo(8) credentials. + # + + msg="$msg_please_enter_username_password" + hline="$hline_alnum_punc_tab_enter" + size=$( f_xdialog_2inputsbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$field_username" "" \ + "$field_password" "" ) + width="${size##*[$IFS]}" + height="${size%%[$IFS]*}" + height=$(( $height + 2 )) # Add height for --password + + # + # Continue prompting until they either Cancel, succeed or exceed the + # number of allowed failures. + # + local user_pass nfailures=0 retval + while [ $nfailures -lt $PASSWD_TRIES ]; do + user_pass=$( $DIALOG \ + --title "$DIALOG_TITLE" \ + --backtitle "$DIALOG_BACKTITLE" \ + --hline "$hline" \ + --ok-label "$msg_ok" \ + --cancel-label "$msg_cancel" \ + --password --2inputsbox "$msg" \ + $height $width \ + "$field_username" "" \ + "$field_password" "" \ + 2>&1 > /dev/null ) + retval=$? + + # Catch X11-related errors + [ $retval -eq 255 ] && f_die $retval "$user_pass" + + # Exit if the user cancelled. + [ $retval -eq $SUCCESS ] || exit $retval + + # + # Make sure the user exists and is non-root + # + local user password + user="${user_pass%%/*}" + password="${user_pass#*/}" + unset user_pass # scrub memory + if [ ! "$user" ]; then + nfailures=$(( $nfailures + 1 )) + f_dialog_msgbox "$msg_no_username" + continue + fi + if [ ! "$SECURE_ALLOW_ROOT" ]; then + case "$user" in + root|toor) + nfailures=$(( $nfailures + 1 )) + f_dialog_msgbox "$( printf \ + "$msg_user_disallowed" "$user" )" + continue + esac + fi + if ! f_quietly id "$user"; then + nfailures=$(( $nfailures + 1 )) + if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then + f_dialog_msgbox "$( printf \ + "$msg_unknown_user" "$user" )" + elif [ $nfailures -lt $PASSWD_TRIES ]; then + f_dialog_info "$msg_sorry_try_again" + sleep 1 + fi + continue + fi + + # + # Validate sudo(8) credentials for given user + # + su -m "$user" <<-EOF + sh < /dev/null < /dev/null 2>&1 +} + +fi # ! $_STRINGS_SUBR diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr new file mode 100644 index 0000000000000..014ab46a1f51f --- /dev/null +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -0,0 +1,618 @@ +if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION + +# +# Standard pathnames (inherit values from shell if available) +# +: ${RC_DEFAULTS:="/etc/defaults/rc.conf"} + +############################################################ GLOBALS + +# +# Global exit status variables +# +SUCCESS=0 +FAILURE=1 + +############################################################ FUNCTIONS + +# f_clean_env [ --except $varname ... ] +# +# Unset all environment variables in the current scope. An optional list of +# arguments can be passed, indicating which variables to avoid unsetting; the +# `--except' is required to enable the exclusion-list as the remainder of +# positional arguments. +# +# Be careful not to call this in a shell that you still expect to perform +# $PATH expansion in, because this will blow $PATH away. This is best used +# within a sub-shell block "(...)" or "$(...)" or "`...`". +# +f_clean_env() +{ + local var arg except= + + # + # Should we process an exclusion-list? + # + if [ "$1" = "--except" ]; then + except=1 + shift 1 + fi + + # + # Loop over a list of variable names from set(1) built-in. + # + for var in $( set | awk -F= \ + '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' \ + | grep -v '^except$' + ); do + # + # In POSIX bourne-shell, attempting to unset(1) OPTIND results + # in "unset: Illegal number:" and causes abrupt termination. + # + [ "$var" = OPTIND ] && continue + + # + # Process the exclusion-list? + # + if [ "$except" ]; then + for arg in "$@" ""; do + [ "$var" = "$arg" ] && break + done + [ "$arg" ] && continue + fi + + unset "$var" + done +} + +# f_sysrc_get $varname +# +# Get a system configuration setting from the collection of system- +# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf +# and /etc/rc.conf). +# +# NOTE: Additional shell parameter-expansion formats are supported. For +# example, passing an argument of "hostname%%.*" (properly quoted) will +# return the hostname up to (but not including) the first `.' (see sh(1), +# "Parameter Expansion" for more information on additional formats). +# +f_sysrc_get() +{ + # Sanity check + [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE + + # Taint-check variable name + case "$1" in + [0-9]*) + # Don't expand possible positional parameters + return $FAILURE;; + *) + [ "$1" ] || return $FAILURE + esac + + ( # Execute within sub-shell to protect parent environment + + # + # Clear the environment of all variables, preventing the + # expansion of normals such as `PS1', `TERM', etc. + # + f_clean_env --except RC_CONFS RC_DEFAULTS SUCCESS + + . "$RC_DEFAULTS" > /dev/null 2>&1 + + unset RC_DEFAULTS + # no longer needed + + # + # If the query is for `rc_conf_files' then store the value that + # we inherited from sourcing RC_DEFAULTS (above) so that we may + # conditionally restore this value after source_rc_confs in the + # event that RC_CONFS does not customize the value. + # + if [ "$1" = "rc_conf_files" ]; then + _rc_conf_files="$rc_conf_files" + fi + + # + # If RC_CONFS is defined, set $rc_conf_files to an explicit + # value, modifying the default behavior of source_rc_confs(). + # + ( : ${RC_CONFS?} ) > /dev/null 2>&1 + if [ $? -eq ${SUCCESS:-0} ]; then + rc_conf_files="$RC_CONFS" + _rc_confs_set=1 + fi + + unset SUCCESS + # no longer needed + + source_rc_confs > /dev/null 2>&1 + + # + # If the query was for `rc_conf_files' AND after calling + # source_rc_confs the vaue has not changed, then we should + # restore the value to the one inherited from RC_DEFAULTS + # before performing the final query (preventing us from + # returning what was set via RC_CONFS when the intent was + # instead to query the value from the file(s) specified). + # + if [ "$1" = "rc_conf_files" -a \ + "$_rc_confs_set" -a \ + "$rc_conf_files" = "$RC_CONFS" \ + ]; then + rc_conf_files="$_rc_conf_files" + unset _rc_conf_files + unset _rc_confs_set + fi + + unset RC_CONFS + # no longer needed + + # + # This must be the last functional line for both the sub-shell + # and the function to preserve the return status from formats + # such as "${varname?}" and "${varname:?}" (see "Parameter + # Expansion" in sh(1) for more information). + # + eval echo '"${'"$1"'}"' 2> /dev/null + ) +} + +# f_sysrc_get_default $varname +# +# Get a system configuration default setting from the default rc.conf(5) file +# (or whatever RC_DEFAULTS points at). +# +f_sysrc_get_default() +{ + # Sanity check + [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ] || return $FAILURE + + # Taint-check variable name + case "$1" in + [0-9]*) + # Don't expand possible positional parameters + return $FAILURE;; + *) + [ "$1" ] || return $FAILURE + esac + + ( # Execute within sub-shell to protect parent environment + + # + # Clear the environment of all variables, preventing the + # expansion of normals such as `PS1', `TERM', etc. + # + f_clean_env --except RC_DEFAULTS + + . "$RC_DEFAULTS" > /dev/null 2>&1 + + unset RC_DEFAULTS + # no longer needed + + # + # This must be the last functional line for both the sub-shell + # and the function to preserve the return status from formats + # such as "${varname?}" and "${varname:?}" (see "Parameter + # Expansion" in sh(1) for more information). + # + eval echo '"${'"$1"'}"' 2> /dev/null + ) +} + +# f_sysrc_find $varname +# +# Find which file holds the effective last-assignment to a given variable +# within the rc.conf(5) file(s). +# +# If the variable is found in any of the rc.conf(5) files, the function prints +# the filename it was found in and then returns success. Otherwise output is +# NULL and the function returns with error status. +# +f_sysrc_find() +{ + local varname="$1" + local regex="^[[:space:]]*$varname=" + local rc_conf_files="$( f_sysrc_get rc_conf_files )" + local conf_files= + local file + + # Check parameters + [ "$varname" ] || return $FAILURE + + # + # If RC_CONFS is defined, set $rc_conf_files to an explicit + # value, modifying the default behavior of source_rc_confs(). + # + [ "$RC_CONFS" ] && rc_conf_files="$RC_CONFS" + + # + # Reverse the order of files in rc_conf_files (the boot process sources + # these in order, so we will search them in reverse-order to find the + # last-assignment -- the one that ultimately effects the environment). + # + for file in $rc_conf_files; do + conf_files="$file${conf_files:+ }$conf_files" + done + + # + # Append the defaults file (since directives in the defaults file + # indeed affect the boot process, we'll want to know when a directive + # is found there). + # + conf_files="$conf_files${conf_files:+ }$RC_DEFAULTS" + + # + # Find which file matches assignment to the given variable name. + # + for file in $conf_files; do + [ -f "$file" -a -r "$file" ] || continue + if grep -Eq "$regex" $file; then + echo $file + return $SUCCESS + fi + done + + return $FAILURE # Not found +} + +# f_sysrc_desc $varname +# +# Attempts to return the comments associated with varname from the rc.conf(5) +# defaults file `/etc/defaults/rc.conf' (or whatever RC_DEFAULTS points to). +# +# Multi-line comments are joined together. Results are NULL if no description +# could be found. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sysrc_desc_awk=' +# Variables that should be defined on the invocation line: +# -v varname="varname" +# +BEGIN { + regex = "^[[:space:]]*"varname"=" + found = 0 + buffer = "" +} +{ + if ( ! found ) + { + if ( ! match($0, regex) ) next + + found = 1 + sub(/^[^#]*(#[[:space:]]*)?/, "") + buffer = $0 + next + } + + if ( !/^[[:space:]]*#/ || + /^[[:space:]]*[[:alpha:]_][[:alnum:]_]*=/ || + /^[[:space:]]*#[[:alpha:]_][[:alnum:]_]*=/ || + /^[[:space:]]*$/ ) exit + + sub(/(.*#)*[[:space:]]*/, "") + buffer = buffer" "$0 +} +END { + # Clean up the buffer + sub(/^[[:space:]]*/, "", buffer) + sub(/[[:space:]]*$/, "", buffer) + + print buffer + exit ! found +} +' +f_sysrc_desc() +{ + awk -v varname="$1" "$f_sysrc_desc_awk" < "$RC_DEFAULTS" +} + +# f_sysrc_set $varname $new_value +# +# Change a setting in the system configuration files (edits the files in-place +# to change the value in the last assignment to the variable). If the variable +# does not appear in the source file, it is appended to the end of the primary +# system configuration file `/etc/rc.conf'. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sysrc_set_awk=' +# Variables that should be defined on the invocation line: +# -v varname="varname" +# -v new_value="new_value" +# +BEGIN { + regex = "^[[:space:]]*"varname"=" + found = retval = 0 +} +{ + # If already found... just spew + if ( found ) { print; next } + + # Does this line match an assignment to our variable? + if ( ! match($0, regex) ) { print; next } + + # Save important match information + found = 1 + matchlen = RSTART + RLENGTH - 1 + + # Store the value text for later munging + value = substr($0, matchlen + 1, length($0) - matchlen) + + # Store the first character of the value + t1 = t2 = substr(value, 0, 1) + + # Assignment w/ back-ticks, expression, or misc. + # We ignore these since we did not generate them + # + if ( t1 ~ /[`$\\]/ ) { retval = 1; print; next } + + # Assignment w/ single-quoted value + else if ( t1 == "'\''" ) { + sub(/^'\''[^'\'']*/, "", value) + if ( length(value) == 0 ) t2 = "" + sub(/^'\''/, "", value) + } + + # Assignment w/ double-quoted value + else if ( t1 == "\"" ) { + sub(/^"(.*\\\\+")*[^"]*/, "", value) + if ( length(value) == 0 ) t2 = "" + sub(/^"/, "", value) + } + + # Assignment w/ non-quoted value + else if ( t1 ~ /[^[:space:];]/ ) { + t1 = t2 = "\"" + sub(/^[^[:space:]]*/, "", value) + } + + # Null-assignment + else if ( t1 ~ /[[:space:];]/ ) { t1 = t2 = "\"" } + + printf "%s%c%s%c%s\n", substr($0, 0, matchlen), \ + t1, new_value, t2, value +} +END { exit retval } +' +f_sysrc_set() +{ + local varname="$1" new_value="$2" + + # Check arguments + [ "$varname" ] || return $FAILURE + + # + # Find which rc.conf(5) file contains the last-assignment + # + local not_found= + local file="$( f_sysrc_find "$varname" )" + if [ "$file" = "$RC_DEFAULTS" -o ! "$file" ]; then + # + # We either got a null response (not found) or the variable + # was only found in the rc.conf(5) defaults. In either case, + # let's instead modify the first file from $rc_conf_files. + # + + not_found=1 + + # + # If RC_CONFS is defined, use $RC_CONFS + # rather than $rc_conf_files. + # + if [ "$RC_CONFS" ]; then + file="${RC_CONFS%%[$IFS]*}" + else + file=$( f_sysrc_get rc_conf_files ) + file="${file%%[$IFS]*}" + fi + fi + + # + # If not found, append new value to last file and return. + # + if [ "$not_found" ]; then + echo "$varname=\"$new_value\"" >> "$file" + return $? + fi + + # + # Perform sanity checks. + # + if [ ! -w "$file" ]; then + f_err "$msg_cannot_create_permission_denied\n" \ + "$pgm" "$file" + return $FAILURE + fi + + # + # Create a new temporary file to write to. + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions (else we're in for a surprise, as mktemp(1) creates + # the temporary file with 0600 permissions, and if we simply mv(1) the + # temporary file over the destination, the destination will inherit the + # permissions from the temporary file). + # + local mode + mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) + f_quietly chmod "${mode:-0644}" "$tmpfile" + + # + # Fixup ownership. The destination file _is_ writable (we tested + # earlier above). However, this will fail if we don't have sufficient + # permissions (so we throw stderr into the bit-bucket). + # + local owner + owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on the matching file, replacing only the last occurrence. + # + local new_contents retval + new_contents=$( tail -r $file 2> /dev/null ) + new_contents=$( echo "$new_contents" | awk -v varname="$varname" \ + -v new_value="$new_value" "$f_sysrc_set_awk" ) + retval=$? + + # + # Write the temporary file contents. + # + echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE + if [ $retval -ne $SUCCESS ]; then + echo "$varname=\"$new_value\"" >> "$tmpfile" + fi + + # + # Taint-check our results. + # + if ! /bin/sh -n "$tmpfile"; then + f_err "$msg_previous_syntax_errors\n" "$pgm" "$file" + rm -f "$tmpfile" + return $FAILURE + fi + + # + # Finally, move the temporary file into place. + # + mv "$tmpfile" "$file" +} + +# f_sysrc_delete $varname +# +# Remove a setting from the system configuration files (edits files in-place). +# Deletes all assignments to the given variable in all config files. If the +# `-f file' option is passed, the removal is restricted to only those files +# specified, otherwise the system collection of rc_conf_files is used. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sysrc_delete_awk=' +# Variables that should be defined on the invocation line: +# -v varname="varname" +# +BEGIN { + regex = "^[[:space:]]*"varname"=" + found = 0 +} +{ + if ( $0 ~ regex ) + found = 1 + else + print +} +END { exit ! found } +' +f_sysrc_delete() +{ + local varname="$1" + local file + + # Check arguments + [ "$varname" ] || return $FAILURE + + # + # Operate on each of the specified files + # + for file in ${RC_CONFS:-$( f_sysrc_get rc_conf_files )}; do + [ -e "$file" ] || continue + + # + # Create a new temporary file to write to. + # + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # + # Fixup permissions and ownership (mktemp(1) defaults to 0600 + # permissions) to instead match the destination file. + # + local mode owner + mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) + f_quietly chmod "${mode:-0644}" "$tmpfile" + f_quietly chown "${owner:-root:wheel}" "$tmpfile" + + # + # Operate on the file, removing all occurrences, saving the + # output in our temporary file. + # + awk -v varname="$varname" "$f_sysrc_delete_awk" "$file" \ + > "$tmpfile" + if [ $? -ne $SUCCESS ]; then + # The file didn't contain any assignments + rm -f "$tmpfile" + continue + fi + + # + # Taint-check our results. + # + if ! /bin/sh -n "$tmpfile"; then + f_err "$msg_previous_syntax_errors\n" \ + "$pgm" "$file" + rm -f "$tmpfile" + return $FAILURE + fi + + # + # Perform sanity checks + # + if [ ! -w "$file" ]; then + f_err "$msg_permission_denied\n" "$pgm" "$file" + rm -f "$tmpfile" + return $FAILURE + fi + + # + # Finally, move the temporary file into place. + # + mv "$tmpfile" "$file" + done +} + +fi # ! $_SYSRC_SUBR diff --git a/usr.sbin/bsdconfig/startup/Makefile b/usr.sbin/bsdconfig/startup/Makefile index dc198da33e286..0bba51014f4bf 100644 --- a/usr.sbin/bsdconfig/startup/Makefile +++ b/usr.sbin/bsdconfig/startup/Makefile @@ -2,7 +2,7 @@ NO_OBJ= -SUBDIR= include +SUBDIR= include share FILESDIR= ${LIBEXECDIR}/bsdconfig/140.startup FILES= INDEX USAGE diff --git a/usr.sbin/bsdconfig/startup/include/Makefile b/usr.sbin/bsdconfig/startup/include/Makefile index c155eb0b3492d..1634314324559 100644 --- a/usr.sbin/bsdconfig/startup/include/Makefile +++ b/usr.sbin/bsdconfig/startup/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/140.startup/include -FILES= messages.subr rcconf.subr rcedit.subr rcvar.subr +FILES= messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/startup/include/rcconf.subr b/usr.sbin/bsdconfig/startup/include/rcconf.subr deleted file mode 100644 index 88bed495b45c2..0000000000000 --- a/usr.sbin/bsdconfig/startup/include/rcconf.subr +++ /dev/null @@ -1,466 +0,0 @@ -if [ ! "$_STARTUP_RCCONF_SUBR" ]; then _STARTUP_RCCONF_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="140.startup" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ GLOBALS - -# -# Initialize in-memory cache variables -# -STARTUP_RCCONF_MAP= -_STARTUP_RCCONF_MAP= - -# -# Define what a variable looks like -# -STARTUP_RCCONF_REGEX="^[[:alpha:]_][[:alnum:]_]*=" - -# -# Default path to on-disk cache file(s) -# -STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" - -############################################################ FUNCTIONS - -# f_startup_rcconf_list -# -# Produce a list of non-default configuration variables configured in the -# rc.conf(5) collection of files. -# -f_startup_rcconf_list() -{ - ( # Operate within a sub-shell to protect the parent environment - . "$RC_DEFAULTS" > /dev/null - f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files - source_rc_confs > /dev/null - export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" - export RC_DEFAULTS - set | awk -F= " - function test_print(var) - { - if ( var == \"OPTIND\" ) return - if ( var == \"PATH\" ) return - if ( var == \"RC_DEFAULTS\" ) return - if ( var == \"STARTUP_RCCONF_REGEX\" ) return - if ( var == \"_rc_conf_files_file\" ) return - if ( var == \"rc_conf_files\" ) - { - if ( ENVIRON[\"_rc_conf_files_file\"] == \ - ENVIRON[\"RC_DEFAULTS\"] ) return - } - print var - } - /$STARTUP_RCCONF_REGEX/ { test_print(\$1) }" - ) -} - -# f_startup_rcconf_map -# -# Produce a map (beit from in-memory cache or on-disk cache) of rc.conf(5) -# variables and their descriptions. The map returned has the following format: -# -# var description -# -# With each as follows: -# -# var the rc.conf(5) variable -# description description of the variable -# -f_startup_rcconf_map() -{ - # If the in-memory cached value is available, return it immediately - if [ "$_STARTUP_RCCONF_MAP" ]; then - echo "$STARTUP_RCCONF_MAP" - return $SUCCESS - fi - - # - # create the in-memory cache (potentially from validated on-disk cache) - # - - # - # Calculate digest used to determine if the on-disk global persistant - # cache file (containg this digest on the first line) is valid and can - # be used to quickly populate the cache value for immediate return. - # - local rc_defaults_digest - rc_defaults_digest=$( md5 < "$RC_DEFAULTS" ) - - # - # Check to see if the global persistant cache file exists - # - if [ -f "$STARTUP_RCCONF_MAP_CACHEFILE" ]; then - # - # Attempt to populate the in-memory cache with the (soon to be) - # be validated on-disk cache. If validation fails, fall-back to - # the current value and provide error exit status. - # - STARTUP_RCCONF_MAP=$( - ( # Get digest as the first word on the first line - read digest rest_ignored - - # - # If the stored digest matches the calculated- - # one populate the in-memory cache from the on- - # disk cache and provide success exit status. - # - if [ "$digest" = "$rc_defaults_digest" ]; then - cat - exit $SUCCESS - else - # Otherwise, return the current value - echo "$STARTUP_RCCONF_MAP" - exit $FAILURE - fi - ) < "$STARTUP_RCCONF_MAP_CACHEFILE" - ) - export STARTUP_RCCONF_MAP - if [ $? -eq $SUCCESS ]; then - export _STARTUP_RCCONF_MAP=1 - echo "$STARTUP_RCCONF_MAP" - return $SUCCESS - fi - # Otherwise, fall-thru to create in-memory cache from scratch - fi - - # - # If we reach this point, we need to generate the data from scratch - # (and after we do, we'll attempt to create the global persistant - # cache file to speed up future executions). - # - - STARTUP_RCCONF_MAP=$( - f_clean_env --except \ - PATH \ - RC_DEFAULTS \ - STARTUP_RCCONF_REGEX \ - f_sysrc_desc_awk - . "$RC_DEFAULTS" - - # Unset variables we don't want reported - unset source_rc_confs_defined - - for var in $( set | awk -F= " - function test_print(var) - { - if ( var == \"OPTIND\" ) return - if ( var == \"PATH\" ) return - if ( var == \"RC_DEFAULTS\" ) return - if ( var == \"STARTUP_RCCONF_REGEX\" ) return - if ( var == \"f_sysrc_desc_awk\" ) return - print var - } - /$STARTUP_RCCONF_REGEX/ { test_print(\$1) } - " ); do - echo $var "$( f_sysrc_desc $var )" - done - ) - export STARTUP_RCCONF_MAP - export _STARTUP_RCCONF_MAP=1 - echo "$STARTUP_RCCONF_MAP" - - # - # Attempt to create the persistant global cache - # - - # Create a new temporary file to write to - local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE - - # Write the temporary file contents - echo "$rc_defaults_digest" > "$tmpfile" - echo "$STARTUP_RCCONF_MAP" >> "$tmpfile" - - # Finally, move the temporary file into place - case "$STARTUP_RCCONF_MAP_CACHEFILE" in - */*) f_quietly mkdir -p "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" - esac - mv "$tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" -} - -# f_startup_rcconf_map_expand -# -# Expands the map ($RCCONF_MAP) into the shell environment namespace by -# creating _${var}_desc variables containing the description of each variable -# encountered. -# -# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. -# -f_startup_rcconf_map_expand() -{ - eval "$( echo "$RCCONF_MAP" | awk ' - BEGIN { - rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" - } - { - var = $1 - desc = $0 - sub(rword, "", desc) - gsub(/'\''/, "'\''\\'\'\''", desc) - printf "_%s_desc='\''%s'\''\n", var, desc - printf "export _%s_desc\n", var - }' )" -} - -# f_dialog_input_view_details -# -# Display a menu for selecting which details are to be displayed. The following -# variables are tracked/modified by the menu/user's selection: -# -# SHOW_DESC Show or hide descriptions -# -# Mutually exclusive options: -# -# SHOW_VALUE Show the value (default; override only) -# SHOW_VALUE_DEFAULT Show both value and default -# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in -# -# Each variable is treated as a boolean (NULL for false, non-NULL for true). -# -# Variables are exported for later-required awk(1) ENVIRON visibility. -# -f_dialog_input_view_details() -{ - local menu_list size - local hline="$hline_arrows_tab_enter" - local prompt="" - - local md=" " m1=" " m2=" " m3=" " - [ "$SHOW_DESC" ] && md="X" - [ "$SHOW_VALUE" ] && m1="*" - [ "$SHOW_DEFAULT_VALUE" ] && m2="*" - [ "$SHOW_CONFIGURED" ] && m3="*" - - menu_list=" - 'X $msg_exit' '$msg_exit_this_menu' - 'R $msg_reset' '$msg_reset_desc' - 'D [$md] $msg_desc' '$msg_desc_desc' - '1 ($m1) $msg_show_value' '$msg_show_value_desc' - '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' - '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' - " # END-QUOTE - - size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list ) - - f_dialog_title "$msg_choose_view_details" - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$prompt\" $size \ - $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" - - local retval=$? - local mtag="$( f_dialog_menutag )" - - f_dialog_title_restore - - [ $retval -eq 0 ] || return $SUCCESS - [ "$mtag" = "X $msg_exit" ] && return $SUCCESS - - case "$mtag" in - "R $msg_reset") - SHOW_VALUE=1 - SHOW_DESC=1 - SHOW_DEFAULT_VALUE= - SHOW_CONFIGURED= - ;; - "D [X] $msg_desc") SHOW_DESC= ;; - "D [ ] $msg_desc") SHOW_DESC=1 ;; - "1 ("?") $msg_show_value") - SHOW_VALUE=1 - SHOW_DEFAULT_VALUE= - SHOW_CONFIGURED= - ;; - "2 ("?") $msg_show_default_value") - SHOW_VALUE= - SHOW_DEFAULT_VALUE=1 - SHOW_CONFIGURED= - ;; - "3 ("?") $msg_show_configured") - SHOW_VALUE= - SHOW_DEFAULT_VALUE= - SHOW_CONFIGURED=1 - ;; - esac -} - -# f_dialog_input_rclist -# -# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This -# function should be treated like a call to dialog(1) (the exit status should -# be captured and f_dialog_menutag() should be used to clean-up and get the -# user's response). -# -f_dialog_input_rclist() -{ - local size - local hline="$hline_arrows_tab_enter" - local prompt="$msg_please_select_an_rcconf_directive" - local menu_list - - menu_list=" - 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_help'} - " # END-QUOTE - - if [ ! "$_RCCONF_MAP" ]; then - # Generate RCCONF_MAP of `var desc ...' per-line - f_dialog_info "$msg_creating_rcconf_map" - RCCONF_MAP=$( f_startup_rcconf_map ) - export RCCONF_MAP - # Generate _${var}_desc variables from $RCCONF_MAP - f_startup_rcconf_map_expand - export _RCCONF_MAP=1 - fi - - menu_list="$menu_list $( - export SHOW_DESC - echo "$RCCONF_MAP" | awk ' - BEGIN { - prefix = "" - rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" - } - { - cur_prefix = tolower(substr($1, 1, 1)) - printf "'\''" - if ( prefix != cur_prefix ) - prefix = cur_prefix - else - printf " " - rcvar = $1 - printf "%s'\'' '\'\''", rcvar - if ( ENVIRON["SHOW_DESC"] ) { - desc = $0 - sub(rword, "", desc) - gsub(/'\''/, "'\''\\'\'\''", desc) - printf " '\''%s'\''", desc - } - printf "\n" - }' - )" - - set -f # noglob - - size=$( eval f_dialog_menu_${SHOW_DESC:+with_help_}size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - ${SHOW_DESC:+--item-help} \ - --menu \"\$prompt\" $size \ - $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" -} - -# f_dialog_input_rcvar [$init] -# -# Allows the user to enter the name for a new rc.conf(5) variable. If the user -# does not cancel or press ESC, the $rcvar variable will hold the newly- -# configured value upon return. -# -f_dialog_input_rcvar() -{ - local msg="$msg_please_enter_rcvar_name" - local hline="$hline_alnum_tab_enter" - - # - # Loop until the user provides taint-free/valid input - # - local size _input="$1" - while :; do - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Check for invalid entry (1of2) - if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then - f_show_msg "$msg_rcvar_must_start_with" - continue - fi - - # Check for invalid entry (2of2) - if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" - then - f_show_msg "$msg_rcvar_contains_invalid_chars" - continue - fi - - rcvar="$_input" - break - done - - f_dprintf "f_dialog_input_rcvar: rcvar->[$rcvar]" - - return $SUCCESS -} - -fi # ! $_STARTUP_RCCONF_SUBR diff --git a/usr.sbin/bsdconfig/startup/include/rcedit.subr b/usr.sbin/bsdconfig/startup/include/rcedit.subr deleted file mode 100644 index 325f1fe7e3bad..0000000000000 --- a/usr.sbin/bsdconfig/startup/include/rcedit.subr +++ /dev/null @@ -1,106 +0,0 @@ -if [ ! "$_STARTUP_RCEDIT_SUBR" ]; then _STARTUP_RCEDIT_SUBR=1 -# -# Copyright (c) 2012 Devin Teske -# 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. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="140.startup" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_dialog_rcedit $var [[--] $init ...] -# -# Allow the user to enter a new value for a given rc.conf(5) variable. If the -# user does not cancel or press ESC, the variable will be saved without -# confirmation. -# -# If the second argument is non-NULL, it will be processed as the initial text -# to be displayed, overriding the default behavior to display the currently -# configured value as the initial text. -# -# If instead the second argument is "--", then the third argument (NULL or -# otherwise) will be treated as the initial text. -# -f_dialog_rcedit() -{ - local msg size retval var="$1" _input - local hline="$hline_alnum_punc_tab_enter" - - msg=$( printf "$msg_please_enter_a_new_value" \ - "$var" "$( f_sysrc_get_default "$var" )" ) - - shift 1 # var - if [ "$1" ]; then - [ "$1" = "--" ] && shift 1 # -- - _input="$1" - else - _input=$( f_sysrc_get "$var" ) - fi - - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - local opterm="--" - [ "$USE_XDIALOG" ] && opterm= - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - $opterm \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Return if the value has not changed from current - local cur_val="$( f_sysrc_get "$var" )" - [ "$_input" = "$cur_val" ] && return $SUCCESS - - f_dprintf "$var: [$cur_val]->[$_input]" - - err=$( f_sysrc_set "$var" "$_input" 2>&1 ) || - f_show_msg "$err" -} - -fi # ! $_STARTUP_RCEDIT_SUBR diff --git a/usr.sbin/bsdconfig/startup/include/rcvar.subr b/usr.sbin/bsdconfig/startup/include/rcvar.subr deleted file mode 100644 index c7ad74d5315f0..0000000000000 --- a/usr.sbin/bsdconfig/startup/include/rcvar.subr +++ /dev/null @@ -1,206 +0,0 @@ -if [ ! "$_STARTUP_RCVAR_SUBR" ]; then _STARTUP_RCVAR_SUBR=1 -# -# Copyright (c) 2006-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/sysrc.subr - -############################################################ CONFIGURATION - -# -# Default path to the `/etc/rc.d' directory where service(8) scripts are stored -# -: ${ETC_RC_D:=/etc/rc.d} - -# -# Default path to `/etc/rc.subr' (for find_local_scripts_new()) -# -: ${ETC_RC_SUBR:=/etc/rc.subr} - -############################################################ GLOBALS - -# -# Initialize in-memory cache variables -# -STARTUP_RCVAR_MAP= -_STARTUP_RCVAR_MAP= - -# -# Define what an rcvar looks like -# -STARTUP_RCVAR_REGEX='[[:alpha:]_][[:alnum:]_]*="([Yy][Ee][Ss]|[Nn][Oo])"' - -# -# Default path to on-disk cache file(s) -# -STARTUP_RCVAR_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcvar_map.cache" - -############################################################ FUNCTIONS - -# f_startup_rcvar_map -# -# Produce a map (beit from in-memory cache or on-disk cache) of rc.d scripts -# and their associated rcvar's. The map returned has the following format: -# -# rcvar default script description -# -# With each as follows: -# -# rcvar the variable used to enable this rc.d script -# default default value for this variable -# script the rc.d script in-question -# description description of the variable from rc.conf(5) defaults -# -f_startup_rcvar_map() -{ - # If the in-memory cached value is available, return it immediately - if [ "$_STARTUP_RCVAR_MAP" ]; then - echo "$STARTUP_RCVAR_MAP" - return $SUCCESS - fi - - # - # create the in-memory cache (potentially from validated on-disk cache) - # - - # Get a list of /etc/rc.d scripts ... - local file rc_script_list= - for file in "$ETC_RC_D"/*; do - [ -f "$file" ] || continue - [ -x "$file" ] || continue - rc_script_list="$rc_script_list${rc_script_list:+ }$file" - done - # ... and /usr/local/etc/rc.d scripts - rc_script_list="$rc_script_list${rc_script_list:+ }$( - local_startup=$( f_sysrc_get local_startup ) - f_include "$ETC_RC_SUBR" - find_local_scripts_new - echo $local_rc - )" - - # - # Calculate a digest given the checksums of all dependencies (scripts and - # the defaults file). This digest will be used to determine if an on-disk - # global persistant cache file (containg this digest on the first line) - # is valid and can be used to quickly populate the cache value for - # immediate return. - # - local rc_script_list_digest - rc_script_list_digest=$( cd "$ETC_RC_D" && - cksum "$RC_DEFAULTS" $rc_script_list | md5 ) - - # - # Check to see if the global persistant cache file exists - # - if [ -f "$STARTUP_RCVAR_MAP_CACHEFILE" ]; then - # - # Attempt to populate the in-memory cache with the (soon to be) - # be validated on-disk cache. If validation fails, fall-back to - # the current value and provide error exit status. - # - STARTUP_RCVAR_MAP=$( - ( # Get digest as the first word on the first line - read digest rest_ignored - - # - # If the stored digest matches the calculated-one - # populate the in-memory cache from the on-disk - # cache and provide success exit status. - # - if [ "$digest" = "$rc_script_list_digest" ]; then - cat - exit $SUCCESS - else - # Otherwise, return the current value - echo "$STARTUP_RCVAR_MAP" - exit $FAILURE - fi - ) < "$STARTUP_RCVAR_MAP_CACHEFILE" - ) - export STARTUP_RCVAR_MAP - if [ $? -eq $SUCCESS ]; then - export _STARTUP_RCVAR_MAP=1 - echo "$STARTUP_RCVAR_MAP" - return $SUCCESS - fi - # Otherwise, fall-through to create in-memory cache from scratch - fi - - # - # If we reach this point, we need to generate the data from scratch - # (and after we do, we'll attempt to create the global persistant - # cache file to speed up future executions). - # - - STARTUP_RCVAR_MAP=$( - for script in $rc_script_list; do - rcvar_list=$( $script rcvar | awk -F= \ - -v script="$script" ' - /^'"$STARTUP_RCVAR_REGEX"'/ { - if ( $2 ~ /^"[Yy][Ee][Ss]"$/ ) - print $1 ",YES" - else - print $1 ",NO" - }' ) - for entry in $rcvar_list; do - rcvar="${entry%%,*}" - rcvar_default=$( f_sysrc_get_default "$rcvar" ) - [ "$rcvar_default" ] || - rcvar_default="${entry#*,}" - rcvar_desc=$( f_sysrc_desc "$rcvar" ) - echo $rcvar ${rcvar_default:-NO} \ - $script "$rcvar_desc" - done - done | sort -u - ) - export STARTUP_RCVAR_MAP - export _STARTUP_RCVAR_MAP=1 - echo "$STARTUP_RCVAR_MAP" - - # - # Attempt to create the persistant global cache - # - - # Create a new temporary file to write to - local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE - - # Write the temporary file contents - echo "$rc_script_list_digest" > "$tmpfile" - echo "$STARTUP_RCVAR_MAP" >> "$tmpfile" - - # Finally, move the temporary file into place - case "$STARTUP_RCVAR_MAP_CACHEFILE" in - */*) f_quietly mkdir -p "${STARTUP_RCVAR_MAP_CACHEFILE%/*}" - esac - mv "$tmpfile" "$STARTUP_RCVAR_MAP_CACHEFILE" -} - -fi # ! $_STARTUP_RCVAR_SUBR diff --git a/usr.sbin/bsdconfig/startup/misc b/usr.sbin/bsdconfig/startup/misc index 1dd2694098839..f4a0e1a9de8f2 100755 --- a/usr.sbin/bsdconfig/startup/misc +++ b/usr.sbin/bsdconfig/startup/misc @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr -APP_DIR="140.startup" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/startup/rcadd b/usr.sbin/bsdconfig/startup/rcadd index 1fba829824cda..49138f546f14a 100755 --- a/usr.sbin/bsdconfig/startup/rcadd +++ b/usr.sbin/bsdconfig/startup/rcadd @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/startup/rcconf.subr -APP_DIR="140.startup" -f_include $BSDCFG_LIBE/$APP_DIR/include/rcconf.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index a18cad1f4f249..4deff5353db9b 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -28,14 +28,14 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="140.startup" -f_include $BSDCFG_LIBE/$APP_DIR/include/rcconf.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/startup/rcconf.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index acec48e599d58..bef9651fdcd2d 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -28,14 +28,14 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="140.startup" -f_include $BSDCFG_LIBE/$APP_DIR/include/rcconf.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/startup/rcconf.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/startup/rcedit b/usr.sbin/bsdconfig/startup/rcedit index 19d19410b04b8..c060fcb5a2778 100755 --- a/usr.sbin/bsdconfig/startup/rcedit +++ b/usr.sbin/bsdconfig/startup/rcedit @@ -28,13 +28,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/startup/rcedit.subr -APP_DIR="140.startup" -f_include $BSDCFG_LIBE/$APP_DIR/include/rcedit.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index e66f3bbb04a46..10e9baf712439 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -28,14 +28,14 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/sysrc.subr - -APP_DIR="140.startup" -f_include $BSDCFG_LIBE/$APP_DIR/include/rcvar.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/sysrc.subr +f_include $BSDCFG_SHARE/startup/rcvar.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/startup/share/Makefile b/usr.sbin/bsdconfig/startup/share/Makefile new file mode 100644 index 0000000000000..01c7d032af18e --- /dev/null +++ b/usr.sbin/bsdconfig/startup/share/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/bsdconfig/startup +FILES= rcconf.subr rcedit.subr rcvar.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr new file mode 100644 index 0000000000000..96659d05a0e98 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -0,0 +1,466 @@ +if [ ! "$_STARTUP_RCCONF_SUBR" ]; then _STARTUP_RCCONF_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/sysrc.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ GLOBALS + +# +# Initialize in-memory cache variables +# +STARTUP_RCCONF_MAP= +_STARTUP_RCCONF_MAP= + +# +# Define what a variable looks like +# +STARTUP_RCCONF_REGEX="^[[:alpha:]_][[:alnum:]_]*=" + +# +# Default path to on-disk cache file(s) +# +STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" + +############################################################ FUNCTIONS + +# f_startup_rcconf_list +# +# Produce a list of non-default configuration variables configured in the +# rc.conf(5) collection of files. +# +f_startup_rcconf_list() +{ + ( # Operate within a sub-shell to protect the parent environment + . "$RC_DEFAULTS" > /dev/null + f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files + source_rc_confs > /dev/null + export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" + export RC_DEFAULTS + set | awk -F= " + function test_print(var) + { + if ( var == \"OPTIND\" ) return + if ( var == \"PATH\" ) return + if ( var == \"RC_DEFAULTS\" ) return + if ( var == \"STARTUP_RCCONF_REGEX\" ) return + if ( var == \"_rc_conf_files_file\" ) return + if ( var == \"rc_conf_files\" ) + { + if ( ENVIRON[\"_rc_conf_files_file\"] == \ + ENVIRON[\"RC_DEFAULTS\"] ) return + } + print var + } + /$STARTUP_RCCONF_REGEX/ { test_print(\$1) }" + ) +} + +# f_startup_rcconf_map +# +# Produce a map (beit from in-memory cache or on-disk cache) of rc.conf(5) +# variables and their descriptions. The map returned has the following format: +# +# var description +# +# With each as follows: +# +# var the rc.conf(5) variable +# description description of the variable +# +f_startup_rcconf_map() +{ + # If the in-memory cached value is available, return it immediately + if [ "$_STARTUP_RCCONF_MAP" ]; then + echo "$STARTUP_RCCONF_MAP" + return $SUCCESS + fi + + # + # create the in-memory cache (potentially from validated on-disk cache) + # + + # + # Calculate digest used to determine if the on-disk global persistant + # cache file (containg this digest on the first line) is valid and can + # be used to quickly populate the cache value for immediate return. + # + local rc_defaults_digest + rc_defaults_digest=$( md5 < "$RC_DEFAULTS" ) + + # + # Check to see if the global persistant cache file exists + # + if [ -f "$STARTUP_RCCONF_MAP_CACHEFILE" ]; then + # + # Attempt to populate the in-memory cache with the (soon to be) + # be validated on-disk cache. If validation fails, fall-back to + # the current value and provide error exit status. + # + STARTUP_RCCONF_MAP=$( + ( # Get digest as the first word on the first line + read digest rest_ignored + + # + # If the stored digest matches the calculated- + # one populate the in-memory cache from the on- + # disk cache and provide success exit status. + # + if [ "$digest" = "$rc_defaults_digest" ]; then + cat + exit $SUCCESS + else + # Otherwise, return the current value + echo "$STARTUP_RCCONF_MAP" + exit $FAILURE + fi + ) < "$STARTUP_RCCONF_MAP_CACHEFILE" + ) + export STARTUP_RCCONF_MAP + if [ $? -eq $SUCCESS ]; then + export _STARTUP_RCCONF_MAP=1 + echo "$STARTUP_RCCONF_MAP" + return $SUCCESS + fi + # Otherwise, fall-thru to create in-memory cache from scratch + fi + + # + # If we reach this point, we need to generate the data from scratch + # (and after we do, we'll attempt to create the global persistant + # cache file to speed up future executions). + # + + STARTUP_RCCONF_MAP=$( + f_clean_env --except \ + PATH \ + RC_DEFAULTS \ + STARTUP_RCCONF_REGEX \ + f_sysrc_desc_awk + . "$RC_DEFAULTS" + + # Unset variables we don't want reported + unset source_rc_confs_defined + + for var in $( set | awk -F= " + function test_print(var) + { + if ( var == \"OPTIND\" ) return + if ( var == \"PATH\" ) return + if ( var == \"RC_DEFAULTS\" ) return + if ( var == \"STARTUP_RCCONF_REGEX\" ) return + if ( var == \"f_sysrc_desc_awk\" ) return + print var + } + /$STARTUP_RCCONF_REGEX/ { test_print(\$1) } + " ); do + echo $var "$( f_sysrc_desc $var )" + done + ) + export STARTUP_RCCONF_MAP + export _STARTUP_RCCONF_MAP=1 + echo "$STARTUP_RCCONF_MAP" + + # + # Attempt to create the persistant global cache + # + + # Create a new temporary file to write to + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # Write the temporary file contents + echo "$rc_defaults_digest" > "$tmpfile" + echo "$STARTUP_RCCONF_MAP" >> "$tmpfile" + + # Finally, move the temporary file into place + case "$STARTUP_RCCONF_MAP_CACHEFILE" in + */*) f_quietly mkdir -p "${STARTUP_RCCONF_MAP_CACHEFILE%/*}" + esac + mv "$tmpfile" "$STARTUP_RCCONF_MAP_CACHEFILE" +} + +# f_startup_rcconf_map_expand +# +# Expands the map ($RCCONF_MAP) into the shell environment namespace by +# creating _${var}_desc variables containing the description of each variable +# encountered. +# +# NOTE: Variables are exported for later-required awk(1) ENVIRON visibility. +# +f_startup_rcconf_map_expand() +{ + eval "$( echo "$RCCONF_MAP" | awk ' + BEGIN { + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + var = $1 + desc = $0 + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf "_%s_desc='\''%s'\''\n", var, desc + printf "export _%s_desc\n", var + }' )" +} + +# f_dialog_input_view_details +# +# Display a menu for selecting which details are to be displayed. The following +# variables are tracked/modified by the menu/user's selection: +# +# SHOW_DESC Show or hide descriptions +# +# Mutually exclusive options: +# +# SHOW_VALUE Show the value (default; override only) +# SHOW_VALUE_DEFAULT Show both value and default +# SHOW_CONFIGURED Show rc.conf(5) file variable is configured in +# +# Each variable is treated as a boolean (NULL for false, non-NULL for true). +# +# Variables are exported for later-required awk(1) ENVIRON visibility. +# +f_dialog_input_view_details() +{ + local menu_list size + local hline="$hline_arrows_tab_enter" + local prompt="" + + local md=" " m1=" " m2=" " m3=" " + [ "$SHOW_DESC" ] && md="X" + [ "$SHOW_VALUE" ] && m1="*" + [ "$SHOW_DEFAULT_VALUE" ] && m2="*" + [ "$SHOW_CONFIGURED" ] && m3="*" + + menu_list=" + 'X $msg_exit' '$msg_exit_this_menu' + 'R $msg_reset' '$msg_reset_desc' + 'D [$md] $msg_desc' '$msg_desc_desc' + '1 ($m1) $msg_show_value' '$msg_show_value_desc' + '2 ($m2) $msg_show_default_value' '$msg_show_default_value_desc' + '3 ($m3) $msg_show_configured' '$msg_show_configured_desc' + " # END-QUOTE + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + f_dialog_title "$msg_choose_view_details" + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" + + local retval=$? + local mtag="$( f_dialog_menutag )" + + f_dialog_title_restore + + [ $retval -eq 0 ] || return $SUCCESS + [ "$mtag" = "X $msg_exit" ] && return $SUCCESS + + case "$mtag" in + "R $msg_reset") + SHOW_VALUE=1 + SHOW_DESC=1 + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + ;; + "D [X] $msg_desc") SHOW_DESC= ;; + "D [ ] $msg_desc") SHOW_DESC=1 ;; + "1 ("?") $msg_show_value") + SHOW_VALUE=1 + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED= + ;; + "2 ("?") $msg_show_default_value") + SHOW_VALUE= + SHOW_DEFAULT_VALUE=1 + SHOW_CONFIGURED= + ;; + "3 ("?") $msg_show_configured") + SHOW_VALUE= + SHOW_DEFAULT_VALUE= + SHOW_CONFIGURED=1 + ;; + esac +} + +# f_dialog_input_rclist +# +# Presents a menu of rc.conf(5) defaults (with, or without descriptions). This +# function should be treated like a call to dialog(1) (the exit status should +# be captured and f_dialog_menutag() should be used to clean-up and get the +# user's response). +# +f_dialog_input_rclist() +{ + local size + local hline="$hline_arrows_tab_enter" + local prompt="$msg_please_select_an_rcconf_directive" + local menu_list + + menu_list=" + 'X $msg_exit' '' ${SHOW_DESC:+'$msg_exit_help'} + " # END-QUOTE + + if [ ! "$_RCCONF_MAP" ]; then + # Generate RCCONF_MAP of `var desc ...' per-line + f_dialog_info "$msg_creating_rcconf_map" + RCCONF_MAP=$( f_startup_rcconf_map ) + export RCCONF_MAP + # Generate _${var}_desc variables from $RCCONF_MAP + f_startup_rcconf_map_expand + export _RCCONF_MAP=1 + fi + + menu_list="$menu_list $( + export SHOW_DESC + echo "$RCCONF_MAP" | awk ' + BEGIN { + prefix = "" + rword = "^[[:space:]]*[^[:space:]]*[[:space:]]*" + } + { + cur_prefix = tolower(substr($1, 1, 1)) + printf "'\''" + if ( prefix != cur_prefix ) + prefix = cur_prefix + else + printf " " + rcvar = $1 + printf "%s'\'' '\'\''", rcvar + if ( ENVIRON["SHOW_DESC"] ) { + desc = $0 + sub(rword, "", desc) + gsub(/'\''/, "'\''\\'\'\''", desc) + printf " '\''%s'\''", desc + } + printf "\n" + }' + )" + + set -f # noglob + + size=$( eval f_dialog_menu_${SHOW_DESC:+with_help_}size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + ${SHOW_DESC:+--item-help} \ + --menu \"\$prompt\" $size \ + $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_input_rcvar [$init] +# +# Allows the user to enter the name for a new rc.conf(5) variable. If the user +# does not cancel or press ESC, the $rcvar variable will hold the newly- +# configured value upon return. +# +f_dialog_input_rcvar() +{ + local msg="$msg_please_enter_rcvar_name" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size _input="$1" + while :; do + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for invalid entry (1of2) + if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then + f_show_msg "$msg_rcvar_must_start_with" + continue + fi + + # Check for invalid entry (2of2) + if ! echo "$_input" | grep -q "^[[:alpha:]_][[:alnum:]_]*$" + then + f_show_msg "$msg_rcvar_contains_invalid_chars" + continue + fi + + rcvar="$_input" + break + done + + f_dprintf "f_dialog_input_rcvar: rcvar->[$rcvar]" + + return $SUCCESS +} + +fi # ! $_STARTUP_RCCONF_SUBR diff --git a/usr.sbin/bsdconfig/startup/share/rcedit.subr b/usr.sbin/bsdconfig/startup/share/rcedit.subr new file mode 100644 index 0000000000000..f0b5b59abe2e4 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/share/rcedit.subr @@ -0,0 +1,106 @@ +if [ ! "$_STARTUP_RCEDIT_SUBR" ]; then _STARTUP_RCEDIT_SUBR=1 +# +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/sysrc.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_dialog_rcedit $var [[--] $init ...] +# +# Allow the user to enter a new value for a given rc.conf(5) variable. If the +# user does not cancel or press ESC, the variable will be saved without +# confirmation. +# +# If the second argument is non-NULL, it will be processed as the initial text +# to be displayed, overriding the default behavior to display the currently +# configured value as the initial text. +# +# If instead the second argument is "--", then the third argument (NULL or +# otherwise) will be treated as the initial text. +# +f_dialog_rcedit() +{ + local msg size retval var="$1" _input + local hline="$hline_alnum_punc_tab_enter" + + msg=$( printf "$msg_please_enter_a_new_value" \ + "$var" "$( f_sysrc_get_default "$var" )" ) + + shift 1 # var + if [ "$1" ]; then + [ "$1" = "--" ] && shift 1 # -- + _input="$1" + else + _input=$( f_sysrc_get "$var" ) + fi + + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + local opterm="--" + [ "$USE_XDIALOG" ] && opterm= + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + $opterm \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Return if the value has not changed from current + local cur_val="$( f_sysrc_get "$var" )" + [ "$_input" = "$cur_val" ] && return $SUCCESS + + f_dprintf "$var: [$cur_val]->[$_input]" + + err=$( f_sysrc_set "$var" "$_input" 2>&1 ) || + f_show_msg "$err" +} + +fi # ! $_STARTUP_RCEDIT_SUBR diff --git a/usr.sbin/bsdconfig/startup/share/rcvar.subr b/usr.sbin/bsdconfig/startup/share/rcvar.subr new file mode 100644 index 0000000000000..3cd60e8d8b4b6 --- /dev/null +++ b/usr.sbin/bsdconfig/startup/share/rcvar.subr @@ -0,0 +1,206 @@ +if [ ! "$_STARTUP_RCVAR_SUBR" ]; then _STARTUP_RCVAR_SUBR=1 +# +# Copyright (c) 2006-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/sysrc.subr + +############################################################ CONFIGURATION + +# +# Default path to the `/etc/rc.d' directory where service(8) scripts are stored +# +: ${ETC_RC_D:=/etc/rc.d} + +# +# Default path to `/etc/rc.subr' (for find_local_scripts_new()) +# +: ${ETC_RC_SUBR:=/etc/rc.subr} + +############################################################ GLOBALS + +# +# Initialize in-memory cache variables +# +STARTUP_RCVAR_MAP= +_STARTUP_RCVAR_MAP= + +# +# Define what an rcvar looks like +# +STARTUP_RCVAR_REGEX='[[:alpha:]_][[:alnum:]_]*="([Yy][Ee][Ss]|[Nn][Oo])"' + +# +# Default path to on-disk cache file(s) +# +STARTUP_RCVAR_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcvar_map.cache" + +############################################################ FUNCTIONS + +# f_startup_rcvar_map +# +# Produce a map (beit from in-memory cache or on-disk cache) of rc.d scripts +# and their associated rcvar's. The map returned has the following format: +# +# rcvar default script description +# +# With each as follows: +# +# rcvar the variable used to enable this rc.d script +# default default value for this variable +# script the rc.d script in-question +# description description of the variable from rc.conf(5) defaults +# +f_startup_rcvar_map() +{ + # If the in-memory cached value is available, return it immediately + if [ "$_STARTUP_RCVAR_MAP" ]; then + echo "$STARTUP_RCVAR_MAP" + return $SUCCESS + fi + + # + # create the in-memory cache (potentially from validated on-disk cache) + # + + # Get a list of /etc/rc.d scripts ... + local file rc_script_list= + for file in "$ETC_RC_D"/*; do + [ -f "$file" ] || continue + [ -x "$file" ] || continue + rc_script_list="$rc_script_list${rc_script_list:+ }$file" + done + # ... and /usr/local/etc/rc.d scripts + rc_script_list="$rc_script_list${rc_script_list:+ }$( + local_startup=$( f_sysrc_get local_startup ) + f_include "$ETC_RC_SUBR" + find_local_scripts_new + echo $local_rc + )" + + # + # Calculate a digest given the checksums of all dependencies (scripts and + # the defaults file). This digest will be used to determine if an on-disk + # global persistant cache file (containg this digest on the first line) + # is valid and can be used to quickly populate the cache value for + # immediate return. + # + local rc_script_list_digest + rc_script_list_digest=$( cd "$ETC_RC_D" && + cksum "$RC_DEFAULTS" $rc_script_list | md5 ) + + # + # Check to see if the global persistant cache file exists + # + if [ -f "$STARTUP_RCVAR_MAP_CACHEFILE" ]; then + # + # Attempt to populate the in-memory cache with the (soon to be) + # be validated on-disk cache. If validation fails, fall-back to + # the current value and provide error exit status. + # + STARTUP_RCVAR_MAP=$( + ( # Get digest as the first word on the first line + read digest rest_ignored + + # + # If the stored digest matches the calculated-one + # populate the in-memory cache from the on-disk + # cache and provide success exit status. + # + if [ "$digest" = "$rc_script_list_digest" ]; then + cat + exit $SUCCESS + else + # Otherwise, return the current value + echo "$STARTUP_RCVAR_MAP" + exit $FAILURE + fi + ) < "$STARTUP_RCVAR_MAP_CACHEFILE" + ) + export STARTUP_RCVAR_MAP + if [ $? -eq $SUCCESS ]; then + export _STARTUP_RCVAR_MAP=1 + echo "$STARTUP_RCVAR_MAP" + return $SUCCESS + fi + # Otherwise, fall-through to create in-memory cache from scratch + fi + + # + # If we reach this point, we need to generate the data from scratch + # (and after we do, we'll attempt to create the global persistant + # cache file to speed up future executions). + # + + STARTUP_RCVAR_MAP=$( + for script in $rc_script_list; do + rcvar_list=$( $script rcvar | awk -F= \ + -v script="$script" ' + /^'"$STARTUP_RCVAR_REGEX"'/ { + if ( $2 ~ /^"[Yy][Ee][Ss]"$/ ) + print $1 ",YES" + else + print $1 ",NO" + }' ) + for entry in $rcvar_list; do + rcvar="${entry%%,*}" + rcvar_default=$( f_sysrc_get_default "$rcvar" ) + [ "$rcvar_default" ] || + rcvar_default="${entry#*,}" + rcvar_desc=$( f_sysrc_desc "$rcvar" ) + echo $rcvar ${rcvar_default:-NO} \ + $script "$rcvar_desc" + done + done | sort -u + ) + export STARTUP_RCVAR_MAP + export _STARTUP_RCVAR_MAP=1 + echo "$STARTUP_RCVAR_MAP" + + # + # Attempt to create the persistant global cache + # + + # Create a new temporary file to write to + local tmpfile="$( mktemp -t "$pgm" )" + [ "$tmpfile" ] || return $FAILURE + + # Write the temporary file contents + echo "$rc_script_list_digest" > "$tmpfile" + echo "$STARTUP_RCVAR_MAP" >> "$tmpfile" + + # Finally, move the temporary file into place + case "$STARTUP_RCVAR_MAP_CACHEFILE" in + */*) f_quietly mkdir -p "${STARTUP_RCVAR_MAP_CACHEFILE%/*}" + esac + mv "$tmpfile" "$STARTUP_RCVAR_MAP_CACHEFILE" +} + +fi # ! $_STARTUP_RCVAR_SUBR diff --git a/usr.sbin/bsdconfig/startup/startup b/usr.sbin/bsdconfig/startup/startup index 70e82e0981351..7bb5d90aa005d 100755 --- a/usr.sbin/bsdconfig/startup/startup +++ b/usr.sbin/bsdconfig/startup/startup @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="140.startup" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="140.startup" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/timezone/Makefile b/usr.sbin/bsdconfig/timezone/Makefile index f77a37b693fa4..b4ef0cbd025a6 100644 --- a/usr.sbin/bsdconfig/timezone/Makefile +++ b/usr.sbin/bsdconfig/timezone/Makefile @@ -2,7 +2,7 @@ NO_OBJ= -SUBDIR= include +SUBDIR= include share FILESDIR= ${LIBEXECDIR}/bsdconfig/090.timezone FILES= INDEX USAGE diff --git a/usr.sbin/bsdconfig/timezone/include/Makefile b/usr.sbin/bsdconfig/timezone/include/Makefile index a4c5ba1df3bea..887536eec0795 100644 --- a/usr.sbin/bsdconfig/timezone/include/Makefile +++ b/usr.sbin/bsdconfig/timezone/include/Makefile @@ -3,8 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/090.timezone/include -FILES= continents.subr countries.subr iso3166.subr menus.subr \ - messages.subr zones.subr +FILES= messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/timezone/include/continents.subr b/usr.sbin/bsdconfig/timezone/include/continents.subr deleted file mode 100644 index 126020cdf936d..0000000000000 --- a/usr.sbin/bsdconfig/timezone/include/continents.subr +++ /dev/null @@ -1,138 +0,0 @@ -if [ ! "$_TIMEZONE_CONTINENTS_SUBR" ]; then _TIMEZONE_CONTINENTS_SUBR=1 -# -# Copyright (c) 2011-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 - -APP_DIR="090.timezone" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ CONFIGURATION - -# -# List of worldly continents/oceans (export'ed for awk(1) ENVIRON visibility) -# -export CONTINENTS=" - africa - america - antarctica - arctic - asia - atlantic - australia - europe - indian - pacific - utc -" - -# -# Directory name of each continent/ocean (in _PATH_ZONEINFO) -# -export continent_africa_name="Africa" -export continent_america_name="America" -export continent_antarctica_name="Antarctica" -export continent_arctic_name="Arctic" -export continent_asia_name="Asia" -export continent_atlantic_name="Atlantic" -export continent_australia_name="Australia" -export continent_europe_name="Europe" -export continent_indian_name="Indian" -export continent_pacific_name="Pacific" -export continent_utc_name="UTC" - -# -# Export i18n menu texts of continents/oceans for awk(1) ENVIRON visibility -# NOTE: These are defined in messages.subr included above. -# -export continent_africa_title -export continent_america_title -export continent_antarctica_title -export continent_arctic_title -export continent_asia_title -export continent_atlantic_title -export continent_australia_title -export continent_europe_title -export continent_indian_title -export continent_pacific_title -export continent_utc_title - -############################################################ FUNCTIONS - -# f_continent $cont $property -# -# Returns a single property of a given continent. Available properties are: -# -# name Directory name of continent/ocean as it appears in -# _PATH_ZONEINFO. -# title Menu text of this continent/ocean to be displayed in the -# continent-selection menu. -# nitems Number of submenu items associated with this -# continent/ocean. -# tlc_N 2-character country code of the Nth submenu item associated -# with this continent displayed in the country-selection menu -# (which appears after continent selection). -# menu_list Menu-list of regions for this continent. -# -f_continent() -{ - local cont="$1" property="$2" - eval echo \"\${continent_${cont}_$property}\" -} - -# f_find_continent $title -# -# Returns continent identifier given continent title. -# -f_find_continent() -{ - local cont - for cont in $CONTINENTS; do - if [ "$1" = "$( f_continent $cont title )" ]; then - echo "$cont" - return $SUCCESS - fi - done - return $FAILURE -} - -# f_OCEANP $cont -# -# Returns "1" if the first argument is an ocean, otherwise NULL. -# -f_OCEANP() -{ - case "$1" in - arctic|atlantic|indian|pacific) - echo 1 - esac -} - -fi # ! $_TIMEZONE_CONTINENTS_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/countries.subr b/usr.sbin/bsdconfig/timezone/include/countries.subr deleted file mode 100644 index 9c6a164e213ab..0000000000000 --- a/usr.sbin/bsdconfig/timezone/include/countries.subr +++ /dev/null @@ -1,78 +0,0 @@ -if [ ! "$_TIMEZONE_COUNTRIES_SUBR" ]; then _TIMEZONE_COUNTRIES_SUBR=1 -# -# Copyright (c) 2011-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ - -# f_country $code $property -# -# Returns a single property of a given country. Available properties are: -# -# name Name of the country as read from _PATH_ISO3166. -# nzones Number of zones within the country (-1 if country has -# only a single zone). -# filename The filename portion of the TZ field (after the `/') as -# read from _PATH_ZONETAB. -# cont The principal continent in which the country lies (appears -# before the `/' in the TZ field of _PATH_ZONETAB). -# filename_N Like filename, but for the Nth zone when the country has -# multiple zones (nzones > 0). -# cont_N Like cont, but for the Nth zone when the country has -# multiple zones (nzones > 0). -# descr_N Like name, but for the Nth zone when the country has -# multiple zones (nzones > 0) -# -f_country() -{ - local code="$1" property="$2" - eval echo \"\${country_${code}_$property}\" -} - -# f_sort_countries -# -# Sorts alphabetically the 2-character country codes listed in $COUNTRIES based -# on the name of each country. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_sort_countries_awk=' -{ - split($0, array, /[[:space:]]+/) - for (item in array) - { - tlc = array[item] - print ENVIRON["country_" tlc "_name"] " " tlc - } -} -' -f_sort_countries() -{ - COUNTRIES=$( echo "$COUNTRIES" | awk "$f_sort_countries_awk" | - sort | awk '{print $NF}' ) - export COUNTRIES -} - -fi # ! $_TIMEZONE_COUNTRIES_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/iso3166.subr b/usr.sbin/bsdconfig/timezone/include/iso3166.subr deleted file mode 100644 index d69f3da4d0a37..0000000000000 --- a/usr.sbin/bsdconfig/timezone/include/iso3166.subr +++ /dev/null @@ -1,197 +0,0 @@ -if [ ! "$_TIMEZONE_ISO3166_SUBR" ]; then _TIMEZONE_ISO3166_SUBR=1 -# -# Copyright (c) 2011-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 - -APP_DIR="090.timezone" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ CONFIGURATION - -# -# Standard pathnames -# -_PATH_ISO3166="/usr/share/misc/iso3166" - -# -# Export required i18n messages for awk(1) ENVIRON visibility -# -export msg_country_code_multiply_defined -export msg_invalid_code -export msg_invalid_format - -############################################################ FUNCTIONS - -# f_read_iso3166_table -# -# Read the ISO 3166 country code database in _PATH_ISO3166: -# /usr/share/misc/iso3166 on FreeBSD -# /usr/share/zoneinfo/iso3166.tab on Linux, Mac OS X, and Cygwin -# -# The format of this file on FreeBSD is: -# two three number name -# -# The format of this file on Linux, Mac OS X, and Cygwin is: -# two name -# -# With each of the following elements (described below) being separated by a -# single tab character: -# -# two ISO 3166 2-character country code -# three ISO 3166 3-character country code (if provided) -# number ISO 3166 numeric country code (if provided) -# name Human-readable country name (may contain spaces) -# -# Variables created by this function: -# -# COUNTRIES -# A space-separated list of 2-character country codes. -# country_CODE_name -# The country `name' (as described above). -# -# where CODE is the 2-character country code. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_read_iso3166_table_awk=' -# Variables that should be defined on the invocation line: -# -v progname="progname" -# -BEGIN { - lineno = 0 - failed = 0 -} -function die(fmt, argc, argv) -{ - printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname - for (n = 1; n <= argc; n++) - printf " \"%s\"", argv[n] - print "" - failed++ - exit 1 -} -function add_country(tlc, name) -{ - if (country_name[tlc]) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = tlc - argv[4] = name - die(ENVIRON["msg_country_code_multiply_defined"], 4, argv) - } - - country_name[tlc] = name -} -function print_country_name(tlc) -{ - name = country_name[tlc] - gsub(/"/, "\\\"", name) - printf "country_%s_name=\"%s\"\n", tlc, name - printf "export country_%s_name\n", tlc -} -/^#/ { - lineno++ - next -} -!/^#/ { - lineno++ - - # Split the current record (on TAB) into an array - split($0, line, /\t/) - - # Get the ISO3166-1 (Alpha 1) 2-letter country code - tlc = line[1] - - # - # Validate the two-character country code - # - if (length(tlc) != 2) - { - argv[1] = FILENAME - argv[2] = lineno - die(ENVIRON["msg_invalid_format"], 2, argv) - } - if (!match(tlc, /^[A-Z][A-Z]$/)) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = tlc - die(ENVIRON["msg_invalid_code"], 3, argv) - } - - # - # Calculate the substr start-position of the name - # - name_start = 0 - n = 4 - if (FILENAME ~ /\.tab$/) - n = 2 - while (--n) - { - # - # Validate field-length of 2nd/3rd columns while we are here - # - if (n > 1 && length(line[n]) != 3) - { - argv[1] = FILENAME - argv[2] = lineno - die(ENVIRON["msg_invalid_format"], 2, argv) - } - - name_start += length(line[n]) + 1 - } - - # Get the name field - name = substr($0, name_start + 1) - - add_country(tlc, name) -} -END { - list = "" - for (tlc in country_name) - { - list = list (length(list) > 0 ? " " : "") tlc - print_country_name(tlc) - } - printf "COUNTRIES=\"%s\"\n", list - print "export COUNTRIES" -} -' -f_read_iso3166_table() -{ - eval $( awk -v progname="$pgm" \ - "$f_read_iso3166_table_awk" \ - "$_PATH_ISO3166" ) -} - -fi # ! $_TIMEZONE_ISO3166_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/menus.subr b/usr.sbin/bsdconfig/timezone/include/menus.subr deleted file mode 100644 index 46bdc0446b809..0000000000000 --- a/usr.sbin/bsdconfig/timezone/include/menus.subr +++ /dev/null @@ -1,220 +0,0 @@ -if [ ! "$_TIMEZONE_MENUS_SUBR" ]; then _TIMEZONE_MENUS_SUBR=1 -# -# Copyright (c) 2011-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr - -############################################################ GLOBALS - -# -# Export special included variables required by awk(1) for `ENVIRON' visibility -# -export DIALOG_MENU_TAGS - -############################################################ FUNCTIONS - -# f_make_menus -# -# Creates the tag/item ordered-pair list environment variables for the -# continent and country menus. -# -# Required variables [from continents.subr]: -# -# CONTINENTS -# Space-separated list of continents. -# continent_*_title -# Desired menu text for the continent represented by *. -# -# Required variables [created by f_read_iso3166_table from iso3166.subr]: -# -# COUNTRIES -# Space-separated list of 2-character country codes. -# country_*_name :: when country_*_nzones < 0 -# Desired menu text for the country-zone represented by *, the 2- -# character country code. -# -# Required variables [created by f_read_zones from zones.subr]: -# -# country_*_nzones -# Number of zones for the country represented by *, the 2- -# character country code. Should be -1 if the country has only -# one single zone, otherwise 1 or greater to indicate how many -# zones the country has. -# country_*_cont :: when country_*_nzones < 0 -# Principal continent (or ocean) in which the country-zone -# represented by *, the 2-character country code, resides. -# country_*_cont_N :: when country_*_nzones > 0 -# Principal continent (or ocean) in which zone-N of the country -# represented by * resides, the 2-character country code. -# country_*_descr_N :: when country_*_nzones > 0 -# Desired submenu text for zone-N of the country represented by -# *, the 2-character country code. -# -# Variables created by this function: -# -# continent_menu_list -# Menu-list of continents. -# continent_*_nitems -# Number of items associated with the continent represented by *, -# the continent identifier. -# continent_*_tlc_N -# 2-character country code of the Nth item in the continent menu -# for the continent represented by *, the continent identifier. -# continent_*_menu_list -# Menu-list of countries/zones for each continent represented by -# *, the continent identifier. -# country_*_menu_list -# For countries that have multiple zones, this is the submenu- -# list of zones for said country represented by *, the 2- -# character country code. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_make_menus_awk=' -function add_zone_n_to_country_menu(tlc, n) -{ - zone_title = ENVIRON["country_" tlc "_descr_" n] - gsub(/'\''/, "'\''\\'\'''\''", zone_title) - country_menu_list[tlc] = country_menu_list[tlc] \ - ( length(country_menu_list[tlc]) > 0 ? "\n" : "" ) \ - n " '\''" zone_title "'\''" -} -BEGIN { - # - # First, count up all the countries in each continent/ocean. - # Be careful to count those countries which have multiple zones - # only once for each. NB: some countries are in multiple - # continents/oceans. - # - i = split(ENVIRON["COUNTRIES"], countries, /[[:space:]]+/) - for (cp = 1; cp <= i; cp++) - { - tlc = countries[cp] - title = ENVIRON["country_" tlc "_name"] - gsub(/'\''/, "'\''\\'\'''\''", title) - nzones = ENVIRON["country_" tlc "_nzones"] - if (!nzones) - { - # Country has no zones - continue - } - else if (nzones < 0) - { - # Country has only one zone - cont = ENVIRON["country_" tlc "_cont"] - nitems = ++continent_nitems[cont] - continent_tlc[cont,nitems] = tlc - continent_title[cont,nitems] = title - } - else - { - # Country has one or more zones - for (n = 1; n <= nzones; n++) - { - add_zone_n_to_country_menu(tlc, n) - cont = ENVIRON["country_" tlc "_cont_" n] - for (x = 1; x < n; x++) - { - contx = ENVIRON["country_"tlc"_cont_"x] - if (cont == contx) break - } - if (x == n) - { - nitems = ++continent_nitems[cont] - continent_tlc[cont,nitems] = tlc - continent_title[cont,nitems] = title - } - } - } - } -} -END { - tags = ENVIRON["DIALOG_MENU_TAGS"] - cont_menu_list = "" - tagn = 0 - - # - # Assemble the menu items in the menu list for each continent/ocean. - # - i = split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) - for (item = 1; item <= i; item++) - { - cont = array[item] - if (!cont) continue - - if (++tagn >= length(tags)) break - tag = substr(tags, tagn, 1) - cont_menu_list = cont_menu_list \ - ( length(cont_menu_list) > 0 ? "\n" : "" ) \ - "'\''" tag "'\'' '\''" \ - ENVIRON["continent_" cont "_title"] "'\''" - - nitems = continent_nitems[cont] - printf "continent_%s_nitems=%d\n", cont, nitems - - menu_list = "" - for (n = 1; n <= nitems; n++) - { - printf "continent_%s_tlc_%d=%s\n", - cont, n, continent_tlc[cont,n] - - title = continent_title[cont,n] - menu_list = menu_list \ - ( length(menu_list) > 0 ? "\n" : "" ) \ - n " '\''" title "'\''" - } - - gsub(/"/, "\\\"", menu_list) - printf "continent_%s_menu_list=\"%s\"\n", cont, menu_list - } - - gsub(/"/, "\\\"", continent_menu_list) - printf "continent_menu_list=\"%s\"\n", cont_menu_list - print "export continent_menu_list" - - # - # Dump the submenus of countries with multiple zones - # - for (tlc in country_menu_list) - { - menu_list = country_menu_list[tlc] - gsub(/"/, "\\\"", menu_list) - printf "country_%s_menu_list=\"%s\"\n", tlc, menu_list - } -} -' -f_make_menus() -{ - eval $( :| awk "$f_make_menus_awk" ) -} - -fi # ! $_TIMEZONE_MENUS_SUBR diff --git a/usr.sbin/bsdconfig/timezone/include/zones.subr b/usr.sbin/bsdconfig/timezone/include/zones.subr deleted file mode 100644 index 704d02f975239..0000000000000 --- a/usr.sbin/bsdconfig/timezone/include/zones.subr +++ /dev/null @@ -1,670 +0,0 @@ -if [ ! "$_TIMEZONE_ZONES_SUBR" ]; then _TIMEZONE_ZONES_SUBR=1 -# -# Copyright (c) 2011-2012 Devin Teske -# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr - -APP_DIR="090.timezone" -f_include $BSDCFG_LIBE/$APP_DIR/include/continents.subr -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ CONFIGURATION - -# -# Standard pathnames -# -_PATH_ZONETAB="/usr/share/zoneinfo/zone.tab" -_PATH_ZONEINFO="/usr/share/zoneinfo" -_PATH_LOCALTIME="/etc/localtime" -_PATH_DB="/var/db/zoneinfo" - -# -# Export required i18n messages for awk(1) ENVIRON visibility -# -export msg_conflicting_zone_definition -export msg_country_code_invalid -export msg_country_code_unknown -export msg_invalid_country_code -export msg_invalid_format -export msg_invalid_region -export msg_invalid_zone_name -export msg_zone_multiply_defined -export msg_zone_must_have_description - -############################################################ FUNCTIONS - -# f_read_zones -# -# Read the zone descriptions database in _PATH_ZONETAB: -# /usr/share/zoneinfo/zone.tab on all OSes -# -# The format of this file (on all OSes) is: -# code coordinates TZ comments -# -# With each of the following elements (described below) being separated by a -# single tab character: -# -# code -# The ISO 3166 2-character country code. -# coordinates -# Latitude and logitude of the zone's principal location in ISO -# 6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM -# or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long- -# itude (+ is east). -# TZ -# Zone name used in value of TZ environment variable. -# comments -# Comments; present if and only if the country has multiple rows. -# -# Required variables [from continents.subr]: -# -# CONTINENTS -# Space-separated list of continents. -# continent_*_name -# Directory element in _PATH_ZONEINFO for the continent -# represented by *. -# -# Required variables [created by f_read_iso3166_table from iso3166.subr]: -# -# country_CODE_name -# Country name of the country represented by CODE, the 2- -# character country code. -# -# Variables created by this function: -# -# country_CODE_nzones -# Either set to `-1' to indicate that the 2-character country -# code has only a single zone associated with it (and therefore -# you should query the `country_CODE_*' environment variables), -# or set to `0' or higher to indicate how many zones are assoc- -# iated with the given country code. When multiple zones are -# configured for a single code, you should instead query the -# `country_CODE_*_N' environment variables (e.g., `echo -# $country_AQ_descr_1' prints the description of the first -# timezone in Antarctica). -# country_CODE_filename -# The ``filename'' portion of the TZ value that appears after the -# `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man' -# from `Europe/Isle_of_Man'). -# country_CODE_cont -# The ``continent'' portion of the TZ value that appears before -# the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from -# `Europe/Isle_of_Man'). -# country_CODE_descr -# The comments associated with the ISO 3166 code entry (if any). -# -# NOTE: CODE is the 2-character country code. -# -# This function is a two-parter. Below is the awk(1) portion of the function, -# afterward is the sh(1) function which utilizes the below awk script. -# -f_read_zones_awk=' -# Variables that should be defined on the invocation line: -# -v progname="progname" -# -BEGIN { - lineno = 0 - failed = 0 - - # - # Initialize continents array/map (name => id) - # - split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) - for (item in array) - { - cont = array[item] - if (!cont) continue - name = ENVIRON["continent_" cont "_name"] - continents[name] = cont - } -} -function die(fmt, argc, argv) -{ - printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname - for (n = 1; n <= argc; n++) - printf " \"%s\"", argv[n] - print "" - failed++ - exit 1 -} -function find_continent(name) -{ - return continents[name] -} -function add_zone_to_country(lineno, tlc, descr, file, cont) -{ - # - # Validate the two-character country code - # - if (!match(tlc, /^[A-Z][A-Z]$/)) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = tlc - die(ENVRION["msg_country_code_invalid"], 3, argv) - } - if (!ENVIRON["country_" tlc "_name"]) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = tlc - die(ENVIRON["msg_country_code_unknown"], 3, argv) - } - - # - # Add Zone to an array that we will parse at the end - # - if (length(descr) > 0) - { - if (country_nzones[tlc] < 0) - { - argv[1] = FILENAME - argv[2] = lineno - die(ENVIRON["msg_conflicting_zone_definition"], 2, argv) - } - - n = ++country_nzones[tlc] - country_cont[tlc,n] = cont - country_filename[tlc,n] = file - country_descr[tlc,n] = descr - } - else - { - if (country_nzones[tlc] > 0) - { - argv[1] = FILENAME - argv[2] = lineno - die(ENVIRON["msg_zone_must_have_description"], 2, argv) - } - if (country_nzones[tlc] < 0) - { - argv[1] = FILENAME - argv[2] = lineno - die(ENVIRON["msg_zone_multiply_defined"], 2, argv) - } - - country_nzones[tlc] = -1 - country_cont[tlc] = cont - country_filename[tlc] = file - } -} -function print_country_code(tlc) -{ - nz = country_nzones[tlc] - - printf "country_%s_nzones=%d\n", tlc, nz - printf "export country_%s_nzones\n", tlc - - if (nz < 0) - { - printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc] - printf "export country_%s_cont\n", tlc - printf "country_%s_filename=\"%s\"\n", - tlc, country_filename[tlc] - } - else - { - n = 0 - while ( ++n <= nz ) - { - printf "country_%s_cont_%d=\"%s\"\n", - tlc, n, country_cont[tlc,n] - printf "export country_%s_cont_%d\n", tlc, n - printf "country_%s_filename_%d=\"%s\"\n", - tlc, n, country_filename[tlc,n] - printf "country_%s_descr_%d=\"%s\"\n", - tlc, n, country_descr[tlc,n] - } - } -} -/^#/ { - lineno++ - next -} -!/^#/ { - lineno++ - - # - # Split the current record (on TAB) into an array - # - if (split($0, line, /\t/) < 2) - { - argv[1] = FILENAME - argv[2] = lineno - die(ENVIRON["msg_invalid_format"], 2, argv) - } - - # Get the ISO3166-1 (Alpha 1) 2-letter country code - tlc = line[1] - - # - # Validate the two-character country code - # - if (length(tlc) != 2) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = tlc - die(ENVIRON["msg_invalid_country_code"], 3, argv) - } - - # Get the TZ field - tz = line[3] - - # - # Validate the TZ field - # - if (!match(tz, "/")) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = tz - die(ENVIRON["msg_invalid_zone_name"], 3, argv) - } - - # - # Get the continent portion of the TZ field - # - contbuf = tz - sub("/.*$", "", contbuf) - - # - # Validate the continent - # - cont = find_continent(contbuf) - if (!cont) - { - argv[1] = FILENAME - argv[2] = lineno - argv[3] = contbuf - die(ENVIRON["msg_invalid_region"], 3, argv) - } - - # - # Get the filename portion of the TZ field - # - filename = tz - sub("^[^/]*/", "", filename) - - # - # Calculate the substr start-position of the comment - # - descr_start = 0 - n = 4 - while (--n) - descr_start += length(line[n]) + 1 - - # Get the comment field - descr = substr($0, descr_start + 1) - - add_zone_to_country(lineno, tlc, descr, filename, cont) -} -END { - if (failed) exit failed - for (tlc in country_nzones) - print_country_code(tlc) -} -' -f_read_zones() -{ - eval $( awk -v progname="$pgm" \ - "$f_read_zones_awk" \ - "$_PATH_ZONETAB" ) -} - -# f_install_zoneinfo_file $filename -# -# Installs a zone file to _PATH_LOCALTIME. -# -f_install_zoneinfo_file() -{ - local zoneinfo_file="$1" - local copymode title msg err size - - if [ -L "$_PATH_LOCALTIME" ]; then - copymode= - elif [ ! -e "$_PATH_LOCALTIME" ]; then - # Nothing there yet... - copymode=1 - else - copymode=1 - fi - - if [ "$VERBOSE" ]; then - if [ ! "$zoneinfo_file" ]; then - msg=$( printf "$msg_removing_file" "$_PATH_LOCALTIME" ) - elif [ "$copymode" ]; then - msg=$( printf "$msg_copying_file" \ - "$zoneinfo_file" "$_PATH_LOCALTIME" ) - else - msg=$( printf "$msg_creating_symlink" \ - "$_PATH_LOCALTIME" "$zoneinfo_file" ) - fi - if [ "$USEDIALOG" ]; then - f_dialog_title "$msg_info" - title="$DIALOG_TITLE" - btitle="$DIALOG_BACKTITLE" - f_dialog_title_restore - size=$( f_dialog_buttonbox_size "$title" \ - "$btitle" "$msg" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$msg\" $size - else - printf "%s\n" "$msg" - fi - fi - - if [ "$REALLYDOIT" ]; then - f_dialog_title "$msg_error" - title="$DIALOG_TITLE" - btitle="$DIALOG_BACKTITLE" - f_dialog_title_restore - - if [ ! "$zoneinfo_file" ]; then - - err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - err=$( rm -f "$_PATH_DB" 2>&1 ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - if [ "$VERBOSE" ]; then - title="$msg_done" - msg=$( printf "$msg_removed_file" \ - "$_PATH_LOCALTIME" ) - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$msg" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$msg\" $size - else - printf "%s\n" "$msg" - fi - fi - - return $SUCCESS - - fi # ! zoneinfo_file - - if [ "$copymode" ]; then - - err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - else # ! copymode - - err=$( ( :< "$zoneinfo_file" ) 2>&1 ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 ) - if [ "$err" ]; then - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" \ - "$btitle" \ - "$err" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$err\" $size - else - f_err "%s\n" "$err" - fi - return $FAILURE - fi - - fi # copymode - - if [ "$VERBOSE" ]; then - title="$msg_done" - if [ "$copymode" ]; then - msg=$( printf "$msg_copied_timezone_file" \ - "$zoneinfo_file" \ - "$_PATH_LOCALTIME" ) - else - msg=$( printf "$msg_created_symlink" \ - "$_PATH_LOCALTIME" \ - "$zoneinfo_file" ) - fi - if [ "$USEDIALOG" ]; then - size=$( f_dialog_buttonbox_size \ - "$title" "$btitle" "$msg" ) - eval $DIALOG \ - --title \"\$title\" \ - --backtitle \"\$btitle\" \ - --ok-label \"\$msg_ok\" \ - --msgbox \"\$msg\" $size - else - printf "%s\n" "$msg" - fi - fi - - fi # REALLYDOIT - - return $SUCCESS -} - -# f_install_zoneinfo $zoneinfo -# -# Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo -# will be written to _PATH_DB (usable later with the `-r' flag). -# -f_install_zoneinfo() -{ - local zoneinfo="$1" - local rv - - f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo" - rv=$? - - # Save knowledge for later - if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then - if : 2> /dev/null > "$_PATH_DB"; then - cat <<-EOF > "$_PATH_DB" - $zoneinfo - EOF - fi - fi - - return $rv -} - -# f_confirm_zone $filename -# -# Prompt the user to confirm the new timezone data. The first (and only) -# argument should be the pathname to the zoneinfo file, either absolute or -# relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles"). -# -# The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is -# pressed (see dialog(1) for additional details). -# -f_confirm_zone() -{ - local filename="$1" - f_dialog_title "$msg_confirmation" - local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE" - f_dialog_title_restore - local tm_zone="$( TZ="$filename" date +%Z )" - local prompt="$( printf "$msg_look_reasonable" "$tm_zone" )" - local height=5 width=72 - - if [ "$USE_XDIALOG" ]; then - height=$(( $height + 4 )) - $DIALOG \ - --title "$title" \ - --backtitle "$btitle" \ - --ok-label "$msg_yes" \ - --cancel-label "$msg_no" \ - --yesno "$prompt" $height $width - else - $DIALOG \ - --title "$title" \ - --backtitle "$btitle" \ - --yes-label "$msg_yes" \ - --no-label "$msg_no" \ - --yesno "$prompt" $height $width - fi -} - -# f_set_zone_utc -# -# Resets to the UTC timezone. -# -f_set_zone_utc() -{ - f_confirm_zone "" || return $FAILURE - f_install_zoneinfo_file "" -} - -fi # ! $_TIMEZONE_ZONES_SUBR diff --git a/usr.sbin/bsdconfig/timezone/share/Makefile b/usr.sbin/bsdconfig/timezone/share/Makefile new file mode 100644 index 0000000000000..1b7b80c3aa6e3 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/share/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/bsdconfig/timezone +FILES= continents.subr countries.subr iso3166.subr menus.subr \ + zones.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/timezone/share/continents.subr b/usr.sbin/bsdconfig/timezone/share/continents.subr new file mode 100644 index 0000000000000..8f3e516085124 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/share/continents.subr @@ -0,0 +1,138 @@ +if [ ! "$_TIMEZONE_CONTINENTS_SUBR" ]; then _TIMEZONE_CONTINENTS_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# List of worldly continents/oceans (export'ed for awk(1) ENVIRON visibility) +# +export CONTINENTS=" + africa + america + antarctica + arctic + asia + atlantic + australia + europe + indian + pacific + utc +" + +# +# Directory name of each continent/ocean (in _PATH_ZONEINFO) +# +export continent_africa_name="Africa" +export continent_america_name="America" +export continent_antarctica_name="Antarctica" +export continent_arctic_name="Arctic" +export continent_asia_name="Asia" +export continent_atlantic_name="Atlantic" +export continent_australia_name="Australia" +export continent_europe_name="Europe" +export continent_indian_name="Indian" +export continent_pacific_name="Pacific" +export continent_utc_name="UTC" + +# +# Export i18n menu texts of continents/oceans for awk(1) ENVIRON visibility +# NOTE: These are defined in messages.subr included above. +# +export continent_africa_title +export continent_america_title +export continent_antarctica_title +export continent_arctic_title +export continent_asia_title +export continent_atlantic_title +export continent_australia_title +export continent_europe_title +export continent_indian_title +export continent_pacific_title +export continent_utc_title + +############################################################ FUNCTIONS + +# f_continent $cont $property +# +# Returns a single property of a given continent. Available properties are: +# +# name Directory name of continent/ocean as it appears in +# _PATH_ZONEINFO. +# title Menu text of this continent/ocean to be displayed in the +# continent-selection menu. +# nitems Number of submenu items associated with this +# continent/ocean. +# tlc_N 2-character country code of the Nth submenu item associated +# with this continent displayed in the country-selection menu +# (which appears after continent selection). +# menu_list Menu-list of regions for this continent. +# +f_continent() +{ + local cont="$1" property="$2" + eval echo \"\${continent_${cont}_$property}\" +} + +# f_find_continent $title +# +# Returns continent identifier given continent title. +# +f_find_continent() +{ + local cont + for cont in $CONTINENTS; do + if [ "$1" = "$( f_continent $cont title )" ]; then + echo "$cont" + return $SUCCESS + fi + done + return $FAILURE +} + +# f_OCEANP $cont +# +# Returns "1" if the first argument is an ocean, otherwise NULL. +# +f_OCEANP() +{ + case "$1" in + arctic|atlantic|indian|pacific) + echo 1 + esac +} + +fi # ! $_TIMEZONE_CONTINENTS_SUBR diff --git a/usr.sbin/bsdconfig/timezone/share/countries.subr b/usr.sbin/bsdconfig/timezone/share/countries.subr new file mode 100644 index 0000000000000..9c6a164e213ab --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/share/countries.subr @@ -0,0 +1,78 @@ +if [ ! "$_TIMEZONE_COUNTRIES_SUBR" ]; then _TIMEZONE_COUNTRIES_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# f_country $code $property +# +# Returns a single property of a given country. Available properties are: +# +# name Name of the country as read from _PATH_ISO3166. +# nzones Number of zones within the country (-1 if country has +# only a single zone). +# filename The filename portion of the TZ field (after the `/') as +# read from _PATH_ZONETAB. +# cont The principal continent in which the country lies (appears +# before the `/' in the TZ field of _PATH_ZONETAB). +# filename_N Like filename, but for the Nth zone when the country has +# multiple zones (nzones > 0). +# cont_N Like cont, but for the Nth zone when the country has +# multiple zones (nzones > 0). +# descr_N Like name, but for the Nth zone when the country has +# multiple zones (nzones > 0) +# +f_country() +{ + local code="$1" property="$2" + eval echo \"\${country_${code}_$property}\" +} + +# f_sort_countries +# +# Sorts alphabetically the 2-character country codes listed in $COUNTRIES based +# on the name of each country. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_sort_countries_awk=' +{ + split($0, array, /[[:space:]]+/) + for (item in array) + { + tlc = array[item] + print ENVIRON["country_" tlc "_name"] " " tlc + } +} +' +f_sort_countries() +{ + COUNTRIES=$( echo "$COUNTRIES" | awk "$f_sort_countries_awk" | + sort | awk '{print $NF}' ) + export COUNTRIES +} + +fi # ! $_TIMEZONE_COUNTRIES_SUBR diff --git a/usr.sbin/bsdconfig/timezone/share/iso3166.subr b/usr.sbin/bsdconfig/timezone/share/iso3166.subr new file mode 100644 index 0000000000000..72e934afddfb0 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/share/iso3166.subr @@ -0,0 +1,197 @@ +if [ ! "$_TIMEZONE_ISO3166_SUBR" ]; then _TIMEZONE_ISO3166_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Standard pathnames +# +_PATH_ISO3166="/usr/share/misc/iso3166" + +# +# Export required i18n messages for awk(1) ENVIRON visibility +# +export msg_country_code_multiply_defined +export msg_invalid_code +export msg_invalid_format + +############################################################ FUNCTIONS + +# f_read_iso3166_table +# +# Read the ISO 3166 country code database in _PATH_ISO3166: +# /usr/share/misc/iso3166 on FreeBSD +# /usr/share/zoneinfo/iso3166.tab on Linux, Mac OS X, and Cygwin +# +# The format of this file on FreeBSD is: +# two three number name +# +# The format of this file on Linux, Mac OS X, and Cygwin is: +# two name +# +# With each of the following elements (described below) being separated by a +# single tab character: +# +# two ISO 3166 2-character country code +# three ISO 3166 3-character country code (if provided) +# number ISO 3166 numeric country code (if provided) +# name Human-readable country name (may contain spaces) +# +# Variables created by this function: +# +# COUNTRIES +# A space-separated list of 2-character country codes. +# country_CODE_name +# The country `name' (as described above). +# +# where CODE is the 2-character country code. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_read_iso3166_table_awk=' +# Variables that should be defined on the invocation line: +# -v progname="progname" +# +BEGIN { + lineno = 0 + failed = 0 +} +function die(fmt, argc, argv) +{ + printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname + for (n = 1; n <= argc; n++) + printf " \"%s\"", argv[n] + print "" + failed++ + exit 1 +} +function add_country(tlc, name) +{ + if (country_name[tlc]) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + argv[4] = name + die(ENVIRON["msg_country_code_multiply_defined"], 4, argv) + } + + country_name[tlc] = name +} +function print_country_name(tlc) +{ + name = country_name[tlc] + gsub(/"/, "\\\"", name) + printf "country_%s_name=\"%s\"\n", tlc, name + printf "export country_%s_name\n", tlc +} +/^#/ { + lineno++ + next +} +!/^#/ { + lineno++ + + # Split the current record (on TAB) into an array + split($0, line, /\t/) + + # Get the ISO3166-1 (Alpha 1) 2-letter country code + tlc = line[1] + + # + # Validate the two-character country code + # + if (length(tlc) != 2) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_invalid_format"], 2, argv) + } + if (!match(tlc, /^[A-Z][A-Z]$/)) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVIRON["msg_invalid_code"], 3, argv) + } + + # + # Calculate the substr start-position of the name + # + name_start = 0 + n = 4 + if (FILENAME ~ /\.tab$/) + n = 2 + while (--n) + { + # + # Validate field-length of 2nd/3rd columns while we are here + # + if (n > 1 && length(line[n]) != 3) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_invalid_format"], 2, argv) + } + + name_start += length(line[n]) + 1 + } + + # Get the name field + name = substr($0, name_start + 1) + + add_country(tlc, name) +} +END { + list = "" + for (tlc in country_name) + { + list = list (length(list) > 0 ? " " : "") tlc + print_country_name(tlc) + } + printf "COUNTRIES=\"%s\"\n", list + print "export COUNTRIES" +} +' +f_read_iso3166_table() +{ + eval $( awk -v progname="$pgm" \ + "$f_read_iso3166_table_awk" \ + "$_PATH_ISO3166" ) +} + +fi # ! $_TIMEZONE_ISO3166_SUBR diff --git a/usr.sbin/bsdconfig/timezone/share/menus.subr b/usr.sbin/bsdconfig/timezone/share/menus.subr new file mode 100644 index 0000000000000..5ce50f8750f19 --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/share/menus.subr @@ -0,0 +1,220 @@ +if [ ! "$_TIMEZONE_MENUS_SUBR" ]; then _TIMEZONE_MENUS_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr + +############################################################ GLOBALS + +# +# Export special included variables required by awk(1) for `ENVIRON' visibility +# +export DIALOG_MENU_TAGS + +############################################################ FUNCTIONS + +# f_make_menus +# +# Creates the tag/item ordered-pair list environment variables for the +# continent and country menus. +# +# Required variables [from continents.subr]: +# +# CONTINENTS +# Space-separated list of continents. +# continent_*_title +# Desired menu text for the continent represented by *. +# +# Required variables [created by f_read_iso3166_table from iso3166.subr]: +# +# COUNTRIES +# Space-separated list of 2-character country codes. +# country_*_name :: when country_*_nzones < 0 +# Desired menu text for the country-zone represented by *, the 2- +# character country code. +# +# Required variables [created by f_read_zones from zones.subr]: +# +# country_*_nzones +# Number of zones for the country represented by *, the 2- +# character country code. Should be -1 if the country has only +# one single zone, otherwise 1 or greater to indicate how many +# zones the country has. +# country_*_cont :: when country_*_nzones < 0 +# Principal continent (or ocean) in which the country-zone +# represented by *, the 2-character country code, resides. +# country_*_cont_N :: when country_*_nzones > 0 +# Principal continent (or ocean) in which zone-N of the country +# represented by * resides, the 2-character country code. +# country_*_descr_N :: when country_*_nzones > 0 +# Desired submenu text for zone-N of the country represented by +# *, the 2-character country code. +# +# Variables created by this function: +# +# continent_menu_list +# Menu-list of continents. +# continent_*_nitems +# Number of items associated with the continent represented by *, +# the continent identifier. +# continent_*_tlc_N +# 2-character country code of the Nth item in the continent menu +# for the continent represented by *, the continent identifier. +# continent_*_menu_list +# Menu-list of countries/zones for each continent represented by +# *, the continent identifier. +# country_*_menu_list +# For countries that have multiple zones, this is the submenu- +# list of zones for said country represented by *, the 2- +# character country code. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_make_menus_awk=' +function add_zone_n_to_country_menu(tlc, n) +{ + zone_title = ENVIRON["country_" tlc "_descr_" n] + gsub(/'\''/, "'\''\\'\'''\''", zone_title) + country_menu_list[tlc] = country_menu_list[tlc] \ + ( length(country_menu_list[tlc]) > 0 ? "\n" : "" ) \ + n " '\''" zone_title "'\''" +} +BEGIN { + # + # First, count up all the countries in each continent/ocean. + # Be careful to count those countries which have multiple zones + # only once for each. NB: some countries are in multiple + # continents/oceans. + # + i = split(ENVIRON["COUNTRIES"], countries, /[[:space:]]+/) + for (cp = 1; cp <= i; cp++) + { + tlc = countries[cp] + title = ENVIRON["country_" tlc "_name"] + gsub(/'\''/, "'\''\\'\'''\''", title) + nzones = ENVIRON["country_" tlc "_nzones"] + if (!nzones) + { + # Country has no zones + continue + } + else if (nzones < 0) + { + # Country has only one zone + cont = ENVIRON["country_" tlc "_cont"] + nitems = ++continent_nitems[cont] + continent_tlc[cont,nitems] = tlc + continent_title[cont,nitems] = title + } + else + { + # Country has one or more zones + for (n = 1; n <= nzones; n++) + { + add_zone_n_to_country_menu(tlc, n) + cont = ENVIRON["country_" tlc "_cont_" n] + for (x = 1; x < n; x++) + { + contx = ENVIRON["country_"tlc"_cont_"x] + if (cont == contx) break + } + if (x == n) + { + nitems = ++continent_nitems[cont] + continent_tlc[cont,nitems] = tlc + continent_title[cont,nitems] = title + } + } + } + } +} +END { + tags = ENVIRON["DIALOG_MENU_TAGS"] + cont_menu_list = "" + tagn = 0 + + # + # Assemble the menu items in the menu list for each continent/ocean. + # + i = split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) + for (item = 1; item <= i; item++) + { + cont = array[item] + if (!cont) continue + + if (++tagn >= length(tags)) break + tag = substr(tags, tagn, 1) + cont_menu_list = cont_menu_list \ + ( length(cont_menu_list) > 0 ? "\n" : "" ) \ + "'\''" tag "'\'' '\''" \ + ENVIRON["continent_" cont "_title"] "'\''" + + nitems = continent_nitems[cont] + printf "continent_%s_nitems=%d\n", cont, nitems + + menu_list = "" + for (n = 1; n <= nitems; n++) + { + printf "continent_%s_tlc_%d=%s\n", + cont, n, continent_tlc[cont,n] + + title = continent_title[cont,n] + menu_list = menu_list \ + ( length(menu_list) > 0 ? "\n" : "" ) \ + n " '\''" title "'\''" + } + + gsub(/"/, "\\\"", menu_list) + printf "continent_%s_menu_list=\"%s\"\n", cont, menu_list + } + + gsub(/"/, "\\\"", continent_menu_list) + printf "continent_menu_list=\"%s\"\n", cont_menu_list + print "export continent_menu_list" + + # + # Dump the submenus of countries with multiple zones + # + for (tlc in country_menu_list) + { + menu_list = country_menu_list[tlc] + gsub(/"/, "\\\"", menu_list) + printf "country_%s_menu_list=\"%s\"\n", tlc, menu_list + } +} +' +f_make_menus() +{ + eval $( :| awk "$f_make_menus_awk" ) +} + +fi # ! $_TIMEZONE_MENUS_SUBR diff --git a/usr.sbin/bsdconfig/timezone/share/zones.subr b/usr.sbin/bsdconfig/timezone/share/zones.subr new file mode 100644 index 0000000000000..874b27200459f --- /dev/null +++ b/usr.sbin/bsdconfig/timezone/share/zones.subr @@ -0,0 +1,670 @@ +if [ ! "$_TIMEZONE_ZONES_SUBR" ]; then _TIMEZONE_ZONES_SUBR=1 +# +# Copyright (c) 2011-2012 Devin Teske +# 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/timezone/continents.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Standard pathnames +# +_PATH_ZONETAB="/usr/share/zoneinfo/zone.tab" +_PATH_ZONEINFO="/usr/share/zoneinfo" +_PATH_LOCALTIME="/etc/localtime" +_PATH_DB="/var/db/zoneinfo" + +# +# Export required i18n messages for awk(1) ENVIRON visibility +# +export msg_conflicting_zone_definition +export msg_country_code_invalid +export msg_country_code_unknown +export msg_invalid_country_code +export msg_invalid_format +export msg_invalid_region +export msg_invalid_zone_name +export msg_zone_multiply_defined +export msg_zone_must_have_description + +############################################################ FUNCTIONS + +# f_read_zones +# +# Read the zone descriptions database in _PATH_ZONETAB: +# /usr/share/zoneinfo/zone.tab on all OSes +# +# The format of this file (on all OSes) is: +# code coordinates TZ comments +# +# With each of the following elements (described below) being separated by a +# single tab character: +# +# code +# The ISO 3166 2-character country code. +# coordinates +# Latitude and logitude of the zone's principal location in ISO +# 6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM +# or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long- +# itude (+ is east). +# TZ +# Zone name used in value of TZ environment variable. +# comments +# Comments; present if and only if the country has multiple rows. +# +# Required variables [from continents.subr]: +# +# CONTINENTS +# Space-separated list of continents. +# continent_*_name +# Directory element in _PATH_ZONEINFO for the continent +# represented by *. +# +# Required variables [created by f_read_iso3166_table from iso3166.subr]: +# +# country_CODE_name +# Country name of the country represented by CODE, the 2- +# character country code. +# +# Variables created by this function: +# +# country_CODE_nzones +# Either set to `-1' to indicate that the 2-character country +# code has only a single zone associated with it (and therefore +# you should query the `country_CODE_*' environment variables), +# or set to `0' or higher to indicate how many zones are assoc- +# iated with the given country code. When multiple zones are +# configured for a single code, you should instead query the +# `country_CODE_*_N' environment variables (e.g., `echo +# $country_AQ_descr_1' prints the description of the first +# timezone in Antarctica). +# country_CODE_filename +# The ``filename'' portion of the TZ value that appears after the +# `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man' +# from `Europe/Isle_of_Man'). +# country_CODE_cont +# The ``continent'' portion of the TZ value that appears before +# the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from +# `Europe/Isle_of_Man'). +# country_CODE_descr +# The comments associated with the ISO 3166 code entry (if any). +# +# NOTE: CODE is the 2-character country code. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_read_zones_awk=' +# Variables that should be defined on the invocation line: +# -v progname="progname" +# +BEGIN { + lineno = 0 + failed = 0 + + # + # Initialize continents array/map (name => id) + # + split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) + for (item in array) + { + cont = array[item] + if (!cont) continue + name = ENVIRON["continent_" cont "_name"] + continents[name] = cont + } +} +function die(fmt, argc, argv) +{ + printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname + for (n = 1; n <= argc; n++) + printf " \"%s\"", argv[n] + print "" + failed++ + exit 1 +} +function find_continent(name) +{ + return continents[name] +} +function add_zone_to_country(lineno, tlc, descr, file, cont) +{ + # + # Validate the two-character country code + # + if (!match(tlc, /^[A-Z][A-Z]$/)) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVRION["msg_country_code_invalid"], 3, argv) + } + if (!ENVIRON["country_" tlc "_name"]) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVIRON["msg_country_code_unknown"], 3, argv) + } + + # + # Add Zone to an array that we will parse at the end + # + if (length(descr) > 0) + { + if (country_nzones[tlc] < 0) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_conflicting_zone_definition"], 2, argv) + } + + n = ++country_nzones[tlc] + country_cont[tlc,n] = cont + country_filename[tlc,n] = file + country_descr[tlc,n] = descr + } + else + { + if (country_nzones[tlc] > 0) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_zone_must_have_description"], 2, argv) + } + if (country_nzones[tlc] < 0) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_zone_multiply_defined"], 2, argv) + } + + country_nzones[tlc] = -1 + country_cont[tlc] = cont + country_filename[tlc] = file + } +} +function print_country_code(tlc) +{ + nz = country_nzones[tlc] + + printf "country_%s_nzones=%d\n", tlc, nz + printf "export country_%s_nzones\n", tlc + + if (nz < 0) + { + printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc] + printf "export country_%s_cont\n", tlc + printf "country_%s_filename=\"%s\"\n", + tlc, country_filename[tlc] + } + else + { + n = 0 + while ( ++n <= nz ) + { + printf "country_%s_cont_%d=\"%s\"\n", + tlc, n, country_cont[tlc,n] + printf "export country_%s_cont_%d\n", tlc, n + printf "country_%s_filename_%d=\"%s\"\n", + tlc, n, country_filename[tlc,n] + printf "country_%s_descr_%d=\"%s\"\n", + tlc, n, country_descr[tlc,n] + } + } +} +/^#/ { + lineno++ + next +} +!/^#/ { + lineno++ + + # + # Split the current record (on TAB) into an array + # + if (split($0, line, /\t/) < 2) + { + argv[1] = FILENAME + argv[2] = lineno + die(ENVIRON["msg_invalid_format"], 2, argv) + } + + # Get the ISO3166-1 (Alpha 1) 2-letter country code + tlc = line[1] + + # + # Validate the two-character country code + # + if (length(tlc) != 2) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tlc + die(ENVIRON["msg_invalid_country_code"], 3, argv) + } + + # Get the TZ field + tz = line[3] + + # + # Validate the TZ field + # + if (!match(tz, "/")) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = tz + die(ENVIRON["msg_invalid_zone_name"], 3, argv) + } + + # + # Get the continent portion of the TZ field + # + contbuf = tz + sub("/.*$", "", contbuf) + + # + # Validate the continent + # + cont = find_continent(contbuf) + if (!cont) + { + argv[1] = FILENAME + argv[2] = lineno + argv[3] = contbuf + die(ENVIRON["msg_invalid_region"], 3, argv) + } + + # + # Get the filename portion of the TZ field + # + filename = tz + sub("^[^/]*/", "", filename) + + # + # Calculate the substr start-position of the comment + # + descr_start = 0 + n = 4 + while (--n) + descr_start += length(line[n]) + 1 + + # Get the comment field + descr = substr($0, descr_start + 1) + + add_zone_to_country(lineno, tlc, descr, filename, cont) +} +END { + if (failed) exit failed + for (tlc in country_nzones) + print_country_code(tlc) +} +' +f_read_zones() +{ + eval $( awk -v progname="$pgm" \ + "$f_read_zones_awk" \ + "$_PATH_ZONETAB" ) +} + +# f_install_zoneinfo_file $filename +# +# Installs a zone file to _PATH_LOCALTIME. +# +f_install_zoneinfo_file() +{ + local zoneinfo_file="$1" + local copymode title msg err size + + if [ -L "$_PATH_LOCALTIME" ]; then + copymode= + elif [ ! -e "$_PATH_LOCALTIME" ]; then + # Nothing there yet... + copymode=1 + else + copymode=1 + fi + + if [ "$VERBOSE" ]; then + if [ ! "$zoneinfo_file" ]; then + msg=$( printf "$msg_removing_file" "$_PATH_LOCALTIME" ) + elif [ "$copymode" ]; then + msg=$( printf "$msg_copying_file" \ + "$zoneinfo_file" "$_PATH_LOCALTIME" ) + else + msg=$( printf "$msg_creating_symlink" \ + "$_PATH_LOCALTIME" "$zoneinfo_file" ) + fi + if [ "$USEDIALOG" ]; then + f_dialog_title "$msg_info" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + size=$( f_dialog_buttonbox_size "$title" \ + "$btitle" "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" + fi + fi + + if [ "$REALLYDOIT" ]; then + f_dialog_title "$msg_error" + title="$DIALOG_TITLE" + btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + + if [ ! "$zoneinfo_file" ]; then + + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( rm -f "$_PATH_DB" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + if [ "$VERBOSE" ]; then + title="$msg_done" + msg=$( printf "$msg_removed_file" \ + "$_PATH_LOCALTIME" ) + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" + fi + fi + + return $SUCCESS + + fi # ! zoneinfo_file + + if [ "$copymode" ]; then + + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + else # ! copymode + + err=$( ( :< "$zoneinfo_file" ) 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" \ + "$btitle" \ + "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$err\" $size + else + f_err "%s\n" "$err" + fi + return $FAILURE + fi + + fi # copymode + + if [ "$VERBOSE" ]; then + title="$msg_done" + if [ "$copymode" ]; then + msg=$( printf "$msg_copied_timezone_file" \ + "$zoneinfo_file" \ + "$_PATH_LOCALTIME" ) + else + msg=$( printf "$msg_created_symlink" \ + "$_PATH_LOCALTIME" \ + "$zoneinfo_file" ) + fi + if [ "$USEDIALOG" ]; then + size=$( f_dialog_buttonbox_size \ + "$title" "$btitle" "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --backtitle \"\$btitle\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" + fi + fi + + fi # REALLYDOIT + + return $SUCCESS +} + +# f_install_zoneinfo $zoneinfo +# +# Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo +# will be written to _PATH_DB (usable later with the `-r' flag). +# +f_install_zoneinfo() +{ + local zoneinfo="$1" + local rv + + f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo" + rv=$? + + # Save knowledge for later + if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then + if : 2> /dev/null > "$_PATH_DB"; then + cat <<-EOF > "$_PATH_DB" + $zoneinfo + EOF + fi + fi + + return $rv +} + +# f_confirm_zone $filename +# +# Prompt the user to confirm the new timezone data. The first (and only) +# argument should be the pathname to the zoneinfo file, either absolute or +# relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles"). +# +# The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is +# pressed (see dialog(1) for additional details). +# +f_confirm_zone() +{ + local filename="$1" + f_dialog_title "$msg_confirmation" + local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE" + f_dialog_title_restore + local tm_zone="$( TZ="$filename" date +%Z )" + local prompt="$( printf "$msg_look_reasonable" "$tm_zone" )" + local height=5 width=72 + + if [ "$USE_XDIALOG" ]; then + height=$(( $height + 4 )) + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --ok-label "$msg_yes" \ + --cancel-label "$msg_no" \ + --yesno "$prompt" $height $width + else + $DIALOG \ + --title "$title" \ + --backtitle "$btitle" \ + --yes-label "$msg_yes" \ + --no-label "$msg_no" \ + --yesno "$prompt" $height $width + fi +} + +# f_set_zone_utc +# +# Resets to the UTC timezone. +# +f_set_zone_utc() +{ + f_confirm_zone "" || return $FAILURE + f_install_zoneinfo_file "" +} + +fi # ! $_TIMEZONE_ZONES_SUBR diff --git a/usr.sbin/bsdconfig/timezone/timezone b/usr.sbin/bsdconfig/timezone/timezone index 2b41d908d3094..f785a75ef3f57 100755 --- a/usr.sbin/bsdconfig/timezone/timezone +++ b/usr.sbin/bsdconfig/timezone/timezone @@ -28,18 +28,18 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr -f_include $BSDCFG_LIBE/include/strings.subr - -APP_DIR="090.timezone" -f_include $BSDCFG_LIBE/$APP_DIR/include/continents.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/countries.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/iso3166.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/menus.subr -f_include $BSDCFG_LIBE/$APP_DIR/include/zones.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/timezone/continents.subr +f_include $BSDCFG_SHARE/timezone/countries.subr +f_include $BSDCFG_SHARE/timezone/iso3166.subr +f_include $BSDCFG_SHARE/timezone/menus.subr +f_include $BSDCFG_SHARE/timezone/zones.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/ttys/ttys b/usr.sbin/bsdconfig/ttys/ttys index 83715ca849bc9..4e67e2c8dd1c9 100755 --- a/usr.sbin/bsdconfig/ttys/ttys +++ b/usr.sbin/bsdconfig/ttys/ttys @@ -28,12 +28,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="150.ttys" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="150.ttys" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/Makefile b/usr.sbin/bsdconfig/usermgmt/Makefile index 7c1b3e50da9bb..910d380622d3a 100644 --- a/usr.sbin/bsdconfig/usermgmt/Makefile +++ b/usr.sbin/bsdconfig/usermgmt/Makefile @@ -2,7 +2,7 @@ NO_OBJ= -SUBDIR= include +SUBDIR= include share FILESDIR= ${LIBEXECDIR}/bsdconfig/070.usermgmt FILES= INDEX USAGE diff --git a/usr.sbin/bsdconfig/usermgmt/groupadd b/usr.sbin/bsdconfig/usermgmt/groupadd index c4f4fde475c1c..7326ab3aaa8ae 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupadd +++ b/usr.sbin/bsdconfig/usermgmt/groupadd @@ -29,11 +29,11 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr -APP_DIR="070.usermgmt" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" diff --git a/usr.sbin/bsdconfig/usermgmt/groupdel b/usr.sbin/bsdconfig/usermgmt/groupdel index aa5a71dd32e82..cbbe554f3be7b 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupdel +++ b/usr.sbin/bsdconfig/usermgmt/groupdel @@ -29,13 +29,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/group_input.subr -APP_DIR="070.usermgmt" -f_include $BSDCFG_LIBE/$APP_DIR/include/group_input.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/groupedit b/usr.sbin/bsdconfig/usermgmt/groupedit index beb11afb0b639..6de6fa8718e5c 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupedit +++ b/usr.sbin/bsdconfig/usermgmt/groupedit @@ -29,13 +29,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/group_input.subr -APP_DIR="070.usermgmt" -f_include $BSDCFG_LIBE/$APP_DIR/include/group_input.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/groupinput b/usr.sbin/bsdconfig/usermgmt/groupinput index 96186be258a73..e1df6140070d4 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupinput +++ b/usr.sbin/bsdconfig/usermgmt/groupinput @@ -29,13 +29,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/group_input.subr -APP_DIR="070.usermgmt" -f_include $BSDCFG_LIBE/$APP_DIR/include/group_input.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/include/Makefile b/usr.sbin/bsdconfig/usermgmt/include/Makefile index 15f88aa2ac2cd..a026728015ec1 100644 --- a/usr.sbin/bsdconfig/usermgmt/include/Makefile +++ b/usr.sbin/bsdconfig/usermgmt/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/070.usermgmt/include -FILES= group_input.subr messages.subr user_input.subr +FILES= messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/usermgmt/include/group_input.subr b/usr.sbin/bsdconfig/usermgmt/include/group_input.subr deleted file mode 100644 index b4121b1b62903..0000000000000 --- a/usr.sbin/bsdconfig/usermgmt/include/group_input.subr +++ /dev/null @@ -1,437 +0,0 @@ -if [ ! "$_USERMGMT_GROUP_INPUT_SUBR" ]; then _USERMGMT_GROUP_INPUT_SUBR=1 -# -# Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012 Devin Teske -# 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. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/strings.subr -f_include_lang $BSDCFG_LIBE/include/messages.subr - -APP_DIR="070.usermgmt" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ FUNCTIONS - -# f_input_group $group -# -# Given $group name or id, create the environment variables group_name, -# group_gid, and group_members (and group_password is reset to NULL). -# -f_input_group() -{ - eval $( pw groupshow "$1" | awk -F: ' - { - printf "group_name='\'%s\''\n", $1 - printf "group_password=\n" - printf "group_gid='\'%s\''\n", $3 - printf "group_members='\'%s\''\n", $4 - exit - }' ) -} - -# f_dialog_menu_group_list -# -# Allows the user to select a group from a list. -# -f_dialog_menu_group_list() -{ - local menu_list size - local hline="$hline_alnum_punc_tab_enter" - - menu_list=" - 'X $msg_exit' '' - " # END-QUOTE - - # Add groups from group(5) - menu_list="$menu_list $( pw groupshow -a | awk -F: ' - !/^[[:space:]]*(#|$)/ { - printf "'\'%s\'\ \'%s\''\n", $1, $1 - }' - )" - - size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\" \ - \"\$hline\" \ - $menu_list ) - - eval $DIALOG \ - --clear --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" -} - -# f_dialog_input_group_name [$group_name] -# -# Allows the user to enter a new groupname for a given group. If the user does -# not cancel or press ESC, the $group_name variable will hold the -# newly-configured value upon return. -# -# If $cur_group_name is defined, the user can enter that and by-pass error- -# checking (allowing the user to "revert" to an old value without, for example, -# being told that the groupname already exists). -# -f_dialog_input_group_name() -{ - local msg="$( printf "$msg_group" )" - local hline="$hline_alnum_tab_enter" - - # - # Loop until the user provides taint-free/valid input - # - local size retval _name="$1" _input="$1" - while :; do - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Check for no-change - [ "$_input" = "$_name" ] && return $SUCCESS - - # Check for reversion - if [ "$_input" = "$cur_group_name" ]; then - group_name="$cur_group_name" - return $SUCCESS - fi - - # Check for NULL entry - if [ ! "$_input" ]; then - f_show_msg "$msg_group_is_empty" - continue - fi - - # Check for invalid entry - if ! echo "$_input" | grep -q "^[[:alpha:]]"; then - f_show_msg "$msg_group_must_start_with_letter" - continue - fi - - # Check for duplicate entry - if f_quietly pw groupshow -n "$_input"; then - f_show_msg "$msg_group_already_used" "$_input" - continue - fi - - group_name="$_input" - break - done - save_flag=1 - - f_dprintf "group_name: [$cur_group_name]->[$group_name]" - - return $SUCCESS -} - -# f_dialog_input_group_password -# -# Prompt the user to enter a password (twice). -# -f_dialog_input_group_password() -{ - local hline="$hline_alnum_punc_tab_enter" - local msg size rmsg rsize - - msg=$( printf "$msg_group_password" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "" \ - "$hline" ) - - rmsg=$( printf "$msg_reenter_group_password" ) - rsize=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$rmsg" \ - "" \ - "$hline" ) - - # - # Loop until the user provides taint-free/valid input - # - local retval _password1 _password2 - while :; do - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --insecure \ - --passwordbox \"\$msg\" $size \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _password1=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --insecure \ - --passwordbox \"\$rmsg\" $rsize \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _password2=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Check for password mismatch - if [ "$_password1" != "$_password2" ]; then - f_show_msg "$msg_group_passwords_do_not_match" - continue - fi - - # Check for NULL entry - if [ ! "$_password1" ]; then - f_dialog_yesno \ - "$msg_disable_password_auth_for_group" || - continue - pw_group_password_disable=1 - else - pw_group_password_disable= - fi - - group_password="$_password1" - break - done - save_flag=1 - - f_dprintf "group_password: [$cur_group_password]->[$group_password]" - - return $SUCCESS -} - -# f_dialog_input_group_gid [$group_gid] -# -# Allow the user to enter a new GID for a given group. If the user does not -# cancel or press ESC, the $group_gid variable will hold the newly-configured -# value upon return. -# -f_dialog_input_group_gid() -{ - local msg size retval _input="$1" - local hline="$hline_num_tab_enter" - - msg=$( printf "$msg_group_id_leave_empty_for_default" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - group_gid="$_input" - save_flag=1 - - f_dprintf "group_gid: [$cur_group_gid]->[$group_gid]" - - return $SUCCESS -} - -# f_dialog_input_group_members [$group_members] -# -# Allow the user to modify a list of members for a given group. If the user does -# not cancel or press ESC, the $group_members variable will hold the newly- -# configured value upon return. -# -f_dialog_input_group_members() -{ - local menu_choice msg size retval _input="$1" - local hline="$hline_num_arrows_tab_enter" - local user - local menu_list - local all_users_valid - local _group_members - local checklist_users - - menu_list=" - 'X' '$msg_continue' - '1' '$msg_select_group_members_from_list' - '2' '$msg_enter_group_members_manually' - " # END-QUOTE - - while :; do - msg="$msg_group_members:" - menu_size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$msg\" \ - \"\$hline\" \ - $menu_list ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$msg\" $menu_size \ - $menu_list \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ - retval=$? - menu_choice=$( f_dialog_menutag ) - f_dprintf "retval=$retval menu_choice=[$menu_choice]" - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - case "$menu_choice" in - X) # Exit - break ;; - 1) # Select Group Members from a list - user_list=$( pw usershow -a | awk -F: ' - !/^[[:space:]]*(#|$)/ { printf "%s\n", $1 }' ) - checklist_users= - for user in $user_list; do - checklist_users="$checklist_users $user \"\"" - if echo "$_input" | grep -q "\<$user\>"; then - checklist_users="$checklist_users on" - else - checklist_users="$checklist_users off" - fi - done - - size=$( eval f_dialog_radiolist_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\" \ - \"\$hline\" \ - $checklist_users ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --separate-output \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --checklist \"\$msg\" $size \ - $checklist_users \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - _group_members=$( f_dialog_inputstr | tr '\n' ' ' | - sed -e 's/[[:space:]]\{1,\}/,/g;s/^,//;s/,$//' ) - - # Return to previous menu if user has either - # pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input="$_group_members" - ;; - 2) # Enter Group Members manually - hline="$hline_num_tab_enter" - msg=$( - printf "$msg_group_members ($msg_separated_by_commas)" - ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _group_members=$( f_dialog_inputstr ) - - # Return to previous menu if user has either - # pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input="$_group_members" - ;; - esac - done - - group_members="$_input" - save_flag=1 - f_dprintf "group_members: [$cur_group_members]->[$group_members]" - - return $SUCCESS -} - -fi # ! $_USERMGMT_GROUP_INPUT_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/include/user_input.subr b/usr.sbin/bsdconfig/usermgmt/include/user_input.subr deleted file mode 100644 index a522f9d9e53fc..0000000000000 --- a/usr.sbin/bsdconfig/usermgmt/include/user_input.subr +++ /dev/null @@ -1,1180 +0,0 @@ -if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_INPUT_SUBR=1 -# -# Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012 Devin Teske -# 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. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/strings.subr -f_include_lang $BSDCFG_LIBE/include/messages.subr - -APP_DIR="070.usermgmt" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -############################################################ CONFIGURATION - -# -# Default location of shells(5) -# -: ${ETC_SHELLS:=/etc/shells} - -############################################################ FUNCTIONS - -# f_get_member_groups $user -# -# Get a list of additional groups $user is a member of in group(5). -# -f_get_member_groups() -{ - echo $( pw groupshow -a | awk -F: "/[:,]$1(,|\$)/{print \$1}" ) -} - -# f_input_user $user -# -# Given $user name or id, create the environment variables pw_name, pw_uid, -# pw_gid, pw_class, pw_password_expire, pw_account_expire, pw_gecos, -# pw_home_dir, pw_shell, and pw_member_groups (and pw_password is reset to -# NULL). -# -f_input_user() -{ - local user="$1" - eval $( pw usershow "$user" | awk -F: ' - { - printf "pw_name='\'%s\''\n", $1 - printf "pw_password=\n" - printf "pw_uid='\'%s\''\n", $3 - printf "pw_gid='\'%s\''\n", $4 - printf "pw_class='\'%s\''\n", $5 - printf "pw_password_expire='\'%s\''\n", $6 - printf "pw_account_expire='\'%s\''\n", $7 - printf "pw_gecos='\'%s\''\n", $8 - printf "pw_home_dir='\'%s\''\n", $9 - printf "pw_shell='\'%s\''\n", $10 - }' ) - pw_member_groups=$( f_get_member_groups "$user" ) -} - -# f_dialog_menu_user_list -# -# Allows the user to select a login from a list. -# -f_dialog_menu_user_list() -{ - local menu_list size - local hline="$hline_alnum_punc_tab_enter" - - menu_list=" - 'X $msg_exit' '' - " # END-QUOTE - - # Add users from passwd(5) - menu_list="$menu_list $( pw usershow -a | awk -F: ' - !/^[[:space:]]*(#|$)/ { - printf "'\'%s\'\ \'%s\''\n", $1, $8 - }' - )" - - size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\" \ - \"\$hline\" \ - $menu_list ) - - eval $DIALOG \ - --clear --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" -} - -# f_dialog_input_member_groups [$member_groups] -# -# Allows the user to edit group memberships for a given user. If the user does -# not cancel or press ESC, the $pw_member_groups variable will hold the newly- -# configured value upon return. -# -f_dialog_input_member_groups() -{ - local msg="$( printf "$msg_member_of_groups" )" - local hline="$hline_alnum_tab_enter" - - # - # Loop until the user provides taint-free/valid input - # - local size retval all_groups checklist_groups="" _member_groups="$1" - all_groups=$( pw groupshow -a | awk -F: ' - !/^[[:space:]]*(#|$)/ { - printf "%s\n", $1 - }' - ) - for grp in $all_groups; do - checklist_groups="$checklist_groups $grp $grp" - if echo "$_member_groups" | grep -q "\<$grp\>"; then - checklist_groups="$checklist_groups on" - else - checklist_groups="$checklist_groups off" - fi - done - - while :; do - size=$( eval f_dialog_radiolist_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\" \ - \"\$hline\" \ - $checklist_groups ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --separate-output \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --checklist \"\$msg\" $size \ - $checklist_groups \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _member_groups=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # - # Validate each of the groups the user has entered - # - local group all_groups_valid=1 - for group in $_member_groups; do - if ! f_quietly pw groupshow -n "$group"; then - f_show_msg "$msg_group_not_found" "$group" - all_groups_valid= - break - fi - done - [ "$all_groups_valid" ] || continue - - pw_member_groups="$_member_groups" - break - done - save_flag=1 - - local debug="pw_member_groups:" - f_dprintf "$debug [$cur_pw_member_groups]->[$pw_member_groups]" - - return $SUCCESS -} - -# f_dialog_input_name [$name] -# -# Allows the user to enter a new username for a given user. If the user does -# not cancel or press ESC, the $pw_name variable will hold the newly-configured -# value upon return. -# -# If $cur_pw_name is defined, the user can enter that and by-pass error- -# checking (allowing the user to "revert" to an old value without, for example, -# being told that the username already exists). -# -f_dialog_input_name() -{ - local msg="$( printf "$msg_login" )" - local hline="$hline_alnum_tab_enter" - - # - # Loop until the user provides taint-free/valid input - # - local size retval _name="$1" _input="$1" - while :; do - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Check for no-change - [ "$_input" = "$_name" ] && return $SUCCESS - - # Check for reversion - if [ "$_input" = "$cur_pw_name" ]; then - pw_name="$cur_pw_name" - return $SUCCESS - fi - - # Check for NULL entry - if [ ! "$_input" ]; then - f_show_msg "$msg_login_is_empty" - continue - fi - - # Check for invalid entry - if ! echo "$_input" | grep -q "^[[:alpha:]]"; then - f_show_msg "$msg_login_must_start_with_letter" - continue - fi - - # Check for duplicate entry - if f_quietly pw usershow -n "$_input"; then - f_show_msg "$msg_login_already_used" "$_input" - continue - fi - - pw_name="$_input" - break - done - save_flag=1 - - f_dprintf "pw_name: [$cur_pw_name]->[$pw_name]" - - return $SUCCESS -} - -# f_dialog_input_password -# -# Prompt the user to enter a password (twice). -# -f_dialog_input_password() -{ - local hline="$hline_alnum_punc_tab_enter" - local msg size rmsg rsize - - msg=$( printf "$msg_password" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "" \ - "$hline" ) - - rmsg=$( printf "$msg_reenter_password" ) - rsize=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$rmsg" \ - "" \ - "$hline" ) - - # - # Loop until the user provides taint-free/valid input - # - local retval _password1 _password2 - while :; do - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --insecure \ - --passwordbox \"\$msg\" $size \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _password1=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --insecure \ - --passwordbox \"\$rmsg\" $rsize \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _password2=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - # Check for password mismatch - if [ "$_password1" != "$_password2" ]; then - f_show_msg "$msg_passwords_do_not_match" - continue - fi - - # Check for NULL entry - if [ ! "$_password1" ]; then - f_dialog_yesno \ - "$msg_disable_password_auth_for_account" || - continue - pw_password_disable=1 - else - pw_password_disable= - fi - - pw_password="$_password1" - break - done - save_flag=1 - - f_dprintf "pw_password: [$cur_pw_password]->[$pw_password]" - - return $SUCCESS -} - -# f_dialog_input_gecos [$gecos] -# -# Allow the user to enter new GECOS information for a given user. This -# information is commonly used to store the ``Full Name'' of the user. If the -# user does not cancel or press ESC, the $pw_gecos variable will hold the -# newly-configured value upon return. -# -f_dialog_input_gecos() -{ - local msg size retval _input="$1" - local hline="$hline_alnum_punc_tab_enter" - - msg=$( printf "$msg_full_name" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - pw_gecos="$_input" - save_flag=1 - - f_dprintf "pw_gecos: [$cur_pw_gecos]->[$pw_gecos]" - - return $SUCCESS -} - -# f_dialog_input_uid [$uid] -# -# Allow the user to enter a new UID for a given user. If the user does not -# cancel or press ESC, the $pw_uid variable will hold the newly-configured -# value upon return. -# -f_dialog_input_uid() -{ - local msg size retval _input="$1" - local hline="$hline_num_tab_enter" - - msg=$( printf "$msg_user_id_leave_empty_for_default" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - pw_uid="$_input" - save_flag=1 - - f_dprintf "pw_uid: [$cur_pw_uid]->[$pw_uid]" - - return $SUCCESS -} - -# f_dialog_input_gid [$gid] -# -# Allow the user to enter a new primary GID for a given user. If the user does -# not cancel or press ESC, the $pw_gid variable will hold the newly-configured -# value upon return. -# -f_dialog_input_gid() -{ - local msg size retval _input="$1" - local hline="$hline_num_tab_enter" - - msg=$( printf "$msg_group_id_leave_empty_for_default" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - pw_gid="$_input" - save_flag=1 - - f_dprintf "pw_gid: [$cur_pw_gid]->[$pw_gid]" - - return $SUCCESS -} - -# f_dialog_input_class [$class] -# -# Allow the user to enter a new login class for a given user. If the user does -# not cancel or press ESC, the $pw_class variable will hold the newly- -# configured value upon return. -# -f_dialog_input_class() -{ - local msg size retval _input="$1" - local hline="$hline_alnum_tab_enter" - - msg=$( printf "$msg_login_class" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - pw_class="$_input" - save_flag=1 - - f_dprintf "pw_class: [$cur_pw_class]->[$pw_class]" - - return $SUCCESS -} - -# f_dialog_input_change [$seconds] -# -# Allow the user to enter a date/time (in number-of-seconds since the `epoch') -# for when a given user's password must be changed. If the user does not cancel -# or press ESC, the $pw_password_expire variable will hold the newly- -# configured value upon return. -# -f_dialog_input_change() -{ - local calendar_size timebox_size - local msg menu_size size retval _input="$1" - local hline="$hline_num_arrows_tab_enter" - - local menu_list=" - '1' '$msg_password_does_not_expire' - '2' '$msg_edit_date_time_with_a_calendar' - '3' '$msg_enter_number_of_days_into_the_future' - '4' '$msg_enter_value_manually' - " # END-QUOTE - - # - # Loop until the user provides taint-free/cancellation-free input - # - while :; do - msg="$msg_password_expires_on" - menu_size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$msg\" \ - \"\$hline\" \ - $menu_list ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$msg\" $menu_size \ - $menu_list \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ - retval=$? - date_type=$( f_dialog_menutag ) - f_dprintf "retval=$retval date_type=[$date_type]" - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - case "$date_type" in - 1) # Password does not expire - _input="" - break ;; - - 2) # Edit date/time with a calendar - local _input_date _input_time ret_date ret_time - - local secs="$_input" - { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= - _input_date=$( date -j -f "%s" -- "$secs" \ - "+%d %m %Y" 2> /dev/null ) - calendar_size=$( f_dialog_calendar_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --calendar \"\$msg\" $calendar_size \ - $_input_date \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - ret_date=$( f_dialog_inputstr ) - f_dprintf "retval=$retval ret_date=[$ret_date]" - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input_time= - [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) - timebox_size=$( f_dialog_timebox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --timebox \"\$msg\" $timebox_size \ - $_input_time \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - ret_time=$( f_dialog_inputstr ) - f_dprintf "retval=$retval ret_time=[$ret_time]" - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input=$( date \ - -j -f "%d/%m/%Y %T" \ - -- "$ret_date $ret_time" \ - +%s 2> /dev/null ) - f_dprintf "_input=[$_input]" - break ;; - - 3) # Enter number of days into the future - local ret_days seconds="$( date +%s )" - - f_isinteger "$_input" || _input=0 - [ $_input -gt 0 -a $_input -gt $seconds ] && - ret_days=$(( ( $_input - $seconds ) / 86400 )) - f_isinteger "$ret_days" && - ret_days=$(( $ret_days + 1 )) - - msg="$msg_password_expires_in_how_many_days" - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$ret_days" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$ret_days\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - ret_days=$( f_dialog_inputstr ) - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - # Taint-check the user's input - if ! f_isinteger "$ret_days"; then - f_show_msg "$msg_invalid_number_of_days" - continue - fi - - f_dprintf "ret_days=[$ret_days]" - case "$ret_days" in - [-+]*) _input=$( date -v${ret_days}d +%s );; - 0) _input=$( date +%s );; - *) _input=$( date -v+${ret_days}d +%s );; - esac - f_dprintf "_input=[$_input]" - break ;; - - 4) # Enter value manually - local ret_secs - - msg=$( printf "$msg_number_of_seconds_since_epoch" \ - "$( date -r 1 "+%c %Z" )" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - ret_secs=$( f_dialog_inputstr ) - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input="$ret_secs" - - # Taint-check the user's input - if ! f_isinteger "${_input:-0}"; then - f_show_msg "$msg_invalid_number_of_seconds" - continue - fi - - f_dprintf "_input=[$_input]" - break ;; - - esac - - done # Loop forever - - pw_password_expire="$_input" - save_flag=1 - - local debug=pw_password_expire - f_dprintf "$debug: [$cur_pw_password_expire]->[$pw_password_expire]" - - return $SUCCESS -} - -# f_dialog_input_expire [$seconds] -# -# Allow the user to enter a date/time (in number-of-seconds since the `epoch') -# for when a given user's account should become expired. If the user does not -# cancel or press ESC, the $pw_account_expire variable will hold the newly- -# configured value upon return. -# -f_dialog_input_expire() -{ - local calendar_size timebox_size - local msg menu_size size retval _input="$1" - local hline="$hline_num_arrows_tab_enter" - - local menu_list=" - '1' '$msg_account_does_not_expire' - '2' '$msg_edit_date_time_with_a_calendar' - '3' '$msg_enter_number_of_days_into_the_future' - '4' '$msg_enter_value_manually' - " # END-QUOTE - - # - # Loop until the user provides taint-free/cancellation-free input - # - while :; do - msg="$msg_account_expires_on" - menu_size=$( eval f_dialog_menu_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$msg\" \ - \"\$hline\" \ - $menu_list ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --menu \"\$msg\" $menu_size \ - $menu_list \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ - retval=$? - date_type=$( f_dialog_menutag ) - f_dprintf "retval=$retval date_type=[$date_type]" - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - case "$date_type" in - 1) # Account does not expire - _input="" - break ;; - - 2) # Edit date/time with a calendar - local _input_date _input_time ret_date ret_time - - local secs="$_input" - { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= - _input_date=$( date -j -f "%s" -- "$secs" \ - "+%d %m %Y" 2> /dev/null ) - calendar_size=$( f_dialog_calendar_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --calendar \"\$msg\" $calendar_size \ - $_input_date \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - ret_date=$( f_dialog_inputstr ) - f_dprintf "retval=$retval ret_date=[$ret_date]" - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input_time= - [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) - timebox_size=$( f_dialog_timebox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --timebox \"\$msg\" $timebox_size \ - $_input_time \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - ret_time=$( f_dialog_inputstr ) - f_dprintf "retval=$retval ret_time=[$ret_time]" - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input=$( date \ - -j -f "%d/%m/%Y %T" \ - -- "$ret_date $ret_time" \ - +%s 2> /dev/null ) - f_dprintf "_input=[$_input]" - break ;; - - 3) # Enter number of days into the future - local ret_days seconds="$( date +%s )" - - f_isinteger "$_input" || _input=0 - [ $_input -gt 0 -a $_input -gt $seconds ] && - ret_days=$(( ( $_input - $seconds ) / 86400 )) - f_isinteger "$ret_days" && - ret_days=$(( $ret_days + 1 )) - - msg="$msg_account_expires_in_how_many_days" - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$ret_days" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$ret_days\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - retval=$? - ret_days=$( f_dialog_inputstr ) - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - # Taint-check the user's input - if ! f_isinteger "$ret_days"; then - f_show_msg "$msg_invalid_number_of_days" - continue - fi - - f_dprintf "ret_days=[$ret_days]" - case "$ret_days" in - [-+]*) _input=$( date -v${ret_days}d +%s );; - 0) _input=$( date +%s );; - *) _input=$( date -v+${ret_days}d +%s );; - esac - f_dprintf "_input=[$_input]" - break ;; - - 4) # Enter value manually - local ret_secs - - msg=$( printf "$msg_number_of_seconds_since_epoch" \ - "$( date -r 1 "+%c %Z" )" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - ret_secs=$( f_dialog_inputstr ) - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue - - _input="$ret_secs" - - # Taint-check the user's input - if ! f_isinteger "${_input:-0}"; then - f_show_msg "$msg_invalid_number_of_seconds" - continue - fi - - f_dprintf "_input=[$_input]" - break ;; - - esac - - done # Loop forever - - pw_account_expire="$_input" - save_flag=1 - - local debug=pw_account_expire - f_dprintf "$debug: [$cur_pw_account_expire]->[$pw_account_expire]" - - return $SUCCESS -} - -# f_dialog_input_home_dir [$home_dir] -# -# Allow the user to enter a new home directory for a given user. If the user -# does not cancel or press ESC, the $pw_home_dir variable will hold the newly- -# configured value upon return. -# -f_dialog_input_home_dir() -{ - local msg size retval _input="$1" - local hline="$hline_alnum_punc_tab_enter" - - msg=$( printf "$msg_home_directory" ) - size=$( f_dialog_inputbox_size \ - "$DIALOG_TITLE" \ - "$DIALOG_BACKTITLE" \ - "$msg" \ - "$_input" \ - "$hline" ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --inputbox \"\$msg\" $size \ - \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - pw_home_dir="$_input" - save_flag=1 - - f_dprintf "pw_home_dir: [$cur_pw_home_dir]->[$pw_home_dir]" - - return $SUCCESS -} - -# f_dialog_input_home_create -# -# Prompt the user to confirm creation of a given user's home directory. If the -# user does not cancel (by choosing "No") or press ESC, the $pw_home_create -# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return -# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. -# -f_dialog_input_home_create() -{ - local retval - - f_dialog_yesno "$msg_create_home_directory" - retval=$? - - if [ $retval -eq $SUCCESS ]; then - pw_home_create="$msg_yes" - else - pw_home_create="$msg_no" - fi - save_flag=1 - - f_dprintf "pw_home_create: [$cur_pw_home_create]->[$pw_home_create]" - - [ $retval -ne 255 ] # return failure if user pressed ESC -} - -# f_dialog_input_group_delete -# -# Prompt the user to confirm deletion of a given user's primary group. If the -# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete -# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return -# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. -# -f_dialog_input_group_delete() -{ - local retval - - if f_isinteger "$pw_gid"; then - if [ $pw_gid -lt 1000 ]; then - f_dialog_noyes "$msg_delete_primary_group" - else - f_dialog_yesno "$msg_delete_primary_group" - fi - elif [ "$pw_gid" ]; then - local gid=0 - gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' ) - if f_isinteger "$gid" && [ $gid -lt 1000 ]; then - f_dialog_noyes "$msg_delete_primary_group" - else - f_dialog_yesno "$msg_delete_primary_group" - fi - else - f_dialog_yesno "$msg_delete_primary_group" - fi - retval=$? - - if [ $retval -eq $SUCCESS ]; then - pw_group_delete="$msg_yes" - else - pw_group_delete="$msg_no" - fi - save_flag=1 - - f_dprintf "pw_group_delete: [$cur_pw_group_delete]->[$pw_group_delete]" - - [ $retval -ne 255 ] # return failure if user pressed ESC -} - -# f_dialog_input_home_delete -# -# Prompt the user to confirm deletion of a given user's home directory. If the -# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete -# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return -# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. -# -f_dialog_input_home_delete() -{ - local retval - - f_dialog_yesno "$msg_delete_home_directory" - retval=$? - - if [ $retval -eq $SUCCESS ]; then - pw_home_delete="$msg_yes" - else - pw_home_delete="$msg_no" - fi - save_flag=1 - - f_dprintf "pw_home_delete: [$cur_pw_home_delete]->[$pw_home_delete]" - - [ $retval -ne 255 ] # return failure if user pressed ESC -} - -# f_dialog_input_dotfiles_create -# -# Prompt the user to confirm population of a given user's home directory with -# sample dotfiles. If the user does not cancel (by choosing "No") or press ESC, -# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise -# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to -# be i18n-compatible. -# -f_dialog_input_dotfiles_create() -{ - local retval - - f_dialog_yesno "$msg_create_dotfiles" - retval=$? - - if [ $retval -eq $SUCCESS ]; then - pw_dotfiles_create="$msg_yes" - else - pw_dotfiles_create="$msg_no" - fi - save_flag=1 - - local debug="pw_dotfiles_create:" - f_dprintf "$debug: [$cur_pw_dotfiles_create]->[$pw_dotfiles_create]" - - [ $retval -ne 255 ] # return failure if user pressed ESC -} - -# f_dialog_input_shell [$shell] -# -# Allow the user to select a new login shell for a given user. If the user does -# not cancel or press ESC, the $pw_home_dir variable will hold the newly- -# configured value upon return. -# -# -f_dialog_input_shell() -{ - local size retval shells shell_list _input="$1" - local hline="$hline_arrows_space_tab_enter" - local prompt="$msg_select_login_shell" - - shells=$( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" ) - shell_list=$( - for shell in $shells; do - if [ "$shell" = "$_input" ]; then - echo "'$shell' '' 'on'" - else - echo "'$shell' '' 'off'" - fi - done - ) - - size=$( eval f_dialog_radiolist_size \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $shell_list ) - - eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --radiolist \"\$prompt\" $size \ - $shell_list \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ - - retval=$? - _input=$( f_dialog_inputstr ) - - # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval - - pw_shell="$_input" - save_flag=1 - - f_dprintf "pw_shell: [$cur_pw_shell]->[$pw_shell]" - - return $SUCCESS -} - -fi # ! $_USERMGMT_USER_INPUT_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/share/Makefile b/usr.sbin/bsdconfig/usermgmt/share/Makefile new file mode 100644 index 0000000000000..d6b9e3a577468 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/share/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +NO_OBJ= + +FILESDIR= ${SHAREDIR}/bsdconfig/usermgmt +FILES= group_input.subr user_input.subr + +beforeinstall: + mkdir -p ${DESTDIR}${FILESDIR} + +.include diff --git a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr new file mode 100644 index 0000000000000..0c616967b48fd --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr @@ -0,0 +1,437 @@ +if [ ! "$_USERMGMT_GROUP_INPUT_SUBR" ]; then _USERMGMT_GROUP_INPUT_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/include/messages.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ FUNCTIONS + +# f_input_group $group +# +# Given $group name or id, create the environment variables group_name, +# group_gid, and group_members (and group_password is reset to NULL). +# +f_input_group() +{ + eval $( pw groupshow "$1" | awk -F: ' + { + printf "group_name='\'%s\''\n", $1 + printf "group_password=\n" + printf "group_gid='\'%s\''\n", $3 + printf "group_members='\'%s\''\n", $4 + exit + }' ) +} + +# f_dialog_menu_group_list +# +# Allows the user to select a group from a list. +# +f_dialog_menu_group_list() +{ + local menu_list size + local hline="$hline_alnum_punc_tab_enter" + + menu_list=" + 'X $msg_exit' '' + " # END-QUOTE + + # Add groups from group(5) + menu_list="$menu_list $( pw groupshow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { + printf "'\'%s\'\ \'%s\''\n", $1, $1 + }' + )" + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_input_group_name [$group_name] +# +# Allows the user to enter a new groupname for a given group. If the user does +# not cancel or press ESC, the $group_name variable will hold the +# newly-configured value upon return. +# +# If $cur_group_name is defined, the user can enter that and by-pass error- +# checking (allowing the user to "revert" to an old value without, for example, +# being told that the groupname already exists). +# +f_dialog_input_group_name() +{ + local msg="$( printf "$msg_group" )" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size retval _name="$1" _input="$1" + while :; do + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for no-change + [ "$_input" = "$_name" ] && return $SUCCESS + + # Check for reversion + if [ "$_input" = "$cur_group_name" ]; then + group_name="$cur_group_name" + return $SUCCESS + fi + + # Check for NULL entry + if [ ! "$_input" ]; then + f_show_msg "$msg_group_is_empty" + continue + fi + + # Check for invalid entry + if ! echo "$_input" | grep -q "^[[:alpha:]]"; then + f_show_msg "$msg_group_must_start_with_letter" + continue + fi + + # Check for duplicate entry + if f_quietly pw groupshow -n "$_input"; then + f_show_msg "$msg_group_already_used" "$_input" + continue + fi + + group_name="$_input" + break + done + save_flag=1 + + f_dprintf "group_name: [$cur_group_name]->[$group_name]" + + return $SUCCESS +} + +# f_dialog_input_group_password +# +# Prompt the user to enter a password (twice). +# +f_dialog_input_group_password() +{ + local hline="$hline_alnum_punc_tab_enter" + local msg size rmsg rsize + + msg=$( printf "$msg_group_password" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "" \ + "$hline" ) + + rmsg=$( printf "$msg_reenter_group_password" ) + rsize=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$rmsg" \ + "" \ + "$hline" ) + + # + # Loop until the user provides taint-free/valid input + # + local retval _password1 _password2 + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$msg\" $size \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password1=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$rmsg\" $rsize \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password2=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for password mismatch + if [ "$_password1" != "$_password2" ]; then + f_show_msg "$msg_group_passwords_do_not_match" + continue + fi + + # Check for NULL entry + if [ ! "$_password1" ]; then + f_dialog_yesno \ + "$msg_disable_password_auth_for_group" || + continue + pw_group_password_disable=1 + else + pw_group_password_disable= + fi + + group_password="$_password1" + break + done + save_flag=1 + + f_dprintf "group_password: [$cur_group_password]->[$group_password]" + + return $SUCCESS +} + +# f_dialog_input_group_gid [$group_gid] +# +# Allow the user to enter a new GID for a given group. If the user does not +# cancel or press ESC, the $group_gid variable will hold the newly-configured +# value upon return. +# +f_dialog_input_group_gid() +{ + local msg size retval _input="$1" + local hline="$hline_num_tab_enter" + + msg=$( printf "$msg_group_id_leave_empty_for_default" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + group_gid="$_input" + save_flag=1 + + f_dprintf "group_gid: [$cur_group_gid]->[$group_gid]" + + return $SUCCESS +} + +# f_dialog_input_group_members [$group_members] +# +# Allow the user to modify a list of members for a given group. If the user does +# not cancel or press ESC, the $group_members variable will hold the newly- +# configured value upon return. +# +f_dialog_input_group_members() +{ + local menu_choice msg size retval _input="$1" + local hline="$hline_num_arrows_tab_enter" + local user + local menu_list + local all_users_valid + local _group_members + local checklist_users + + menu_list=" + 'X' '$msg_continue' + '1' '$msg_select_group_members_from_list' + '2' '$msg_enter_group_members_manually' + " # END-QUOTE + + while :; do + msg="$msg_group_members:" + menu_size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $menu_size \ + $menu_list \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + retval=$? + menu_choice=$( f_dialog_menutag ) + f_dprintf "retval=$retval menu_choice=[$menu_choice]" + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + case "$menu_choice" in + X) # Exit + break ;; + 1) # Select Group Members from a list + user_list=$( pw usershow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { printf "%s\n", $1 }' ) + checklist_users= + for user in $user_list; do + checklist_users="$checklist_users $user \"\"" + if echo "$_input" | grep -q "\<$user\>"; then + checklist_users="$checklist_users on" + else + checklist_users="$checklist_users off" + fi + done + + size=$( eval f_dialog_radiolist_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $checklist_users ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --separate-output \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --checklist \"\$msg\" $size \ + $checklist_users \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + _group_members=$( f_dialog_inputstr | tr '\n' ' ' | + sed -e 's/[[:space:]]\{1,\}/,/g;s/^,//;s/,$//' ) + + # Return to previous menu if user has either + # pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$_group_members" + ;; + 2) # Enter Group Members manually + hline="$hline_num_tab_enter" + msg=$( + printf "$msg_group_members ($msg_separated_by_commas)" + ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _group_members=$( f_dialog_inputstr ) + + # Return to previous menu if user has either + # pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$_group_members" + ;; + esac + done + + group_members="$_input" + save_flag=1 + f_dprintf "group_members: [$cur_group_members]->[$group_members]" + + return $SUCCESS +} + +fi # ! $_USERMGMT_GROUP_INPUT_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr new file mode 100644 index 0000000000000..d27b9be3e4960 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -0,0 +1,1180 @@ +if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_INPUT_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/include/messages.subr +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# Default location of shells(5) +# +: ${ETC_SHELLS:=/etc/shells} + +############################################################ FUNCTIONS + +# f_get_member_groups $user +# +# Get a list of additional groups $user is a member of in group(5). +# +f_get_member_groups() +{ + echo $( pw groupshow -a | awk -F: "/[:,]$1(,|\$)/{print \$1}" ) +} + +# f_input_user $user +# +# Given $user name or id, create the environment variables pw_name, pw_uid, +# pw_gid, pw_class, pw_password_expire, pw_account_expire, pw_gecos, +# pw_home_dir, pw_shell, and pw_member_groups (and pw_password is reset to +# NULL). +# +f_input_user() +{ + local user="$1" + eval $( pw usershow "$user" | awk -F: ' + { + printf "pw_name='\'%s\''\n", $1 + printf "pw_password=\n" + printf "pw_uid='\'%s\''\n", $3 + printf "pw_gid='\'%s\''\n", $4 + printf "pw_class='\'%s\''\n", $5 + printf "pw_password_expire='\'%s\''\n", $6 + printf "pw_account_expire='\'%s\''\n", $7 + printf "pw_gecos='\'%s\''\n", $8 + printf "pw_home_dir='\'%s\''\n", $9 + printf "pw_shell='\'%s\''\n", $10 + }' ) + pw_member_groups=$( f_get_member_groups "$user" ) +} + +# f_dialog_menu_user_list +# +# Allows the user to select a login from a list. +# +f_dialog_menu_user_list() +{ + local menu_list size + local hline="$hline_alnum_punc_tab_enter" + + menu_list=" + 'X $msg_exit' '' + " # END-QUOTE + + # Add users from passwd(5) + menu_list="$menu_list $( pw usershow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { + printf "'\'%s\'\ \'%s\''\n", $1, $8 + }' + )" + + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --clear --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\" $size $menu_list \ + 2> "$DIALOG_TMPDIR/dialog.menu.$$" +} + +# f_dialog_input_member_groups [$member_groups] +# +# Allows the user to edit group memberships for a given user. If the user does +# not cancel or press ESC, the $pw_member_groups variable will hold the newly- +# configured value upon return. +# +f_dialog_input_member_groups() +{ + local msg="$( printf "$msg_member_of_groups" )" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size retval all_groups checklist_groups="" _member_groups="$1" + all_groups=$( pw groupshow -a | awk -F: ' + !/^[[:space:]]*(#|$)/ { + printf "%s\n", $1 + }' + ) + for grp in $all_groups; do + checklist_groups="$checklist_groups $grp $grp" + if echo "$_member_groups" | grep -q "\<$grp\>"; then + checklist_groups="$checklist_groups on" + else + checklist_groups="$checklist_groups off" + fi + done + + while :; do + size=$( eval f_dialog_radiolist_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\" \ + \"\$hline\" \ + $checklist_groups ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --separate-output \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --checklist \"\$msg\" $size \ + $checklist_groups \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _member_groups=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # + # Validate each of the groups the user has entered + # + local group all_groups_valid=1 + for group in $_member_groups; do + if ! f_quietly pw groupshow -n "$group"; then + f_show_msg "$msg_group_not_found" "$group" + all_groups_valid= + break + fi + done + [ "$all_groups_valid" ] || continue + + pw_member_groups="$_member_groups" + break + done + save_flag=1 + + local debug="pw_member_groups:" + f_dprintf "$debug [$cur_pw_member_groups]->[$pw_member_groups]" + + return $SUCCESS +} + +# f_dialog_input_name [$name] +# +# Allows the user to enter a new username for a given user. If the user does +# not cancel or press ESC, the $pw_name variable will hold the newly-configured +# value upon return. +# +# If $cur_pw_name is defined, the user can enter that and by-pass error- +# checking (allowing the user to "revert" to an old value without, for example, +# being told that the username already exists). +# +f_dialog_input_name() +{ + local msg="$( printf "$msg_login" )" + local hline="$hline_alnum_tab_enter" + + # + # Loop until the user provides taint-free/valid input + # + local size retval _name="$1" _input="$1" + while :; do + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for no-change + [ "$_input" = "$_name" ] && return $SUCCESS + + # Check for reversion + if [ "$_input" = "$cur_pw_name" ]; then + pw_name="$cur_pw_name" + return $SUCCESS + fi + + # Check for NULL entry + if [ ! "$_input" ]; then + f_show_msg "$msg_login_is_empty" + continue + fi + + # Check for invalid entry + if ! echo "$_input" | grep -q "^[[:alpha:]]"; then + f_show_msg "$msg_login_must_start_with_letter" + continue + fi + + # Check for duplicate entry + if f_quietly pw usershow -n "$_input"; then + f_show_msg "$msg_login_already_used" "$_input" + continue + fi + + pw_name="$_input" + break + done + save_flag=1 + + f_dprintf "pw_name: [$cur_pw_name]->[$pw_name]" + + return $SUCCESS +} + +# f_dialog_input_password +# +# Prompt the user to enter a password (twice). +# +f_dialog_input_password() +{ + local hline="$hline_alnum_punc_tab_enter" + local msg size rmsg rsize + + msg=$( printf "$msg_password" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "" \ + "$hline" ) + + rmsg=$( printf "$msg_reenter_password" ) + rsize=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$rmsg" \ + "" \ + "$hline" ) + + # + # Loop until the user provides taint-free/valid input + # + local retval _password1 _password2 + while :; do + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$msg\" $size \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password1=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --insecure \ + --passwordbox \"\$rmsg\" $rsize \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _password2=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + # Check for password mismatch + if [ "$_password1" != "$_password2" ]; then + f_show_msg "$msg_passwords_do_not_match" + continue + fi + + # Check for NULL entry + if [ ! "$_password1" ]; then + f_dialog_yesno \ + "$msg_disable_password_auth_for_account" || + continue + pw_password_disable=1 + else + pw_password_disable= + fi + + pw_password="$_password1" + break + done + save_flag=1 + + f_dprintf "pw_password: [$cur_pw_password]->[$pw_password]" + + return $SUCCESS +} + +# f_dialog_input_gecos [$gecos] +# +# Allow the user to enter new GECOS information for a given user. This +# information is commonly used to store the ``Full Name'' of the user. If the +# user does not cancel or press ESC, the $pw_gecos variable will hold the +# newly-configured value upon return. +# +f_dialog_input_gecos() +{ + local msg size retval _input="$1" + local hline="$hline_alnum_punc_tab_enter" + + msg=$( printf "$msg_full_name" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_gecos="$_input" + save_flag=1 + + f_dprintf "pw_gecos: [$cur_pw_gecos]->[$pw_gecos]" + + return $SUCCESS +} + +# f_dialog_input_uid [$uid] +# +# Allow the user to enter a new UID for a given user. If the user does not +# cancel or press ESC, the $pw_uid variable will hold the newly-configured +# value upon return. +# +f_dialog_input_uid() +{ + local msg size retval _input="$1" + local hline="$hline_num_tab_enter" + + msg=$( printf "$msg_user_id_leave_empty_for_default" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_uid="$_input" + save_flag=1 + + f_dprintf "pw_uid: [$cur_pw_uid]->[$pw_uid]" + + return $SUCCESS +} + +# f_dialog_input_gid [$gid] +# +# Allow the user to enter a new primary GID for a given user. If the user does +# not cancel or press ESC, the $pw_gid variable will hold the newly-configured +# value upon return. +# +f_dialog_input_gid() +{ + local msg size retval _input="$1" + local hline="$hline_num_tab_enter" + + msg=$( printf "$msg_group_id_leave_empty_for_default" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_gid="$_input" + save_flag=1 + + f_dprintf "pw_gid: [$cur_pw_gid]->[$pw_gid]" + + return $SUCCESS +} + +# f_dialog_input_class [$class] +# +# Allow the user to enter a new login class for a given user. If the user does +# not cancel or press ESC, the $pw_class variable will hold the newly- +# configured value upon return. +# +f_dialog_input_class() +{ + local msg size retval _input="$1" + local hline="$hline_alnum_tab_enter" + + msg=$( printf "$msg_login_class" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_class="$_input" + save_flag=1 + + f_dprintf "pw_class: [$cur_pw_class]->[$pw_class]" + + return $SUCCESS +} + +# f_dialog_input_change [$seconds] +# +# Allow the user to enter a date/time (in number-of-seconds since the `epoch') +# for when a given user's password must be changed. If the user does not cancel +# or press ESC, the $pw_password_expire variable will hold the newly- +# configured value upon return. +# +f_dialog_input_change() +{ + local calendar_size timebox_size + local msg menu_size size retval _input="$1" + local hline="$hline_num_arrows_tab_enter" + + local menu_list=" + '1' '$msg_password_does_not_expire' + '2' '$msg_edit_date_time_with_a_calendar' + '3' '$msg_enter_number_of_days_into_the_future' + '4' '$msg_enter_value_manually' + " # END-QUOTE + + # + # Loop until the user provides taint-free/cancellation-free input + # + while :; do + msg="$msg_password_expires_on" + menu_size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $menu_size \ + $menu_list \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + retval=$? + date_type=$( f_dialog_menutag ) + f_dprintf "retval=$retval date_type=[$date_type]" + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + case "$date_type" in + 1) # Password does not expire + _input="" + break ;; + + 2) # Edit date/time with a calendar + local _input_date _input_time ret_date ret_time + + local secs="$_input" + { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= + _input_date=$( date -j -f "%s" -- "$secs" \ + "+%d %m %Y" 2> /dev/null ) + calendar_size=$( f_dialog_calendar_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --calendar \"\$msg\" $calendar_size \ + $_input_date \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_date=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_date=[$ret_date]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input_time= + [ "$secs" ] && _input_time=$( date -j \ + -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) + timebox_size=$( f_dialog_timebox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --timebox \"\$msg\" $timebox_size \ + $_input_time \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_time=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_time=[$ret_time]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input=$( date \ + -j -f "%d/%m/%Y %T" \ + -- "$ret_date $ret_time" \ + +%s 2> /dev/null ) + f_dprintf "_input=[$_input]" + break ;; + + 3) # Enter number of days into the future + local ret_days seconds="$( date +%s )" + + f_isinteger "$_input" || _input=0 + [ $_input -gt 0 -a $_input -gt $seconds ] && + ret_days=$(( ( $_input - $seconds ) / 86400 )) + f_isinteger "$ret_days" && + ret_days=$(( $ret_days + 1 )) + + msg="$msg_password_expires_in_how_many_days" + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$ret_days" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$ret_days\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_days=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + # Taint-check the user's input + if ! f_isinteger "$ret_days"; then + f_show_msg "$msg_invalid_number_of_days" + continue + fi + + f_dprintf "ret_days=[$ret_days]" + case "$ret_days" in + [-+]*) _input=$( date -v${ret_days}d +%s );; + 0) _input=$( date +%s );; + *) _input=$( date -v+${ret_days}d +%s );; + esac + f_dprintf "_input=[$_input]" + break ;; + + 4) # Enter value manually + local ret_secs + + msg=$( printf "$msg_number_of_seconds_since_epoch" \ + "$( date -r 1 "+%c %Z" )" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + ret_secs=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$ret_secs" + + # Taint-check the user's input + if ! f_isinteger "${_input:-0}"; then + f_show_msg "$msg_invalid_number_of_seconds" + continue + fi + + f_dprintf "_input=[$_input]" + break ;; + + esac + + done # Loop forever + + pw_password_expire="$_input" + save_flag=1 + + local debug=pw_password_expire + f_dprintf "$debug: [$cur_pw_password_expire]->[$pw_password_expire]" + + return $SUCCESS +} + +# f_dialog_input_expire [$seconds] +# +# Allow the user to enter a date/time (in number-of-seconds since the `epoch') +# for when a given user's account should become expired. If the user does not +# cancel or press ESC, the $pw_account_expire variable will hold the newly- +# configured value upon return. +# +f_dialog_input_expire() +{ + local calendar_size timebox_size + local msg menu_size size retval _input="$1" + local hline="$hline_num_arrows_tab_enter" + + local menu_list=" + '1' '$msg_account_does_not_expire' + '2' '$msg_edit_date_time_with_a_calendar' + '3' '$msg_enter_number_of_days_into_the_future' + '4' '$msg_enter_value_manually' + " # END-QUOTE + + # + # Loop until the user provides taint-free/cancellation-free input + # + while :; do + msg="$msg_account_expires_on" + menu_size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $menu_size \ + $menu_list \ + 2> $DIALOG_TMPDIR/dialog.menu.$$ + retval=$? + date_type=$( f_dialog_menutag ) + f_dprintf "retval=$retval date_type=[$date_type]" + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + case "$date_type" in + 1) # Account does not expire + _input="" + break ;; + + 2) # Edit date/time with a calendar + local _input_date _input_time ret_date ret_time + + local secs="$_input" + { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= + _input_date=$( date -j -f "%s" -- "$secs" \ + "+%d %m %Y" 2> /dev/null ) + calendar_size=$( f_dialog_calendar_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --calendar \"\$msg\" $calendar_size \ + $_input_date \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_date=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_date=[$ret_date]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input_time= + [ "$secs" ] && _input_time=$( date -j \ + -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) + timebox_size=$( f_dialog_timebox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --timebox \"\$msg\" $timebox_size \ + $_input_time \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_time=$( f_dialog_inputstr ) + f_dprintf "retval=$retval ret_time=[$ret_time]" + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input=$( date \ + -j -f "%d/%m/%Y %T" \ + -- "$ret_date $ret_time" \ + +%s 2> /dev/null ) + f_dprintf "_input=[$_input]" + break ;; + + 3) # Enter number of days into the future + local ret_days seconds="$( date +%s )" + + f_isinteger "$_input" || _input=0 + [ $_input -gt 0 -a $_input -gt $seconds ] && + ret_days=$(( ( $_input - $seconds ) / 86400 )) + f_isinteger "$ret_days" && + ret_days=$(( $ret_days + 1 )) + + msg="$msg_account_expires_in_how_many_days" + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$ret_days" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$ret_days\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + retval=$? + ret_days=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + # Taint-check the user's input + if ! f_isinteger "$ret_days"; then + f_show_msg "$msg_invalid_number_of_days" + continue + fi + + f_dprintf "ret_days=[$ret_days]" + case "$ret_days" in + [-+]*) _input=$( date -v${ret_days}d +%s );; + 0) _input=$( date +%s );; + *) _input=$( date -v+${ret_days}d +%s );; + esac + f_dprintf "_input=[$_input]" + break ;; + + 4) # Enter value manually + local ret_secs + + msg=$( printf "$msg_number_of_seconds_since_epoch" \ + "$( date -r 1 "+%c %Z" )" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + ret_secs=$( f_dialog_inputstr ) + + # Return to menu if either ESC or Cancel/No + [ $retval -eq $SUCCESS ] || continue + + _input="$ret_secs" + + # Taint-check the user's input + if ! f_isinteger "${_input:-0}"; then + f_show_msg "$msg_invalid_number_of_seconds" + continue + fi + + f_dprintf "_input=[$_input]" + break ;; + + esac + + done # Loop forever + + pw_account_expire="$_input" + save_flag=1 + + local debug=pw_account_expire + f_dprintf "$debug: [$cur_pw_account_expire]->[$pw_account_expire]" + + return $SUCCESS +} + +# f_dialog_input_home_dir [$home_dir] +# +# Allow the user to enter a new home directory for a given user. If the user +# does not cancel or press ESC, the $pw_home_dir variable will hold the newly- +# configured value upon return. +# +f_dialog_input_home_dir() +{ + local msg size retval _input="$1" + local hline="$hline_alnum_punc_tab_enter" + + msg=$( printf "$msg_home_directory" ) + size=$( f_dialog_inputbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$msg" \ + "$_input" \ + "$hline" ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --inputbox \"\$msg\" $size \ + \"\$_input\" \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_home_dir="$_input" + save_flag=1 + + f_dprintf "pw_home_dir: [$cur_pw_home_dir]->[$pw_home_dir]" + + return $SUCCESS +} + +# f_dialog_input_home_create +# +# Prompt the user to confirm creation of a given user's home directory. If the +# user does not cancel (by choosing "No") or press ESC, the $pw_home_create +# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return +# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. +# +f_dialog_input_home_create() +{ + local retval + + f_dialog_yesno "$msg_create_home_directory" + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_home_create="$msg_yes" + else + pw_home_create="$msg_no" + fi + save_flag=1 + + f_dprintf "pw_home_create: [$cur_pw_home_create]->[$pw_home_create]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_group_delete +# +# Prompt the user to confirm deletion of a given user's primary group. If the +# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete +# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return +# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. +# +f_dialog_input_group_delete() +{ + local retval + + if f_isinteger "$pw_gid"; then + if [ $pw_gid -lt 1000 ]; then + f_dialog_noyes "$msg_delete_primary_group" + else + f_dialog_yesno "$msg_delete_primary_group" + fi + elif [ "$pw_gid" ]; then + local gid=0 + gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' ) + if f_isinteger "$gid" && [ $gid -lt 1000 ]; then + f_dialog_noyes "$msg_delete_primary_group" + else + f_dialog_yesno "$msg_delete_primary_group" + fi + else + f_dialog_yesno "$msg_delete_primary_group" + fi + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_group_delete="$msg_yes" + else + pw_group_delete="$msg_no" + fi + save_flag=1 + + f_dprintf "pw_group_delete: [$cur_pw_group_delete]->[$pw_group_delete]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_home_delete +# +# Prompt the user to confirm deletion of a given user's home directory. If the +# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete +# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return +# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. +# +f_dialog_input_home_delete() +{ + local retval + + f_dialog_yesno "$msg_delete_home_directory" + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_home_delete="$msg_yes" + else + pw_home_delete="$msg_no" + fi + save_flag=1 + + f_dprintf "pw_home_delete: [$cur_pw_home_delete]->[$pw_home_delete]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_dotfiles_create +# +# Prompt the user to confirm population of a given user's home directory with +# sample dotfiles. If the user does not cancel (by choosing "No") or press ESC, +# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise +# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to +# be i18n-compatible. +# +f_dialog_input_dotfiles_create() +{ + local retval + + f_dialog_yesno "$msg_create_dotfiles" + retval=$? + + if [ $retval -eq $SUCCESS ]; then + pw_dotfiles_create="$msg_yes" + else + pw_dotfiles_create="$msg_no" + fi + save_flag=1 + + local debug="pw_dotfiles_create:" + f_dprintf "$debug: [$cur_pw_dotfiles_create]->[$pw_dotfiles_create]" + + [ $retval -ne 255 ] # return failure if user pressed ESC +} + +# f_dialog_input_shell [$shell] +# +# Allow the user to select a new login shell for a given user. If the user does +# not cancel or press ESC, the $pw_home_dir variable will hold the newly- +# configured value upon return. +# +# +f_dialog_input_shell() +{ + local size retval shells shell_list _input="$1" + local hline="$hline_arrows_space_tab_enter" + local prompt="$msg_select_login_shell" + + shells=$( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" ) + shell_list=$( + for shell in $shells; do + if [ "$shell" = "$_input" ]; then + echo "'$shell' '' 'on'" + else + echo "'$shell' '' 'off'" + fi + done + ) + + size=$( eval f_dialog_radiolist_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $shell_list ) + + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --radiolist \"\$prompt\" $size \ + $shell_list \ + 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + + retval=$? + _input=$( f_dialog_inputstr ) + + # Return if user has either pressed ESC or chosen Cancel/No + [ $retval -eq $SUCCESS ] || return $retval + + pw_shell="$_input" + save_flag=1 + + f_dprintf "pw_shell: [$cur_pw_shell]->[$pw_shell]" + + return $SUCCESS +} + +fi # ! $_USERMGMT_USER_INPUT_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/useradd b/usr.sbin/bsdconfig/usermgmt/useradd index 8dc1934d077cd..4821ccdaff195 100755 --- a/usr.sbin/bsdconfig/usermgmt/useradd +++ b/usr.sbin/bsdconfig/usermgmt/useradd @@ -29,11 +29,11 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr -APP_DIR="070.usermgmt" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" diff --git a/usr.sbin/bsdconfig/usermgmt/userdel b/usr.sbin/bsdconfig/usermgmt/userdel index 49a54c4de5182..c530682a42884 100755 --- a/usr.sbin/bsdconfig/usermgmt/userdel +++ b/usr.sbin/bsdconfig/usermgmt/userdel @@ -29,13 +29,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/user_input.subr -APP_DIR="070.usermgmt" -f_include $BSDCFG_LIBE/$APP_DIR/include/user_input.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/useredit b/usr.sbin/bsdconfig/usermgmt/useredit index 3109d36ae5097..a38afa203c35f 100755 --- a/usr.sbin/bsdconfig/usermgmt/useredit +++ b/usr.sbin/bsdconfig/usermgmt/useredit @@ -29,13 +29,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/user_input.subr -APP_DIR="070.usermgmt" -f_include $BSDCFG_LIBE/$APP_DIR/include/user_input.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/userinput b/usr.sbin/bsdconfig/usermgmt/userinput index 43a750e3857f7..7cabfdc333361 100755 --- a/usr.sbin/bsdconfig/usermgmt/userinput +++ b/usr.sbin/bsdconfig/usermgmt/userinput @@ -29,13 +29,13 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/user_input.subr -APP_DIR="070.usermgmt" -f_include $BSDCFG_LIBE/$APP_DIR/include/user_input.subr +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) diff --git a/usr.sbin/bsdconfig/usermgmt/usermgmt b/usr.sbin/bsdconfig/usermgmt/usermgmt index 460f46bfd594f..b4905b39a3ea0 100755 --- a/usr.sbin/bsdconfig/usermgmt/usermgmt +++ b/usr.sbin/bsdconfig/usermgmt/usermgmt @@ -29,12 +29,12 @@ # ############################################################ INCLUDES -BSDCFG_LIBE="/usr/libexec/bsdconfig" -. $BSDCFG_LIBE/include/common.subr || exit 1 -f_include $BSDCFG_LIBE/include/dialog.subr -f_include $BSDCFG_LIBE/include/mustberoot.subr +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr -APP_DIR="070.usermgmt" +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) -- cgit v1.3 From a5c5eaae8c14304b636f65980066aedf6512e2fe Mon Sep 17 00:00:00 2001 From: Gavin Atkinson Date: Wed, 19 Sep 2012 18:22:14 +0000 Subject: Recognise NVM Express devices and pretty-print their name. MFC after: 1 week --- sys/dev/pci/pci.c | 1 + usr.sbin/pciconf/pciconf.c | 1 + 2 files changed, 2 insertions(+) (limited to 'usr.sbin') diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 3de932ab8740e..4c64430084199 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -3661,6 +3661,7 @@ static struct {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, + {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM Express"}, {PCIC_NETWORK, -1, "network"}, {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c index 76e478b7c8ac6..160445db5ae92 100644 --- a/usr.sbin/pciconf/pciconf.c +++ b/usr.sbin/pciconf/pciconf.c @@ -345,6 +345,7 @@ static struct {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, + {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM Express"}, {PCIC_NETWORK, -1, "network"}, {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, -- cgit v1.3 From 3cdfd8d3b27acff2e35727c745f95b11dbe6be27 Mon Sep 17 00:00:00 2001 From: Gavin Atkinson Date: Thu, 20 Sep 2012 08:30:17 +0000 Subject: The correct generic term for PCIS_STORAGE_NVM is "NVM" not "NVM Express". Submitted by: jimharris MFC after: 6 days --- sys/dev/pci/pci.c | 2 +- usr.sbin/pciconf/pciconf.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 4c64430084199..55c4aca9394df 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -3661,7 +3661,7 @@ static struct {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, - {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM Express"}, + {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"}, {PCIC_NETWORK, -1, "network"}, {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c index 160445db5ae92..52ad09a709622 100644 --- a/usr.sbin/pciconf/pciconf.c +++ b/usr.sbin/pciconf/pciconf.c @@ -345,7 +345,7 @@ static struct {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, - {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM Express"}, + {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"}, {PCIC_NETWORK, -1, "network"}, {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, -- cgit v1.3 From 89498fdf4174b08bb67d684d14886539aff698b4 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Thu, 20 Sep 2012 23:44:13 +0000 Subject: Change all invocations of dialog(1) to no-longer require temporary files. This allows bsdconfig to -- like bsdinstall -- operate from read-only media. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/bsdconfig | 9 +- usr.sbin/bsdconfig/console/console | 9 +- usr.sbin/bsdconfig/console/font | 9 +- usr.sbin/bsdconfig/console/keymap | 9 +- usr.sbin/bsdconfig/console/repeat | 9 +- usr.sbin/bsdconfig/console/saver | 15 ++- usr.sbin/bsdconfig/console/screenmap | 9 +- usr.sbin/bsdconfig/console/ttys | 9 +- usr.sbin/bsdconfig/mouse/flags | 6 +- usr.sbin/bsdconfig/mouse/mouse | 9 +- usr.sbin/bsdconfig/mouse/port | 9 +- usr.sbin/bsdconfig/mouse/type | 9 +- usr.sbin/bsdconfig/networking/networking | 9 +- usr.sbin/bsdconfig/networking/share/device.subr | 16 ++- usr.sbin/bsdconfig/networking/share/hostname.subr | 7 +- usr.sbin/bsdconfig/networking/share/ipaddr.subr | 7 +- usr.sbin/bsdconfig/networking/share/media.subr | 14 +- usr.sbin/bsdconfig/networking/share/netmask.subr | 7 +- usr.sbin/bsdconfig/networking/share/resolv.subr | 14 +- usr.sbin/bsdconfig/networking/share/routing.subr | 7 +- usr.sbin/bsdconfig/password/share/password.subr | 13 +- usr.sbin/bsdconfig/security/kern_securelevel | 9 +- usr.sbin/bsdconfig/security/security | 9 +- usr.sbin/bsdconfig/share/dialog.subr | 52 +++----- usr.sbin/bsdconfig/share/mustberoot.subr | 7 +- usr.sbin/bsdconfig/startup/misc | 16 ++- usr.sbin/bsdconfig/startup/rcadd | 9 +- usr.sbin/bsdconfig/startup/rcconf | 9 +- usr.sbin/bsdconfig/startup/rcdelete | 18 ++- usr.sbin/bsdconfig/startup/rcvar | 9 +- usr.sbin/bsdconfig/startup/share/rcconf.subr | 23 +++- usr.sbin/bsdconfig/startup/share/rcedit.subr | 7 +- usr.sbin/bsdconfig/startup/startup | 9 +- usr.sbin/bsdconfig/timezone/timezone | 21 ++- usr.sbin/bsdconfig/usermgmt/groupinput | 6 +- usr.sbin/bsdconfig/usermgmt/share/group_input.subr | 57 +++++--- usr.sbin/bsdconfig/usermgmt/share/user_input.subr | 147 +++++++++++++++------ usr.sbin/bsdconfig/usermgmt/userinput | 7 +- usr.sbin/bsdconfig/usermgmt/usermgmt | 9 +- 39 files changed, 441 insertions(+), 188 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index f3e385b82e3f8..ea72d3cf164e3 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -177,7 +177,8 @@ dialog_menu_main() \"\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear \ --title \"\$title\" \ --backtitle \"\$btitle\" \ @@ -185,7 +186,11 @@ dialog_menu_main() --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_exit_bsdconfig\" \ --menu \"\$prompt\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/console/console b/usr.sbin/bsdconfig/console/console index eec65691ca47e..73d9f5b186f3b 100755 --- a/usr.sbin/bsdconfig/console/console +++ b/usr.sbin/bsdconfig/console/console @@ -68,7 +68,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -76,7 +77,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/console/font b/usr.sbin/bsdconfig/console/font index 074f025a2d337..2fe46110bd8af 100755 --- a/usr.sbin/bsdconfig/console/font +++ b/usr.sbin/bsdconfig/console/font @@ -76,7 +76,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -84,7 +85,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/console/keymap b/usr.sbin/bsdconfig/console/keymap index ca0e92ec60260..ea1de2db919b3 100755 --- a/usr.sbin/bsdconfig/console/keymap +++ b/usr.sbin/bsdconfig/console/keymap @@ -116,7 +116,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -124,7 +125,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/console/repeat b/usr.sbin/bsdconfig/console/repeat index 87f1e3c840b0e..76d60d1191053 100755 --- a/usr.sbin/bsdconfig/console/repeat +++ b/usr.sbin/bsdconfig/console/repeat @@ -66,7 +66,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -74,7 +75,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/console/saver b/usr.sbin/bsdconfig/console/saver index f90c837ef5f67..0ec332ca87904 100755 --- a/usr.sbin/bsdconfig/console/saver +++ b/usr.sbin/bsdconfig/console/saver @@ -76,7 +76,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -84,7 +85,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN @@ -174,7 +179,7 @@ while :; do "$prompt" \ "$blanktime" \ "$hline" ) - $DIALOG \ + dialog_inputbox=$( $DIALOG \ --title "$title" \ --backtitle "$btitle" \ --hline "$hline" \ @@ -182,8 +187,10 @@ while :; do --cancel-label "$msg_cancel" \ --inputbox "$prompt" $size \ "$blanktime" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" blanktime=$( f_dialog_inputstr ) [ $retval -eq $SUCCESS ] && f_sysrc_set blanktime "$blanktime" diff --git a/usr.sbin/bsdconfig/console/screenmap b/usr.sbin/bsdconfig/console/screenmap index ddcaec82b1ea4..bdabe6ada8e3b 100755 --- a/usr.sbin/bsdconfig/console/screenmap +++ b/usr.sbin/bsdconfig/console/screenmap @@ -68,7 +68,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -76,7 +77,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys index e95d2b27806ac..fcf45efe63b6d 100755 --- a/usr.sbin/bsdconfig/console/ttys +++ b/usr.sbin/bsdconfig/console/ttys @@ -82,7 +82,8 @@ dialog_menu_main() \"\$hline\" \ $TTY_MENU_LIST ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -90,7 +91,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $TTY_MENU_LIST \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # ttys_set_type $consterm diff --git a/usr.sbin/bsdconfig/mouse/flags b/usr.sbin/bsdconfig/mouse/flags index d763a6d9b20c9..c98904814279d 100755 --- a/usr.sbin/bsdconfig/mouse/flags +++ b/usr.sbin/bsdconfig/mouse/flags @@ -90,7 +90,7 @@ size=$( f_dialog_inputbox_size \ "$prompt" \ "$flags" \ "$hline" ) -eval $DIALOG \ +dialog_inputbox=$( eval $DIALOG \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --hline \"\$hline\" \ @@ -98,8 +98,10 @@ eval $DIALOG \ --cancel-label \"\$msg_cancel\" \ --inputbox \"\$prompt\" $size \ \"\$flags\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD +) retval=$? +setvar MENU_INPUTBOX_$$ "$dialog_inputbox" flags=$( f_dialog_inputstr ) [ $retval -eq $SUCCESS ] || f_die diff --git a/usr.sbin/bsdconfig/mouse/mouse b/usr.sbin/bsdconfig/mouse/mouse index 4c5d903f55bc2..911ea954ee56d 100755 --- a/usr.sbin/bsdconfig/mouse/mouse +++ b/usr.sbin/bsdconfig/mouse/mouse @@ -67,7 +67,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -75,7 +76,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/mouse/port b/usr.sbin/bsdconfig/mouse/port index aed191700ac92..4ab54a7adecb2 100755 --- a/usr.sbin/bsdconfig/mouse/port +++ b/usr.sbin/bsdconfig/mouse/port @@ -68,7 +68,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -76,7 +77,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/mouse/type b/usr.sbin/bsdconfig/mouse/type index 83a712212c396..f1e88fb49e6b7 100755 --- a/usr.sbin/bsdconfig/mouse/type +++ b/usr.sbin/bsdconfig/mouse/type @@ -72,7 +72,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -80,7 +81,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/networking b/usr.sbin/bsdconfig/networking/networking index bbaf2e5f3183a..fe3ce1b2242cc 100755 --- a/usr.sbin/bsdconfig/networking/networking +++ b/usr.sbin/bsdconfig/networking/networking @@ -65,14 +65,19 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index e74a579863072..17c7467bbae06 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -265,7 +265,8 @@ f_dialog_menu_netdev() \"\$prompt\" \ \"\$hline\" \ $interfaces ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -273,7 +274,11 @@ f_dialog_menu_netdev() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $interfaces \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # f_dialog_menu_netdev_edit $interface $ipaddr $netmask $options $dhcp @@ -330,7 +335,8 @@ f_dialog_menu_netdev_edit() \"\$prompt\" \ \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -338,9 +344,11 @@ f_dialog_menu_netdev_edit() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" local tag="$( f_dialog_menutag )" # Return if "Cancel" was chosen (-1) or ESC was pressed (255) diff --git a/usr.sbin/bsdconfig/networking/share/hostname.subr b/usr.sbin/bsdconfig/networking/share/hostname.subr index 54264439149c1..fed8679269eaf 100644 --- a/usr.sbin/bsdconfig/networking/share/hostname.subr +++ b/usr.sbin/bsdconfig/networking/share/hostname.subr @@ -146,7 +146,8 @@ f_dialog_input_hostname() "$msg" \ "$hostname" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -154,9 +155,11 @@ f_dialog_input_hostname() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$hostname\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" hostname=$( f_dialog_inputstr ) [ $retval -eq $SUCCESS ] || return $retval diff --git a/usr.sbin/bsdconfig/networking/share/ipaddr.subr b/usr.sbin/bsdconfig/networking/share/ipaddr.subr index 06571ca0ae2bc..7e858dc296a84 100644 --- a/usr.sbin/bsdconfig/networking/share/ipaddr.subr +++ b/usr.sbin/bsdconfig/networking/share/ipaddr.subr @@ -167,7 +167,8 @@ f_dialog_input_ipaddr() # Loop until the user provides taint-free input. # while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -175,9 +176,11 @@ f_dialog_input_ipaddr() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_ipaddr\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # diff --git a/usr.sbin/bsdconfig/networking/share/media.subr b/usr.sbin/bsdconfig/networking/share/media.subr index c546220365a28..e7da98ad3e3af 100644 --- a/usr.sbin/bsdconfig/networking/share/media.subr +++ b/usr.sbin/bsdconfig/networking/share/media.subr @@ -123,7 +123,8 @@ f_dialog_input_options() local msg="$( printf "$msg_please_enter_mediaopts" "$interface" )" local hline="$hline_alnum_punc_tab_enter" - $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( $DIALOG \ --title "$DIALOG_TITLE" \ --backtitle "$DIALOG_BACKTITLE" \ --hline "$hline" \ @@ -131,9 +132,11 @@ f_dialog_input_options() --cancel-label "$msg_cancel" \ --inputbox "$msg" 9 70 \ "$options" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" local _options="$( f_dialog_inputstr )" [ $retval -eq $SUCCESS ] && options="$_options" @@ -205,7 +208,8 @@ f_dialog_menu_media_options() local hline="$hline_arrows_tab_enter" - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -213,9 +217,11 @@ f_dialog_menu_media_options() --cancel-label \"\$msg_cancel\" \ --menu \"\$msg\" 21 60 12 \ $supported_media \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" if [ $retval -eq $SUCCESS ]; then local tag="$( f_dialog_menutag )" options=$( eval f_dialog_menutag2item \"\$tag\" \ diff --git a/usr.sbin/bsdconfig/networking/share/netmask.subr b/usr.sbin/bsdconfig/networking/share/netmask.subr index 4fa5be30e9719..2f2b37853e68c 100644 --- a/usr.sbin/bsdconfig/networking/share/netmask.subr +++ b/usr.sbin/bsdconfig/networking/share/netmask.subr @@ -177,7 +177,8 @@ f_dialog_input_netmask() # Loop until the user provides taint-free input. # while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -185,9 +186,11 @@ f_dialog_input_netmask() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_netmask\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # diff --git a/usr.sbin/bsdconfig/networking/share/resolv.subr b/usr.sbin/bsdconfig/networking/share/resolv.subr index b635cc1e20625..154748ba52e8e 100644 --- a/usr.sbin/bsdconfig/networking/share/resolv.subr +++ b/usr.sbin/bsdconfig/networking/share/resolv.subr @@ -342,7 +342,8 @@ f_dialog_input_nameserver() # while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -350,9 +351,11 @@ f_dialog_input_nameserver() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$ns\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" new_ns=$( f_dialog_inputstr ) [ $retval -eq $SUCCESS ] || return $retval @@ -476,7 +479,8 @@ f_dialog_menu_nameservers() \"\$prompt\" \ \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -484,9 +488,11 @@ f_dialog_menu_nameservers() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" local tag="$( f_dialog_menutag )" ns="" # Return if "Cancel" was chosen (-1) or ESC was pressed (255) diff --git a/usr.sbin/bsdconfig/networking/share/routing.subr b/usr.sbin/bsdconfig/networking/share/routing.subr index c6b6aedf9f397..b8ccdcd6f987d 100644 --- a/usr.sbin/bsdconfig/networking/share/routing.subr +++ b/usr.sbin/bsdconfig/networking/share/routing.subr @@ -110,7 +110,8 @@ f_dialog_input_defaultrouter() # Loop until the user provides taint-free input. # while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -118,9 +119,11 @@ f_dialog_input_defaultrouter() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$defaultrouter\" \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" defaultrouter=$( f_dialog_inputstr ) [ "$defaultrouter" ] || return $SUCCESS diff --git a/usr.sbin/bsdconfig/password/share/password.subr b/usr.sbin/bsdconfig/password/share/password.subr index 550d2aa7c6568..a0d1c502a3d42 100644 --- a/usr.sbin/bsdconfig/password/share/password.subr +++ b/usr.sbin/bsdconfig/password/share/password.subr @@ -68,7 +68,8 @@ f_dialog_input_password() # local retval _password1 _password2 while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -76,15 +77,17 @@ f_dialog_input_password() --cancel-label \"\$msg_cancel\" \ --insecure \ --passwordbox \"\$msg\" $size \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _password1=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No [ $retval -eq $SUCCESS ] || return $retval - eval $DIALOG \ + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -92,9 +95,11 @@ f_dialog_input_password() --cancel-label \"\$msg_cancel\" \ --insecure \ --passwordbox \"\$rmsg\" $rsize \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _password2=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index b284592822807..c48691ecb586e 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -67,7 +67,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -75,7 +76,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/security/security b/usr.sbin/bsdconfig/security/security index facdbea64535d..3dd71c56119f1 100755 --- a/usr.sbin/bsdconfig/security/security +++ b/usr.sbin/bsdconfig/security/security @@ -86,7 +86,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -94,7 +95,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index cced388af2e04..d2ba529f323c6 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -38,9 +38,11 @@ f_include_lang $BSDCFG_LIBE/include/messages.subr ############################################################ CONFIGURATION # -# Default directory to store dialog(1) temporary files +# Default file descriptor to link to stdout for dialog(1) passthru allowing +# execution of dialog from within a sub-shell (so-long as its standard output +# is explicitly redirected to this file descriptor). # -: ${DIALOG_TMPDIR:="/tmp"} +: ${DIALOG_TERMINAL_PASSTHRU_FD:=3} ############################################################ GLOBALS @@ -1108,12 +1110,8 @@ f_dialog_noyes() # f_dialog_inputstr() { - local tmpfile="$DIALOG_TMPDIR/dialog.inputbox.$$" - - [ -f "$tmpfile" ] || return $FAILURE - # Skip warnings and trim leading/trailing whitespace from user input - awk ' + eval echo \"\$DIALOG_INPUTBOX_$$\" | awk ' BEGIN { found = 0 } { if ( ! found ) @@ -1126,9 +1124,8 @@ f_dialog_inputstr() sub(/[[:space:]]*$/, "") print } - ' "$tmpfile" 2> /dev/null - f_quietly rm -f "$tmpfile" - + ' + setvar DIALOG_INPUTBOX_$$ "" # scrub memory in case data was sensitive return $SUCCESS } @@ -1141,11 +1138,8 @@ f_dialog_inputstr() # f_dialog_menutag() { - local tmpfile="$DIALOG_TMPDIR/dialog.menu.$$" - - [ -f "$tmpfile" ] || return $FAILURE - - awk ' + # Skip warnings + eval echo \"\$DIALOG_MENU_$$\" | awk ' BEGIN { found = 0 } { if ( found ) # ... just spew @@ -1158,9 +1152,8 @@ f_dialog_menutag() found = 1 print } - ' "$tmpfile" 2> /dev/null - f_quietly rm -f "$tmpfile" - + ' + setvar DIALOG_MENU_$$ "" # scrub memory in case data was sensitive return $SUCCESS } @@ -1319,6 +1312,11 @@ f_dialog_init() { DIALOG_SELF_INITIALIZE= + # + # Clone terminal stdout so we can redirect to it from within sub-shells + # + eval exec $DIALOG_TERMINAL_PASSTHRU_FD\>\&1 + # # Process stored command-line arguments # @@ -1422,22 +1420,4 @@ f_dialog_init() fi } -############################################################ CLEAN-UP FUNCTIONS - -# f_clean_up -# -# Clean-up routines (run when script exits or is killed). -# -f_clean_up() -{ - f_quietly rm -f "$DIALOG_TMPDIR"/dialog.*.$$ -} - -############################################################ MAIN - -# -# Trap signals so we can recover gracefully -# -trap 'f_clean_up' EXIT - fi # ! $_DIALOG_SUBR diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr index ec3e16a3547f3..c6514c0d4b1ea 100644 --- a/usr.sbin/bsdconfig/share/mustberoot.subr +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -119,7 +119,8 @@ f_become_root_via_sudo() [ $retval -eq 255 ] && f_die $retval "$password" else - $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( $DIALOG \ --title "$DIALOG_TITLE" \ --backtitle "$DIALOG_BACKTITLE" \ --hline "$hline" \ @@ -127,8 +128,10 @@ f_become_root_via_sudo() --cancel-label "$msg_cancel" \ --insecure \ --passwordbox "$msg" $size \ - 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" password=$( f_dialog_inputstr ) fi diff --git a/usr.sbin/bsdconfig/startup/misc b/usr.sbin/bsdconfig/startup/misc index f4a0e1a9de8f2..3879c5714679f 100755 --- a/usr.sbin/bsdconfig/startup/misc +++ b/usr.sbin/bsdconfig/startup/misc @@ -230,7 +230,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -238,7 +239,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # dialog_input_value [ $prompt [ $init ] ] @@ -260,7 +265,8 @@ dialog_input_value() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -268,9 +274,11 @@ dialog_input_value() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$prompt\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) f_dialog_title_restore diff --git a/usr.sbin/bsdconfig/startup/rcadd b/usr.sbin/bsdconfig/startup/rcadd index 49138f546f14a..d9d6a045353d2 100755 --- a/usr.sbin/bsdconfig/startup/rcadd +++ b/usr.sbin/bsdconfig/startup/rcadd @@ -72,7 +72,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -80,7 +81,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index 4deff5353db9b..3f7c25049ea34 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -168,7 +168,8 @@ dialog_menu_main() \"\$hline\" \ $RCCONF_MENU_LIST ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -179,7 +180,11 @@ dialog_menu_main() ${SHOW_DESC:+--item-help} \ --menu \"\$prompt\" $size \ $RCCONF_MENU_LIST \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index bef9651fdcd2d..a97025838c087 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -175,7 +175,8 @@ dialog_menu_main() \"\$hline\" \ $RCCONF_MENU_LIST ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -186,7 +187,11 @@ dialog_menu_main() ${SHOW_DESC:+--item-help} \ --menu \"\$prompt\" $size \ $RCCONF_MENU_LIST \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # dialog_menu_delete $var1 [$var2 ...] @@ -240,7 +245,8 @@ dialog_menu_delete() local defaultno="defaultno" [ "$USE_XDIALOG" ] && defaultno="default-no" - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -249,7 +255,11 @@ dialog_menu_delete() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index 10e9baf712439..799dd3426b1b6 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -132,7 +132,8 @@ dialog_menu_main() \"\$hline\" \ $RCVAR_MENU_LIST ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -141,7 +142,11 @@ dialog_menu_main() ${SHOW_DESC:+--item-help} \ --menu \"\$prompt\" $size \ $RCVAR_MENU_LIST \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index 96659d05a0e98..25dec5478433b 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -282,7 +282,8 @@ f_dialog_input_view_details() f_dialog_title "$msg_choose_view_details" - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -290,9 +291,11 @@ f_dialog_input_view_details() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" local mtag="$( f_dialog_menutag )" f_dialog_title_restore @@ -390,7 +393,8 @@ f_dialog_input_rclist() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -399,7 +403,11 @@ f_dialog_input_rclist() ${SHOW_DESC:+--item-help} \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # f_dialog_input_rcvar [$init] @@ -425,7 +433,8 @@ f_dialog_input_rcvar() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -433,9 +442,11 @@ f_dialog_input_rcvar() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user either pressed ESC or chosen Cancel/No diff --git a/usr.sbin/bsdconfig/startup/share/rcedit.subr b/usr.sbin/bsdconfig/startup/share/rcedit.subr index f0b5b59abe2e4..909fef27324b6 100644 --- a/usr.sbin/bsdconfig/startup/share/rcedit.subr +++ b/usr.sbin/bsdconfig/startup/share/rcedit.subr @@ -77,7 +77,8 @@ f_dialog_rcedit() local opterm="--" [ "$USE_XDIALOG" ] && opterm= - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -85,9 +86,11 @@ f_dialog_rcedit() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ $opterm \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No diff --git a/usr.sbin/bsdconfig/startup/startup b/usr.sbin/bsdconfig/startup/startup index 7bb5d90aa005d..c48176195d5ff 100755 --- a/usr.sbin/bsdconfig/startup/startup +++ b/usr.sbin/bsdconfig/startup/startup @@ -65,7 +65,8 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -73,7 +74,11 @@ dialog_menu_main() --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/timezone/timezone b/usr.sbin/bsdconfig/timezone/timezone index f785a75ef3f57..02626a6e5f945 100755 --- a/usr.sbin/bsdconfig/timezone/timezone +++ b/usr.sbin/bsdconfig/timezone/timezone @@ -91,14 +91,19 @@ dialog_menu_main() \"\" \ $continent_menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $continent_menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN @@ -354,15 +359,17 @@ while :; do # # Launch the country selection menu # - eval $DIALOG \ + dialog_menu=$( eval $DIALOG \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" tag=$( f_dialog_menutag ) if [ $retval -ne 0 ]; then @@ -409,15 +416,17 @@ while :; do # Launch the zone selection menu # NOTE: This is as deep as we go # - eval $DIALOG \ + dialog_menu=$( eval $DIALOG \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\$prompt\" $size \ $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" n=$( f_dialog_menutag ) if [ $retval -ne 0 ]; then diff --git a/usr.sbin/bsdconfig/usermgmt/groupinput b/usr.sbin/bsdconfig/usermgmt/groupinput index e1df6140070d4..2991d336240be 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupinput +++ b/usr.sbin/bsdconfig/usermgmt/groupinput @@ -228,7 +228,7 @@ while :; do \"\$hline\" \ $menu_items ) - eval $DIALOG \ + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -236,9 +236,11 @@ while :; do --cancel-label \"\$msg_cancel\" \ --menu \"\$menu_text\" $size \ $menu_items \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" mtag=$( f_dialog_menutag ) f_dprintf "retval=$retval mtag=[$mtag]" diff --git a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr index 0c616967b48fd..e3d59d0bdc2b5 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr @@ -84,14 +84,19 @@ f_dialog_menu_group_list() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # f_dialog_input_group_name [$group_name] @@ -121,7 +126,8 @@ f_dialog_input_group_name() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -129,9 +135,11 @@ f_dialog_input_group_name() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -204,7 +212,8 @@ f_dialog_input_group_password() # local retval _password1 _password2 while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -212,15 +221,17 @@ f_dialog_input_group_password() --cancel-label \"\$msg_cancel\" \ --insecure \ --passwordbox \"\$msg\" $size \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _password1=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No [ $retval -eq $SUCCESS ] || return $retval - eval $DIALOG \ + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -228,9 +239,11 @@ f_dialog_input_group_password() --cancel-label \"\$msg_cancel\" \ --insecure \ --passwordbox \"\$rmsg\" $rsize \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _password2=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -281,7 +294,8 @@ f_dialog_input_group_gid() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -289,9 +303,11 @@ f_dialog_input_group_gid() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -327,6 +343,7 @@ f_dialog_input_group_members() '2' '$msg_enter_group_members_manually' " # END-QUOTE + local dialog_menu while :; do msg="$msg_group_members:" menu_size=$( eval f_dialog_menu_size \ @@ -335,7 +352,7 @@ f_dialog_input_group_members() \"\$msg\" \ \"\$hline\" \ $menu_list ) - eval $DIALOG \ + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -343,8 +360,10 @@ f_dialog_input_group_members() --cancel-label \"\$msg_cancel\" \ --menu \"\$msg\" $menu_size \ $menu_list \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" menu_choice=$( f_dialog_menutag ) f_dprintf "retval=$retval menu_choice=[$menu_choice]" @@ -373,7 +392,8 @@ f_dialog_input_group_members() \"\" \ \"\$hline\" \ $checklist_users ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --separate-output \ @@ -382,8 +402,10 @@ f_dialog_input_group_members() --cancel-label \"\$msg_cancel\" \ --checklist \"\$msg\" $size \ $checklist_users \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _group_members=$( f_dialog_inputstr | tr '\n' ' ' | sed -e 's/[[:space:]]\{1,\}/,/g;s/^,//;s/,$//' ) @@ -405,7 +427,8 @@ f_dialog_input_group_members() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -413,9 +436,11 @@ f_dialog_input_group_members() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _group_members=$( f_dialog_inputstr ) # Return to previous menu if user has either diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr index d27b9be3e4960..8b356b67136e4 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -109,14 +109,19 @@ f_dialog_menu_user_list() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } # f_dialog_input_member_groups [$member_groups] @@ -155,7 +160,8 @@ f_dialog_input_member_groups() \"\" \ \"\$hline\" \ $checklist_groups ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --separate-output \ @@ -164,9 +170,11 @@ f_dialog_input_member_groups() --cancel-label \"\$msg_cancel\" \ --checklist \"\$msg\" $size \ $checklist_groups \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _member_groups=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -223,7 +231,8 @@ f_dialog_input_name() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -231,9 +240,11 @@ f_dialog_input_name() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -306,7 +317,8 @@ f_dialog_input_password() # local retval _password1 _password2 while :; do - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -314,15 +326,17 @@ f_dialog_input_password() --cancel-label \"\$msg_cancel\" \ --insecure \ --passwordbox \"\$msg\" $size \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _password1=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No [ $retval -eq $SUCCESS ] || return $retval - eval $DIALOG \ + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -330,9 +344,11 @@ f_dialog_input_password() --cancel-label \"\$msg_cancel\" \ --insecure \ --passwordbox \"\$rmsg\" $rsize \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _password2=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -384,7 +400,8 @@ f_dialog_input_gecos() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -392,9 +409,11 @@ f_dialog_input_gecos() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -427,7 +446,8 @@ f_dialog_input_uid() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -435,9 +455,11 @@ f_dialog_input_uid() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -470,7 +492,8 @@ f_dialog_input_gid() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -478,9 +501,11 @@ f_dialog_input_gid() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -513,7 +538,8 @@ f_dialog_input_class() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -521,9 +547,11 @@ f_dialog_input_class() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -569,7 +597,8 @@ f_dialog_input_change() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -577,8 +606,10 @@ f_dialog_input_change() --cancel-label \"\$msg_cancel\" \ --menu \"\$msg\" $menu_size \ $menu_list \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" date_type=$( f_dialog_menutag ) f_dprintf "retval=$retval date_type=[$date_type]" @@ -602,7 +633,8 @@ f_dialog_input_change() "$DIALOG_BACKTITLE" \ "$msg" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -610,8 +642,10 @@ f_dialog_input_change() --cancel-label \"\$msg_cancel\" \ --calendar \"\$msg\" $calendar_size \ $_input_date \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_date=$( f_dialog_inputstr ) f_dprintf "retval=$retval ret_date=[$ret_date]" @@ -626,7 +660,8 @@ f_dialog_input_change() "$DIALOG_BACKTITLE" \ "$msg" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -634,8 +669,10 @@ f_dialog_input_change() --cancel-label \"\$msg_cancel\" \ --timebox \"\$msg\" $timebox_size \ $_input_time \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_time=$( f_dialog_inputstr ) f_dprintf "retval=$retval ret_time=[$ret_time]" @@ -666,7 +703,8 @@ f_dialog_input_change() "$ret_days" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -674,8 +712,10 @@ f_dialog_input_change() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$ret_days\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_days=$( f_dialog_inputstr ) # Return to menu if either ESC or Cancel/No @@ -707,7 +747,8 @@ f_dialog_input_change() "$msg" \ "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -715,9 +756,11 @@ f_dialog_input_change() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_secs=$( f_dialog_inputstr ) # Return to menu if either ESC or Cancel/No @@ -779,7 +822,8 @@ f_dialog_input_expire() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -787,8 +831,10 @@ f_dialog_input_expire() --cancel-label \"\$msg_cancel\" \ --menu \"\$msg\" $menu_size \ $menu_list \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" date_type=$( f_dialog_menutag ) f_dprintf "retval=$retval date_type=[$date_type]" @@ -812,7 +858,8 @@ f_dialog_input_expire() "$DIALOG_BACKTITLE" \ "$msg" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -820,8 +867,10 @@ f_dialog_input_expire() --cancel-label \"\$msg_cancel\" \ --calendar \"\$msg\" $calendar_size \ $_input_date \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_date=$( f_dialog_inputstr ) f_dprintf "retval=$retval ret_date=[$ret_date]" @@ -836,7 +885,7 @@ f_dialog_input_expire() "$DIALOG_BACKTITLE" \ "$msg" \ "$hline" ) - eval $DIALOG \ + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -844,8 +893,10 @@ f_dialog_input_expire() --cancel-label \"\$msg_cancel\" \ --timebox \"\$msg\" $timebox_size \ $_input_time \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_time=$( f_dialog_inputstr ) f_dprintf "retval=$retval ret_time=[$ret_time]" @@ -876,7 +927,8 @@ f_dialog_input_expire() "$ret_days" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -884,8 +936,10 @@ f_dialog_input_expire() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$ret_days\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_days=$( f_dialog_inputstr ) # Return to menu if either ESC or Cancel/No @@ -917,7 +971,8 @@ f_dialog_input_expire() "$msg" \ "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -925,9 +980,11 @@ f_dialog_input_expire() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" ret_secs=$( f_dialog_inputstr ) # Return to menu if either ESC or Cancel/No @@ -976,7 +1033,8 @@ f_dialog_input_home_dir() "$_input" \ "$hline" ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -984,9 +1042,11 @@ f_dialog_input_home_dir() --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_input\" \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No @@ -1153,7 +1213,8 @@ f_dialog_input_shell() \"\$hline\" \ $shell_list ) - eval $DIALOG \ + local dialog_inputbox + dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -1161,9 +1222,11 @@ f_dialog_input_shell() --cancel-label \"\$msg_cancel\" \ --radiolist \"\$prompt\" $size \ $shell_list \ - 2> $DIALOG_TMPDIR/dialog.inputbox.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # Return if user has either pressed ESC or chosen Cancel/No diff --git a/usr.sbin/bsdconfig/usermgmt/userinput b/usr.sbin/bsdconfig/usermgmt/userinput index 7cabfdc333361..aca8aed058533 100755 --- a/usr.sbin/bsdconfig/usermgmt/userinput +++ b/usr.sbin/bsdconfig/usermgmt/userinput @@ -391,7 +391,8 @@ while :; do \"\$hline\" \ $menu_items ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ @@ -399,9 +400,11 @@ while :; do --cancel-label \"\$msg_cancel\" \ --menu \"\$menu_text\" $size \ $menu_items \ - 2> $DIALOG_TMPDIR/dialog.menu.$$ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" mtag=$( f_dialog_menutag ) f_dprintf "retval=$retval mtag=[$mtag]" diff --git a/usr.sbin/bsdconfig/usermgmt/usermgmt b/usr.sbin/bsdconfig/usermgmt/usermgmt index b4905b39a3ea0..0bfb0c01fc005 100755 --- a/usr.sbin/bsdconfig/usermgmt/usermgmt +++ b/usr.sbin/bsdconfig/usermgmt/usermgmt @@ -69,14 +69,19 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) - eval $DIALOG \ + local dialog_menu + dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --menu \"\" $size $menu_list \ - 2> "$DIALOG_TMPDIR/dialog.menu.$$" + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + return $retval } ############################################################ MAIN -- cgit v1.3 From 104a7f3e1d5463f6b1851b8ae979f38708887903 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 21 Sep 2012 01:36:20 +0000 Subject: Replace redirections to /dev/null with "close file-descriptor" syntax (>&-). Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/bsdconfig | 6 +++--- usr.sbin/bsdconfig/console/ttys | 4 ++-- usr.sbin/bsdconfig/mouse/enable | 2 +- usr.sbin/bsdconfig/networking/devices | 2 +- usr.sbin/bsdconfig/networking/share/device.subr | 2 +- usr.sbin/bsdconfig/networking/share/ipaddr.subr | 2 +- usr.sbin/bsdconfig/networking/share/media.subr | 2 +- usr.sbin/bsdconfig/networking/share/netmask.subr | 2 +- usr.sbin/bsdconfig/networking/share/resolv.subr | 16 ++++++++-------- usr.sbin/bsdconfig/networking/share/routing.subr | 2 +- usr.sbin/bsdconfig/share/common.subr | 2 +- usr.sbin/bsdconfig/share/dialog.subr | 6 +++--- usr.sbin/bsdconfig/share/mustberoot.subr | 12 ++++++------ usr.sbin/bsdconfig/share/strings.subr | 2 +- usr.sbin/bsdconfig/share/sysrc.subr | 22 +++++++++++----------- usr.sbin/bsdconfig/startup/rcadd | 2 +- usr.sbin/bsdconfig/startup/rcconf | 6 +++--- usr.sbin/bsdconfig/startup/rcdelete | 8 ++++---- usr.sbin/bsdconfig/startup/rcvar | 6 +++--- usr.sbin/bsdconfig/startup/share/rcconf.subr | 4 ++-- usr.sbin/bsdconfig/timezone/share/zones.subr | 2 +- usr.sbin/bsdconfig/usermgmt/share/user_input.subr | 12 ++++++------ 22 files changed, 62 insertions(+), 62 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index ea72d3cf164e3..c2a024daa831b 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -73,7 +73,7 @@ usage() # Determine the maximum width of terminal/console # local max_size max_width - max_size=$( stty size 2> /dev/null ) + max_size=$( stty size 2>&- ) : ${max_size:="24 80"} max_width="${max_size#*[$IFS]}" f_dprintf "max_width=[$max_width]" @@ -242,8 +242,8 @@ if [ "$1" ]; then # lang="${LANG:-$LC_ALL}" if [ "$lang" ]; then - sel=$( grep "^menu_selection=\"$1|" */INDEX.$lang \ - 2> /dev/null | tail -1 ) + sel=$( grep "^menu_selection=\"$1|" */INDEX.$lang 2>&- | + tail -1 ) # Fall-back to non-i18n sources if nothing was found [ "$sel" ] || diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys index fcf45efe63b6d..8ec55d81e2d78 100755 --- a/usr.sbin/bsdconfig/console/ttys +++ b/usr.sbin/bsdconfig/console/ttys @@ -117,8 +117,8 @@ ttys_set_type() # with 0600 permissions -- change the permissions and ownership to # match ttys(5) before we write it out and mv(1) it into place). # - local mode="$( stat -f '%#Lp' "$ETC_TTYS" 2> /dev/null )" - local owner="$( stat -f '%u:%g' "$ETC_TTYS" 2> /dev/null )" + local mode="$( stat -f '%#Lp' "$ETC_TTYS" 2>&- )" + local owner="$( stat -f '%u:%g' "$ETC_TTYS" 2>&- )" f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" diff --git a/usr.sbin/bsdconfig/mouse/enable b/usr.sbin/bsdconfig/mouse/enable index e1cf7962d6d29..30f7c57b41bc8 100755 --- a/usr.sbin/bsdconfig/mouse/enable +++ b/usr.sbin/bsdconfig/mouse/enable @@ -86,7 +86,7 @@ flags=$( f_sysrc_get moused_flags ) # f_dialog_info "$msg_trying_to_start_the_mouse_daemon" [ -r "$MOUSED_PIDFILE" ] && - f_quietly kill "$( cat "$MOUSED_PIDFILE" 2> /dev/null )" + f_quietly kill "$( cat "$MOUSED_PIDFILE" 2>&- )" f_quietly vidcontrol -m on f_quietly moused -t "$type" -p "$port" $flags diff --git a/usr.sbin/bsdconfig/networking/devices b/usr.sbin/bsdconfig/networking/devices index c7c550fe21dae..ec55155e8a8d7 100755 --- a/usr.sbin/bsdconfig/networking/devices +++ b/usr.sbin/bsdconfig/networking/devices @@ -112,7 +112,7 @@ while :; do # configuration # dhcp= - eval "$( exec 2> /dev/null + eval "$( exec 2>&- set -- $_ifconfig while [ $# -gt 0 ]; do case "$1" in diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index 17c7467bbae06..a058884b0606f 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -72,7 +72,7 @@ f_device_desc() devname="${device%%$d*}" devunit="${device#$devname}" devunit="${devunit%%[a-zA-Z_]*}" - sysctl -n "dev.$devname.$devunit.%desc" 2> /dev/null && + sysctl -n "dev.$devname.$devunit.%desc" 2>&- && return $SUCCESS fi diff --git a/usr.sbin/bsdconfig/networking/share/ipaddr.subr b/usr.sbin/bsdconfig/networking/share/ipaddr.subr index 7e858dc296a84..707e9818bccd9 100644 --- a/usr.sbin/bsdconfig/networking/share/ipaddr.subr +++ b/usr.sbin/bsdconfig/networking/share/ipaddr.subr @@ -47,7 +47,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr f_ifconfig_inet() { local interface="$1" - ifconfig "$interface" 2> /dev/null | awk \ + ifconfig "$interface" 2>&- | awk \ ' BEGIN { found = 0 } ( $1 == "inet" ) \ diff --git a/usr.sbin/bsdconfig/networking/share/media.subr b/usr.sbin/bsdconfig/networking/share/media.subr index e7da98ad3e3af..d863b3eb42b77 100644 --- a/usr.sbin/bsdconfig/networking/share/media.subr +++ b/usr.sbin/bsdconfig/networking/share/media.subr @@ -83,7 +83,7 @@ f_ifconfig_options() f_ifconfig_media() { local interface="$1" - ifconfig -m "$interface" 2> /dev/null | awk \ + ifconfig -m "$interface" 2>&- | awk \ ' BEGIN { media_found = 0 } { diff --git a/usr.sbin/bsdconfig/networking/share/netmask.subr b/usr.sbin/bsdconfig/networking/share/netmask.subr index 2f2b37853e68c..a502dc46ed1a4 100644 --- a/usr.sbin/bsdconfig/networking/share/netmask.subr +++ b/usr.sbin/bsdconfig/networking/share/netmask.subr @@ -46,7 +46,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr f_ifconfig_netmask() { local interface="$1" octets - octets=$( ifconfig "$interface" 2> /dev/null | awk \ + octets=$( ifconfig "$interface" 2>&- | awk \ ' BEGIN { found = 0 } ( $1 == "inet" ) \ diff --git a/usr.sbin/bsdconfig/networking/share/resolv.subr b/usr.sbin/bsdconfig/networking/share/resolv.subr index 154748ba52e8e..dc96345e4f06f 100644 --- a/usr.sbin/bsdconfig/networking/share/resolv.subr +++ b/usr.sbin/bsdconfig/networking/share/resolv.subr @@ -65,7 +65,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr # f_resolv_conf_domain() { - tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + tail -r "$RESOLV_CONF" 2>&- | awk \ ' BEGIN { found = 0 } ( tolower($1) == "domain" ) \ @@ -84,7 +84,7 @@ f_resolv_conf_domain() # f_resolv_conf_search() { - tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + tail -r "$RESOLV_CONF" 2>&- | awk \ ' BEGIN { found = 0 } { @@ -118,7 +118,7 @@ f_resolv_conf_nameservers() } END { exit ! found } ' \ - "$RESOLV_CONF" 2> /dev/null + "$RESOLV_CONF" 2>&- } # f_dialog_resolv_conf_update $hostname @@ -225,8 +225,8 @@ f_dialog_resolv_conf_update() # permissions and ownership to match resolv.conf(5) before # we write it out and mv(1) it into place). # - local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null )" - local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null )" + local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2>&- )" + local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2>&- )" f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" @@ -246,7 +246,7 @@ f_dialog_resolv_conf_update() # local domain="${hostname#*.}" new_contents [ "$domain" = "$hostname" ] && domain= - new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null ) + new_contents=$( tail -r "$RESOLV_CONF" 2>&- ) new_contents=$( echo "$new_contents" | awk \ -v domain="$domain" \ -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \ @@ -397,8 +397,8 @@ f_dialog_input_nameserver() # Quietly fixup permissions and ownership # local mode owner - mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null ) - owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null ) + mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2>&- ) + owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2>&- ) f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" diff --git a/usr.sbin/bsdconfig/networking/share/routing.subr b/usr.sbin/bsdconfig/networking/share/routing.subr index b8ccdcd6f987d..6c03f4c7943bc 100644 --- a/usr.sbin/bsdconfig/networking/share/routing.subr +++ b/usr.sbin/bsdconfig/networking/share/routing.subr @@ -46,7 +46,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr # f_route_get_default() { - route -n get default 2> /dev/null | awk \ + route -n get default 2>&- | awk \ ' BEGIN { found = 0 } ( $1 == "gateway:" ) \ diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index c724c62f67008..49d3023116893 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -72,7 +72,7 @@ f_err() # f_quietly() { - "$@" > /dev/null 2>&1 + "$@" >&- 2>&- } # f_have $anything ... diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index d2ba529f323c6..4f86e7bfde579 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -1321,7 +1321,7 @@ f_dialog_init() # Process stored command-line arguments # SECURE=$( set -- "$ARGV" - while getopts S flag > /dev/null; do + while getopts S flag >&-; do case "$flag" in S) echo 1;; \?) continue;; @@ -1329,7 +1329,7 @@ f_dialog_init() done ) USE_XDIALOG=$( set -- "$ARGV" - while getopts SX flag > /dev/null; do + while getopts SX flag >&-; do case "$flag" in S|X) echo 1;; \?) continue;; @@ -1372,7 +1372,7 @@ f_dialog_init() eval xauth -if \~$SUDO_USER/.Xauthority extract - \ \"\$HOSTNAME/unix:\$displaynum\" \ \"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \ - ~root/.Xauthority merge - > /dev/null 2>&1' + ~root/.Xauthority merge - >&- 2>&-' fi # diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr index c6514c0d4b1ea..234724e0803e9 100644 --- a/usr.sbin/bsdconfig/share/mustberoot.subr +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -85,7 +85,7 @@ f_become_root_via_sudo() # # Check sudo(8) access before prompting for password. # - :| sudo -S -v 2> /dev/null + :| sudo -S -v 2>&- if [ $? -ne $SUCCESS ]; then # # sudo(8) access denied. Prompt for their password. @@ -112,7 +112,7 @@ f_become_root_via_sudo() --ok-label "$msg_ok" \ --cancel-label "$msg_cancel" \ --password --inputbox "$msg" $size \ - 2>&1 > /dev/null ) + 2>&1 >&- ) retval=$? # Catch X11-related errors @@ -141,7 +141,7 @@ f_become_root_via_sudo() # # Validate sudo(8) credentials # - sudo -S -v 2> /dev/null <<-EOF + sudo -S -v 2>&- <<-EOF $password EOF retval=$? @@ -186,7 +186,7 @@ f_become_root_via_sudo() displaynum="${DISPLAY#*:}" xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ - ~root/.Xauthority merge - > /dev/null 2>&1' + ~root/.Xauthority merge - >&- 2>&-' fi # Re-execute ourselves with sudo(8) @@ -254,7 +254,7 @@ f_authenticate_some_user() $height $width \ "$field_username" "" \ "$field_password" "" \ - 2>&1 > /dev/null ) + 2>&1 >&- ) retval=$? # Catch X11-related errors @@ -302,7 +302,7 @@ f_authenticate_some_user() su -m "$user" <<-EOF sh < /dev/null <&- < /dev/null 2>&1 + ( : $((0/$arg)) ) >&- 2>&- } fi # ! $_STRINGS_SUBR diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index 014ab46a1f51f..e39b67ae4bff0 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -134,7 +134,7 @@ f_sysrc_get() # f_clean_env --except RC_CONFS RC_DEFAULTS SUCCESS - . "$RC_DEFAULTS" > /dev/null 2>&1 + . "$RC_DEFAULTS" >&- 2>&- unset RC_DEFAULTS # no longer needed @@ -153,7 +153,7 @@ f_sysrc_get() # If RC_CONFS is defined, set $rc_conf_files to an explicit # value, modifying the default behavior of source_rc_confs(). # - ( : ${RC_CONFS?} ) > /dev/null 2>&1 + ( : ${RC_CONFS?} ) >&- 2>&- if [ $? -eq ${SUCCESS:-0} ]; then rc_conf_files="$RC_CONFS" _rc_confs_set=1 @@ -162,7 +162,7 @@ f_sysrc_get() unset SUCCESS # no longer needed - source_rc_confs > /dev/null 2>&1 + source_rc_confs >&- 2>&- # # If the query was for `rc_conf_files' AND after calling @@ -190,7 +190,7 @@ f_sysrc_get() # such as "${varname?}" and "${varname:?}" (see "Parameter # Expansion" in sh(1) for more information). # - eval echo '"${'"$1"'}"' 2> /dev/null + eval echo '"${'"$1"'}"' 2>&- ) } @@ -221,7 +221,7 @@ f_sysrc_get_default() # f_clean_env --except RC_DEFAULTS - . "$RC_DEFAULTS" > /dev/null 2>&1 + . "$RC_DEFAULTS" >&- 2>&- unset RC_DEFAULTS # no longer needed @@ -232,7 +232,7 @@ f_sysrc_get_default() # such as "${varname?}" and "${varname:?}" (see "Parameter # Expansion" in sh(1) for more information). # - eval echo '"${'"$1"'}"' 2> /dev/null + eval echo '"${'"$1"'}"' 2>&- ) } @@ -477,7 +477,7 @@ f_sysrc_set() # permissions from the temporary file). # local mode - mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) + mode=$( stat -f '%#Lp' "$file" 2>&- ) f_quietly chmod "${mode:-0644}" "$tmpfile" # @@ -486,14 +486,14 @@ f_sysrc_set() # permissions (so we throw stderr into the bit-bucket). # local owner - owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$file" 2>&- ) f_quietly chown "${owner:-root:wheel}" "$tmpfile" # # Operate on the matching file, replacing only the last occurrence. # local new_contents retval - new_contents=$( tail -r $file 2> /dev/null ) + new_contents=$( tail -r $file 2>&- ) new_contents=$( echo "$new_contents" | awk -v varname="$varname" \ -v new_value="$new_value" "$f_sysrc_set_awk" ) retval=$? @@ -572,8 +572,8 @@ f_sysrc_delete() # permissions) to instead match the destination file. # local mode owner - mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) - owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) + mode=$( stat -f '%#Lp' "$file" 2>&- ) + owner=$( stat -f '%u:%g' "$file" 2>&- ) f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" diff --git a/usr.sbin/bsdconfig/startup/rcadd b/usr.sbin/bsdconfig/startup/rcadd index d9d6a045353d2..ae7f9fc70dbee 100755 --- a/usr.sbin/bsdconfig/startup/rcadd +++ b/usr.sbin/bsdconfig/startup/rcadd @@ -46,7 +46,7 @@ ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +( : ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 ############################################################ FUNCTIONS diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index 3f7c25049ea34..1055f94150643 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -54,7 +54,7 @@ RCCONF_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +( : ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 # Selectively inherit SHOW_* value (in order of preference) if [ "$SHOW_DEFAULT_VALUE" ]; then SHOW_DEFAULT_VALUE=1 @@ -110,8 +110,8 @@ dialog_menu_main() f_dialog_info "$msg_creating_menu_list" RCCONF_MENU_LIST="$RCCONF_MENU_LIST $( - . "$RC_DEFAULTS" > /dev/null - source_rc_confs > /dev/null + . "$RC_DEFAULTS" >&- + source_rc_confs >&- var_list=$( f_startup_rcconf_list ) for var in $var_list; do eval export $var diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index a97025838c087..a93733e38f099 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -54,7 +54,7 @@ RCCONF_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +( : ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 # Selectively inherit SHOW_* value (in order of preference) if [ "$SHOW_DEFAULT_VALUE" ]; then SHOW_DEFAULT_VALUE=1 @@ -112,8 +112,8 @@ dialog_menu_main() f_dialog_info "$msg_creating_menu_list" RCCONF_MENU_LIST="$RCCONF_MENU_LIST $( - . "$RC_DEFAULTS" > /dev/null - source_rc_confs > /dev/null + . "$RC_DEFAULTS" >&- + source_rc_confs >&- var_list=$( f_startup_rcconf_list ) for var in $var_list; do eval export $var @@ -348,7 +348,7 @@ while :; do var="${mtag# }" # Toggle the state-variable and loop back to menu - if ( eval : \${_${var}_delete?} ) > /dev/null 2>&1; then + if ( eval : \${_${var}_delete?} ) >&- 2>&-; then unset _${var}_delete else setvar _${var}_delete 1 diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index 799dd3426b1b6..19171391a708a 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -54,7 +54,7 @@ RCVAR_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +( ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 ############################################################ FUNCTIONS @@ -82,8 +82,8 @@ dialog_menu_main() fi RCVAR_MENU_LIST="$RCVAR_MENU_LIST $( - . "$RC_DEFAULTS" > /dev/null - source_rc_confs > /dev/null + . "$RC_DEFAULTS" >&- + source_rc_confs >&- for rcvar in $( echo "$RCVAR_MAP" | awk '{print $1}' ); do eval export $rcvar done diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index 25dec5478433b..ef413d1bee086 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -63,9 +63,9 @@ STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" f_startup_rcconf_list() { ( # Operate within a sub-shell to protect the parent environment - . "$RC_DEFAULTS" > /dev/null + . "$RC_DEFAULTS" >&- f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files - source_rc_confs > /dev/null + source_rc_confs >&- export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" export RC_DEFAULTS set | awk -F= " diff --git a/usr.sbin/bsdconfig/timezone/share/zones.subr b/usr.sbin/bsdconfig/timezone/share/zones.subr index 874b27200459f..f3db07c9b6c9e 100644 --- a/usr.sbin/bsdconfig/timezone/share/zones.subr +++ b/usr.sbin/bsdconfig/timezone/share/zones.subr @@ -610,7 +610,7 @@ f_install_zoneinfo() # Save knowledge for later if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then - if : 2> /dev/null > "$_PATH_DB"; then + if : 2>&- > "$_PATH_DB"; then cat <<-EOF > "$_PATH_DB" $zoneinfo EOF diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr index 8b356b67136e4..7fa9d79738895 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -627,7 +627,7 @@ f_dialog_input_change() local secs="$_input" { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= _input_date=$( date -j -f "%s" -- "$secs" \ - "+%d %m %Y" 2> /dev/null ) + "+%d %m %Y" 2>&- ) calendar_size=$( f_dialog_calendar_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -654,7 +654,7 @@ f_dialog_input_change() _input_time= [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) + -f %s -- "$_input" "+%H %M %S" 2>&- ) timebox_size=$( f_dialog_timebox_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -682,7 +682,7 @@ f_dialog_input_change() _input=$( date \ -j -f "%d/%m/%Y %T" \ -- "$ret_date $ret_time" \ - +%s 2> /dev/null ) + +%s 2>&- ) f_dprintf "_input=[$_input]" break ;; @@ -852,7 +852,7 @@ f_dialog_input_expire() local secs="$_input" { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= _input_date=$( date -j -f "%s" -- "$secs" \ - "+%d %m %Y" 2> /dev/null ) + "+%d %m %Y" 2>&- ) calendar_size=$( f_dialog_calendar_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -879,7 +879,7 @@ f_dialog_input_expire() _input_time= [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) + -f %s -- "$_input" "+%H %M %S" 2>&- ) timebox_size=$( f_dialog_timebox_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -906,7 +906,7 @@ f_dialog_input_expire() _input=$( date \ -j -f "%d/%m/%Y %T" \ -- "$ret_date $ret_time" \ - +%s 2> /dev/null ) + +%s 2>&- ) f_dprintf "_input=[$_input]" break ;; -- cgit v1.3 From fb7d723e6bb1b81ac1e34e86be13e9806247a2c3 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 21 Sep 2012 19:03:25 +0000 Subject: Reverse SVN r240770 -- jilles@ made a suggestion that allowed us to redesign our embedded rescue environment to support /dev/null making r240770 obsolete. Reviewed by: jilles, adrian (co-mentor) Approved by: jilles, adrian (co-mentor) --- usr.sbin/bsdconfig/bsdconfig | 6 +++--- usr.sbin/bsdconfig/console/ttys | 4 ++-- usr.sbin/bsdconfig/mouse/enable | 2 +- usr.sbin/bsdconfig/networking/devices | 2 +- usr.sbin/bsdconfig/networking/share/device.subr | 2 +- usr.sbin/bsdconfig/networking/share/ipaddr.subr | 2 +- usr.sbin/bsdconfig/networking/share/media.subr | 2 +- usr.sbin/bsdconfig/networking/share/netmask.subr | 2 +- usr.sbin/bsdconfig/networking/share/resolv.subr | 16 ++++++++-------- usr.sbin/bsdconfig/networking/share/routing.subr | 2 +- usr.sbin/bsdconfig/share/common.subr | 2 +- usr.sbin/bsdconfig/share/dialog.subr | 6 +++--- usr.sbin/bsdconfig/share/mustberoot.subr | 12 ++++++------ usr.sbin/bsdconfig/share/strings.subr | 2 +- usr.sbin/bsdconfig/share/sysrc.subr | 22 +++++++++++----------- usr.sbin/bsdconfig/startup/rcadd | 2 +- usr.sbin/bsdconfig/startup/rcconf | 6 +++--- usr.sbin/bsdconfig/startup/rcdelete | 8 ++++---- usr.sbin/bsdconfig/startup/rcvar | 6 +++--- usr.sbin/bsdconfig/startup/share/rcconf.subr | 4 ++-- usr.sbin/bsdconfig/timezone/share/zones.subr | 2 +- usr.sbin/bsdconfig/usermgmt/share/user_input.subr | 12 ++++++------ 22 files changed, 62 insertions(+), 62 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index c2a024daa831b..ea72d3cf164e3 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -73,7 +73,7 @@ usage() # Determine the maximum width of terminal/console # local max_size max_width - max_size=$( stty size 2>&- ) + max_size=$( stty size 2> /dev/null ) : ${max_size:="24 80"} max_width="${max_size#*[$IFS]}" f_dprintf "max_width=[$max_width]" @@ -242,8 +242,8 @@ if [ "$1" ]; then # lang="${LANG:-$LC_ALL}" if [ "$lang" ]; then - sel=$( grep "^menu_selection=\"$1|" */INDEX.$lang 2>&- | - tail -1 ) + sel=$( grep "^menu_selection=\"$1|" */INDEX.$lang \ + 2> /dev/null | tail -1 ) # Fall-back to non-i18n sources if nothing was found [ "$sel" ] || diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys index 8ec55d81e2d78..fcf45efe63b6d 100755 --- a/usr.sbin/bsdconfig/console/ttys +++ b/usr.sbin/bsdconfig/console/ttys @@ -117,8 +117,8 @@ ttys_set_type() # with 0600 permissions -- change the permissions and ownership to # match ttys(5) before we write it out and mv(1) it into place). # - local mode="$( stat -f '%#Lp' "$ETC_TTYS" 2>&- )" - local owner="$( stat -f '%u:%g' "$ETC_TTYS" 2>&- )" + local mode="$( stat -f '%#Lp' "$ETC_TTYS" 2> /dev/null )" + local owner="$( stat -f '%u:%g' "$ETC_TTYS" 2> /dev/null )" f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" diff --git a/usr.sbin/bsdconfig/mouse/enable b/usr.sbin/bsdconfig/mouse/enable index 30f7c57b41bc8..e1cf7962d6d29 100755 --- a/usr.sbin/bsdconfig/mouse/enable +++ b/usr.sbin/bsdconfig/mouse/enable @@ -86,7 +86,7 @@ flags=$( f_sysrc_get moused_flags ) # f_dialog_info "$msg_trying_to_start_the_mouse_daemon" [ -r "$MOUSED_PIDFILE" ] && - f_quietly kill "$( cat "$MOUSED_PIDFILE" 2>&- )" + f_quietly kill "$( cat "$MOUSED_PIDFILE" 2> /dev/null )" f_quietly vidcontrol -m on f_quietly moused -t "$type" -p "$port" $flags diff --git a/usr.sbin/bsdconfig/networking/devices b/usr.sbin/bsdconfig/networking/devices index ec55155e8a8d7..c7c550fe21dae 100755 --- a/usr.sbin/bsdconfig/networking/devices +++ b/usr.sbin/bsdconfig/networking/devices @@ -112,7 +112,7 @@ while :; do # configuration # dhcp= - eval "$( exec 2>&- + eval "$( exec 2> /dev/null set -- $_ifconfig while [ $# -gt 0 ]; do case "$1" in diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index a058884b0606f..17c7467bbae06 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -72,7 +72,7 @@ f_device_desc() devname="${device%%$d*}" devunit="${device#$devname}" devunit="${devunit%%[a-zA-Z_]*}" - sysctl -n "dev.$devname.$devunit.%desc" 2>&- && + sysctl -n "dev.$devname.$devunit.%desc" 2> /dev/null && return $SUCCESS fi diff --git a/usr.sbin/bsdconfig/networking/share/ipaddr.subr b/usr.sbin/bsdconfig/networking/share/ipaddr.subr index 707e9818bccd9..7e858dc296a84 100644 --- a/usr.sbin/bsdconfig/networking/share/ipaddr.subr +++ b/usr.sbin/bsdconfig/networking/share/ipaddr.subr @@ -47,7 +47,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr f_ifconfig_inet() { local interface="$1" - ifconfig "$interface" 2>&- | awk \ + ifconfig "$interface" 2> /dev/null | awk \ ' BEGIN { found = 0 } ( $1 == "inet" ) \ diff --git a/usr.sbin/bsdconfig/networking/share/media.subr b/usr.sbin/bsdconfig/networking/share/media.subr index d863b3eb42b77..e7da98ad3e3af 100644 --- a/usr.sbin/bsdconfig/networking/share/media.subr +++ b/usr.sbin/bsdconfig/networking/share/media.subr @@ -83,7 +83,7 @@ f_ifconfig_options() f_ifconfig_media() { local interface="$1" - ifconfig -m "$interface" 2>&- | awk \ + ifconfig -m "$interface" 2> /dev/null | awk \ ' BEGIN { media_found = 0 } { diff --git a/usr.sbin/bsdconfig/networking/share/netmask.subr b/usr.sbin/bsdconfig/networking/share/netmask.subr index a502dc46ed1a4..2f2b37853e68c 100644 --- a/usr.sbin/bsdconfig/networking/share/netmask.subr +++ b/usr.sbin/bsdconfig/networking/share/netmask.subr @@ -46,7 +46,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr f_ifconfig_netmask() { local interface="$1" octets - octets=$( ifconfig "$interface" 2>&- | awk \ + octets=$( ifconfig "$interface" 2> /dev/null | awk \ ' BEGIN { found = 0 } ( $1 == "inet" ) \ diff --git a/usr.sbin/bsdconfig/networking/share/resolv.subr b/usr.sbin/bsdconfig/networking/share/resolv.subr index dc96345e4f06f..154748ba52e8e 100644 --- a/usr.sbin/bsdconfig/networking/share/resolv.subr +++ b/usr.sbin/bsdconfig/networking/share/resolv.subr @@ -65,7 +65,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr # f_resolv_conf_domain() { - tail -r "$RESOLV_CONF" 2>&- | awk \ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ ' BEGIN { found = 0 } ( tolower($1) == "domain" ) \ @@ -84,7 +84,7 @@ f_resolv_conf_domain() # f_resolv_conf_search() { - tail -r "$RESOLV_CONF" 2>&- | awk \ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ ' BEGIN { found = 0 } { @@ -118,7 +118,7 @@ f_resolv_conf_nameservers() } END { exit ! found } ' \ - "$RESOLV_CONF" 2>&- + "$RESOLV_CONF" 2> /dev/null } # f_dialog_resolv_conf_update $hostname @@ -225,8 +225,8 @@ f_dialog_resolv_conf_update() # permissions and ownership to match resolv.conf(5) before # we write it out and mv(1) it into place). # - local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2>&- )" - local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2>&- )" + local mode="$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null )" + local owner="$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null )" f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" @@ -246,7 +246,7 @@ f_dialog_resolv_conf_update() # local domain="${hostname#*.}" new_contents [ "$domain" = "$hostname" ] && domain= - new_contents=$( tail -r "$RESOLV_CONF" 2>&- ) + new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null ) new_contents=$( echo "$new_contents" | awk \ -v domain="$domain" \ -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \ @@ -397,8 +397,8 @@ f_dialog_input_nameserver() # Quietly fixup permissions and ownership # local mode owner - mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2>&- ) - owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2>&- ) + mode=$( stat -f '%#Lp' "$RESOLV_CONF" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$RESOLV_CONF" 2> /dev/null ) f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" diff --git a/usr.sbin/bsdconfig/networking/share/routing.subr b/usr.sbin/bsdconfig/networking/share/routing.subr index 6c03f4c7943bc..b8ccdcd6f987d 100644 --- a/usr.sbin/bsdconfig/networking/share/routing.subr +++ b/usr.sbin/bsdconfig/networking/share/routing.subr @@ -46,7 +46,7 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr # f_route_get_default() { - route -n get default 2>&- | awk \ + route -n get default 2> /dev/null | awk \ ' BEGIN { found = 0 } ( $1 == "gateway:" ) \ diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 49d3023116893..c724c62f67008 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -72,7 +72,7 @@ f_err() # f_quietly() { - "$@" >&- 2>&- + "$@" > /dev/null 2>&1 } # f_have $anything ... diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index 4f86e7bfde579..d2ba529f323c6 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -1321,7 +1321,7 @@ f_dialog_init() # Process stored command-line arguments # SECURE=$( set -- "$ARGV" - while getopts S flag >&-; do + while getopts S flag > /dev/null; do case "$flag" in S) echo 1;; \?) continue;; @@ -1329,7 +1329,7 @@ f_dialog_init() done ) USE_XDIALOG=$( set -- "$ARGV" - while getopts SX flag >&-; do + while getopts SX flag > /dev/null; do case "$flag" in S|X) echo 1;; \?) continue;; @@ -1372,7 +1372,7 @@ f_dialog_init() eval xauth -if \~$SUDO_USER/.Xauthority extract - \ \"\$HOSTNAME/unix:\$displaynum\" \ \"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \ - ~root/.Xauthority merge - >&- 2>&-' + ~root/.Xauthority merge - > /dev/null 2>&1' fi # diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr index 234724e0803e9..c6514c0d4b1ea 100644 --- a/usr.sbin/bsdconfig/share/mustberoot.subr +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -85,7 +85,7 @@ f_become_root_via_sudo() # # Check sudo(8) access before prompting for password. # - :| sudo -S -v 2>&- + :| sudo -S -v 2> /dev/null if [ $? -ne $SUCCESS ]; then # # sudo(8) access denied. Prompt for their password. @@ -112,7 +112,7 @@ f_become_root_via_sudo() --ok-label "$msg_ok" \ --cancel-label "$msg_cancel" \ --password --inputbox "$msg" $size \ - 2>&1 >&- ) + 2>&1 > /dev/null ) retval=$? # Catch X11-related errors @@ -141,7 +141,7 @@ f_become_root_via_sudo() # # Validate sudo(8) credentials # - sudo -S -v 2>&- <<-EOF + sudo -S -v 2> /dev/null <<-EOF $password EOF retval=$? @@ -186,7 +186,7 @@ f_become_root_via_sudo() displaynum="${DISPLAY#*:}" xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ - ~root/.Xauthority merge - >&- 2>&-' + ~root/.Xauthority merge - > /dev/null 2>&1' fi # Re-execute ourselves with sudo(8) @@ -254,7 +254,7 @@ f_authenticate_some_user() $height $width \ "$field_username" "" \ "$field_password" "" \ - 2>&1 >&- ) + 2>&1 > /dev/null ) retval=$? # Catch X11-related errors @@ -302,7 +302,7 @@ f_authenticate_some_user() su -m "$user" <<-EOF sh <&- < /dev/null <&- 2>&- + ( : $((0/$arg)) ) > /dev/null 2>&1 } fi # ! $_STRINGS_SUBR diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index e39b67ae4bff0..014ab46a1f51f 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -134,7 +134,7 @@ f_sysrc_get() # f_clean_env --except RC_CONFS RC_DEFAULTS SUCCESS - . "$RC_DEFAULTS" >&- 2>&- + . "$RC_DEFAULTS" > /dev/null 2>&1 unset RC_DEFAULTS # no longer needed @@ -153,7 +153,7 @@ f_sysrc_get() # If RC_CONFS is defined, set $rc_conf_files to an explicit # value, modifying the default behavior of source_rc_confs(). # - ( : ${RC_CONFS?} ) >&- 2>&- + ( : ${RC_CONFS?} ) > /dev/null 2>&1 if [ $? -eq ${SUCCESS:-0} ]; then rc_conf_files="$RC_CONFS" _rc_confs_set=1 @@ -162,7 +162,7 @@ f_sysrc_get() unset SUCCESS # no longer needed - source_rc_confs >&- 2>&- + source_rc_confs > /dev/null 2>&1 # # If the query was for `rc_conf_files' AND after calling @@ -190,7 +190,7 @@ f_sysrc_get() # such as "${varname?}" and "${varname:?}" (see "Parameter # Expansion" in sh(1) for more information). # - eval echo '"${'"$1"'}"' 2>&- + eval echo '"${'"$1"'}"' 2> /dev/null ) } @@ -221,7 +221,7 @@ f_sysrc_get_default() # f_clean_env --except RC_DEFAULTS - . "$RC_DEFAULTS" >&- 2>&- + . "$RC_DEFAULTS" > /dev/null 2>&1 unset RC_DEFAULTS # no longer needed @@ -232,7 +232,7 @@ f_sysrc_get_default() # such as "${varname?}" and "${varname:?}" (see "Parameter # Expansion" in sh(1) for more information). # - eval echo '"${'"$1"'}"' 2>&- + eval echo '"${'"$1"'}"' 2> /dev/null ) } @@ -477,7 +477,7 @@ f_sysrc_set() # permissions from the temporary file). # local mode - mode=$( stat -f '%#Lp' "$file" 2>&- ) + mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) f_quietly chmod "${mode:-0644}" "$tmpfile" # @@ -486,14 +486,14 @@ f_sysrc_set() # permissions (so we throw stderr into the bit-bucket). # local owner - owner=$( stat -f '%u:%g' "$file" 2>&- ) + owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) f_quietly chown "${owner:-root:wheel}" "$tmpfile" # # Operate on the matching file, replacing only the last occurrence. # local new_contents retval - new_contents=$( tail -r $file 2>&- ) + new_contents=$( tail -r $file 2> /dev/null ) new_contents=$( echo "$new_contents" | awk -v varname="$varname" \ -v new_value="$new_value" "$f_sysrc_set_awk" ) retval=$? @@ -572,8 +572,8 @@ f_sysrc_delete() # permissions) to instead match the destination file. # local mode owner - mode=$( stat -f '%#Lp' "$file" 2>&- ) - owner=$( stat -f '%u:%g' "$file" 2>&- ) + mode=$( stat -f '%#Lp' "$file" 2> /dev/null ) + owner=$( stat -f '%u:%g' "$file" 2> /dev/null ) f_quietly chmod "${mode:-0644}" "$tmpfile" f_quietly chown "${owner:-root:wheel}" "$tmpfile" diff --git a/usr.sbin/bsdconfig/startup/rcadd b/usr.sbin/bsdconfig/startup/rcadd index ae7f9fc70dbee..d9d6a045353d2 100755 --- a/usr.sbin/bsdconfig/startup/rcadd +++ b/usr.sbin/bsdconfig/startup/rcadd @@ -46,7 +46,7 @@ ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 +( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 ############################################################ FUNCTIONS diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index 1055f94150643..3f7c25049ea34 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -54,7 +54,7 @@ RCCONF_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 +( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 # Selectively inherit SHOW_* value (in order of preference) if [ "$SHOW_DEFAULT_VALUE" ]; then SHOW_DEFAULT_VALUE=1 @@ -110,8 +110,8 @@ dialog_menu_main() f_dialog_info "$msg_creating_menu_list" RCCONF_MENU_LIST="$RCCONF_MENU_LIST $( - . "$RC_DEFAULTS" >&- - source_rc_confs >&- + . "$RC_DEFAULTS" > /dev/null + source_rc_confs > /dev/null var_list=$( f_startup_rcconf_list ) for var in $var_list; do eval export $var diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index a93733e38f099..a97025838c087 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -54,7 +54,7 @@ RCCONF_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 +( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 # Selectively inherit SHOW_* value (in order of preference) if [ "$SHOW_DEFAULT_VALUE" ]; then SHOW_DEFAULT_VALUE=1 @@ -112,8 +112,8 @@ dialog_menu_main() f_dialog_info "$msg_creating_menu_list" RCCONF_MENU_LIST="$RCCONF_MENU_LIST $( - . "$RC_DEFAULTS" >&- - source_rc_confs >&- + . "$RC_DEFAULTS" > /dev/null + source_rc_confs > /dev/null var_list=$( f_startup_rcconf_list ) for var in $var_list; do eval export $var @@ -348,7 +348,7 @@ while :; do var="${mtag# }" # Toggle the state-variable and loop back to menu - if ( eval : \${_${var}_delete?} ) >&- 2>&-; then + if ( eval : \${_${var}_delete?} ) > /dev/null 2>&1; then unset _${var}_delete else setvar _${var}_delete 1 diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index 19171391a708a..799dd3426b1b6 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -54,7 +54,7 @@ RCVAR_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( ${SHOW_DESC?} ) >&- 2>&- || SHOW_DESC=1 +( ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 ############################################################ FUNCTIONS @@ -82,8 +82,8 @@ dialog_menu_main() fi RCVAR_MENU_LIST="$RCVAR_MENU_LIST $( - . "$RC_DEFAULTS" >&- - source_rc_confs >&- + . "$RC_DEFAULTS" > /dev/null + source_rc_confs > /dev/null for rcvar in $( echo "$RCVAR_MAP" | awk '{print $1}' ); do eval export $rcvar done diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index ef413d1bee086..25dec5478433b 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -63,9 +63,9 @@ STARTUP_RCCONF_MAP_CACHEFILE="/var/run/bsdconfig/startup_rcconf_map.cache" f_startup_rcconf_list() { ( # Operate within a sub-shell to protect the parent environment - . "$RC_DEFAULTS" >&- + . "$RC_DEFAULTS" > /dev/null f_clean_env --except PATH STARTUP_RCCONF_REGEX rc_conf_files - source_rc_confs >&- + source_rc_confs > /dev/null export _rc_conf_files_file="$( f_sysrc_find rc_conf_files )" export RC_DEFAULTS set | awk -F= " diff --git a/usr.sbin/bsdconfig/timezone/share/zones.subr b/usr.sbin/bsdconfig/timezone/share/zones.subr index f3db07c9b6c9e..874b27200459f 100644 --- a/usr.sbin/bsdconfig/timezone/share/zones.subr +++ b/usr.sbin/bsdconfig/timezone/share/zones.subr @@ -610,7 +610,7 @@ f_install_zoneinfo() # Save knowledge for later if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then - if : 2>&- > "$_PATH_DB"; then + if : 2> /dev/null > "$_PATH_DB"; then cat <<-EOF > "$_PATH_DB" $zoneinfo EOF diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr index 7fa9d79738895..8b356b67136e4 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -627,7 +627,7 @@ f_dialog_input_change() local secs="$_input" { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= _input_date=$( date -j -f "%s" -- "$secs" \ - "+%d %m %Y" 2>&- ) + "+%d %m %Y" 2> /dev/null ) calendar_size=$( f_dialog_calendar_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -654,7 +654,7 @@ f_dialog_input_change() _input_time= [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2>&- ) + -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) timebox_size=$( f_dialog_timebox_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -682,7 +682,7 @@ f_dialog_input_change() _input=$( date \ -j -f "%d/%m/%Y %T" \ -- "$ret_date $ret_time" \ - +%s 2>&- ) + +%s 2> /dev/null ) f_dprintf "_input=[$_input]" break ;; @@ -852,7 +852,7 @@ f_dialog_input_expire() local secs="$_input" { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= _input_date=$( date -j -f "%s" -- "$secs" \ - "+%d %m %Y" 2>&- ) + "+%d %m %Y" 2> /dev/null ) calendar_size=$( f_dialog_calendar_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -879,7 +879,7 @@ f_dialog_input_expire() _input_time= [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2>&- ) + -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) timebox_size=$( f_dialog_timebox_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ @@ -906,7 +906,7 @@ f_dialog_input_expire() _input=$( date \ -j -f "%d/%m/%Y %T" \ -- "$ret_date $ret_time" \ - +%s 2>&- ) + +%s 2> /dev/null ) f_dprintf "_input=[$_input]" break ;; -- cgit v1.3 From 2d49f165a9fcfc82a440f71d45d23cb507497159 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 22 Sep 2012 03:11:35 +0000 Subject: Spelling and whitespace corrections. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/include/messages.subr | 2 +- usr.sbin/bsdconfig/share/common.subr | 6 +++--- usr.sbin/bsdconfig/share/sysrc.subr | 4 ++-- usr.sbin/bsdconfig/startup/share/rcconf.subr | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr index 282d9369c4bf5..fd3844028ab8f 100644 --- a/usr.sbin/bsdconfig/include/messages.subr +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -44,7 +44,7 @@ msg_no_such_file_or_directory="%s: %s: No such file or directory" msg_no_username="No username provided!" msg_not_found="not found" msg_ok="OK" -msg_permission_denied="%s: %s: permission denied" +msg_permission_denied="%s: %s: Permission denied" msg_please_enter_password="Please enter your password for sudo(8):" msg_please_enter_username_password="Please enter a username and password for sudo(8):" msg_previous_syntax_errors="%s: Not overwriting \`%s' due to previous syntax errors" diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index c724c62f67008..5305a85e72318 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -63,12 +63,12 @@ f_dprintf() # f_err() { - printf "$@" >&2 + printf "$@" >&2 } # f_quietly $command [ $arguments ... ] # -# run a command quietly (quell any output to stdout or stderr) +# Run a command quietly (quell any output to stdout or stderr) # f_quietly() { @@ -76,7 +76,7 @@ f_quietly() } # f_have $anything ... -# +# # A wrapper to the `type' built-in. Returns true if argument is a valid shell # built-in, keyword, or externally-tracked binary, otherwise false. # diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index 014ab46a1f51f..80deaa8be6c6e 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -17,7 +17,7 @@ if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1 # 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 (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# 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 @@ -166,7 +166,7 @@ f_sysrc_get() # # If the query was for `rc_conf_files' AND after calling - # source_rc_confs the vaue has not changed, then we should + # source_rc_confs the value has not changed, then we should # restore the value to the one inherited from RC_DEFAULTS # before performing the final query (preventing us from # returning what was set via RC_CONFS when the intent was diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index 25dec5478433b..16cb29081e3eb 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -113,8 +113,8 @@ f_startup_rcconf_map() # # Calculate digest used to determine if the on-disk global persistant - # cache file (containg this digest on the first line) is valid and can - # be used to quickly populate the cache value for immediate return. + # cache file (containing this digest on the first line) is valid and + # can be used to quickly populate the cache value for immediate return. # local rc_defaults_digest rc_defaults_digest=$( md5 < "$RC_DEFAULTS" ) -- cgit v1.3 From 8d8d314d78caf28e5103b42d035779a516153b0c Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 22 Sep 2012 04:04:02 +0000 Subject: Replace "( : ${var?} )" syntax with better "[ ${var+set} ]" syntax. Reviewed by: jilles, adrian (co-mentor) Approved by: jilles, adrian (co-mentor) --- usr.sbin/bsdconfig/share/sysrc.subr | 8 ++------ usr.sbin/bsdconfig/startup/rcadd | 2 +- usr.sbin/bsdconfig/startup/rcconf | 2 +- usr.sbin/bsdconfig/startup/rcdelete | 4 ++-- usr.sbin/bsdconfig/startup/rcvar | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index 80deaa8be6c6e..51aabcccad5d3 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -132,7 +132,7 @@ f_sysrc_get() # Clear the environment of all variables, preventing the # expansion of normals such as `PS1', `TERM', etc. # - f_clean_env --except RC_CONFS RC_DEFAULTS SUCCESS + f_clean_env --except RC_CONFS RC_DEFAULTS . "$RC_DEFAULTS" > /dev/null 2>&1 @@ -153,15 +153,11 @@ f_sysrc_get() # If RC_CONFS is defined, set $rc_conf_files to an explicit # value, modifying the default behavior of source_rc_confs(). # - ( : ${RC_CONFS?} ) > /dev/null 2>&1 - if [ $? -eq ${SUCCESS:-0} ]; then + if [ "${RC_CONFS+set}" ]; then rc_conf_files="$RC_CONFS" _rc_confs_set=1 fi - unset SUCCESS - # no longer needed - source_rc_confs > /dev/null 2>&1 # diff --git a/usr.sbin/bsdconfig/startup/rcadd b/usr.sbin/bsdconfig/startup/rcadd index d9d6a045353d2..8f28840bf4547 100755 --- a/usr.sbin/bsdconfig/startup/rcadd +++ b/usr.sbin/bsdconfig/startup/rcadd @@ -46,7 +46,7 @@ ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +[ "${SHOW_DESC+set}" ] || SHOW_DESC=1 ############################################################ FUNCTIONS diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index 3f7c25049ea34..4fb58593ebf08 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -54,7 +54,7 @@ RCCONF_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +[ "${SHOW_DESC+set}" ] || SHOW_DESC=1 # Selectively inherit SHOW_* value (in order of preference) if [ "$SHOW_DEFAULT_VALUE" ]; then SHOW_DEFAULT_VALUE=1 diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index a97025838c087..b431d884b2e0a 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -54,7 +54,7 @@ RCCONF_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( : ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +[ "${SHOW_DESC+set}" ] || SHOW_DESC=1 # Selectively inherit SHOW_* value (in order of preference) if [ "$SHOW_DEFAULT_VALUE" ]; then SHOW_DEFAULT_VALUE=1 @@ -348,7 +348,7 @@ while :; do var="${mtag# }" # Toggle the state-variable and loop back to menu - if ( eval : \${_${var}_delete?} ) > /dev/null 2>&1; then + if eval [ \"\${_${var}_delete+set}\" ]; then unset _${var}_delete else setvar _${var}_delete 1 diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index 799dd3426b1b6..b7a52295ea1eb 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -54,7 +54,7 @@ RCVAR_MENU_LIST= # Options # # Inherit SHOW_DESC value if set, otherwise default to 1 -( ${SHOW_DESC?} ) > /dev/null 2>&1 || SHOW_DESC=1 +[ "${SHOW_DESC+set}" ] || SHOW_DESC=1 ############################################################ FUNCTIONS -- cgit v1.3 From 437f6520388f39c410d59d99e5df7490e2e51f32 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 22 Sep 2012 22:16:07 +0000 Subject: jilles accurately advises that, in the context of shell redirection, the `:' builtin is processed specially and thus the `: > file' syntax for example will cause premature termination of the current shell on redirection-error. The `true' builtin on the other-hand is not included in this special processing (for compatibility reasons to satisfy legacy scripts programmed for systems where `true' is not a builtin). Change bare `: > file' syntax into `true > file' syntax to prevent premature shell termination in the event of redirection-error. NOTE: Instances of `: > file' that appear within a sub-shell have been left unmodified as these will not cause premature termination of the main script. Reviewed by: jilles, adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/timezone/share/zones.subr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/timezone/share/zones.subr b/usr.sbin/bsdconfig/timezone/share/zones.subr index 874b27200459f..81008a97c5f6b 100644 --- a/usr.sbin/bsdconfig/timezone/share/zones.subr +++ b/usr.sbin/bsdconfig/timezone/share/zones.subr @@ -610,7 +610,7 @@ f_install_zoneinfo() # Save knowledge for later if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then - if : 2> /dev/null > "$_PATH_DB"; then + if true 2> /dev/null > "$_PATH_DB"; then cat <<-EOF > "$_PATH_DB" $zoneinfo EOF -- cgit v1.3 From 0ad5179deea5284d55bbfdd37d48995b84a758b0 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sun, 23 Sep 2012 17:47:01 +0000 Subject: Always use RC_CONFS when set, even if NULL. Previously only used if non-NULL. Reviewed by: jilles, adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/share/sysrc.subr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index 51aabcccad5d3..dc231d4f71f96 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -256,7 +256,7 @@ f_sysrc_find() # If RC_CONFS is defined, set $rc_conf_files to an explicit # value, modifying the default behavior of source_rc_confs(). # - [ "$RC_CONFS" ] && rc_conf_files="$RC_CONFS" + [ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS" # # Reverse the order of files in rc_conf_files (the boot process sources @@ -435,7 +435,7 @@ f_sysrc_set() # If RC_CONFS is defined, use $RC_CONFS # rather than $rc_conf_files. # - if [ "$RC_CONFS" ]; then + if [ "${RC_CONFS+set}" ]; then file="${RC_CONFS%%[$IFS]*}" else file=$( f_sysrc_get rc_conf_files ) @@ -554,7 +554,7 @@ f_sysrc_delete() # # Operate on each of the specified files # - for file in ${RC_CONFS:-$( f_sysrc_get rc_conf_files )}; do + for file in ${RC_CONFS-$( f_sysrc_get rc_conf_files )}; do [ -e "$file" ] || continue # -- cgit v1.3 From 08f950a3025c710ae8a719539030d587fcbbad64 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Mon, 24 Sep 2012 17:13:33 +0000 Subject: Make sure moused is exiting as quick as possible after that the umsX character device returns a read error. Update devd.conf rules to use "DEVFS" events which are generated after that the umsX character device node has been created/destroyed, and then there should be no need for moused to wait up to 10 seconds for umsX to be ready. Opening umsX should not fail except if the kernel is low on memory. In that case the user can replug the USB mouse or use "usbconfig" to reset the device. In case of USB mouse devices, moused should neither retry to open its character device, once the first read error has happened. This is an indication of device detach. MFC after: 1 week --- etc/devd.conf | 20 ++++++++++++++------ usr.sbin/moused/moused.c | 20 ++++++++------------ 2 files changed, 22 insertions(+), 18 deletions(-) (limited to 'usr.sbin') diff --git a/etc/devd.conf b/etc/devd.conf index fa9ed986b4b94..d379ab0472feb 100644 --- a/etc/devd.conf +++ b/etc/devd.conf @@ -115,14 +115,22 @@ detach 100 { action "/etc/rc.d/syscons setkeyboard /dev/kbd0"; }; -attach 100 { - device-name "ums[0-9]+"; - action "/etc/rc.d/moused quietstart $device-name"; +notify 100 { + match "system" "DEVFS"; + match "subsystem" "CDEV"; + match "type" "CREATE"; + match "cdev" "ums[0-9]+"; + + action "/etc/rc.d/moused quietstart $cdev"; }; -detach 100 { - device-name "ums[0-9]+"; - action "/etc/rc.d/moused stop $device-name"; +notify 100 { + match "system" "DEVFS"; + match "subsystem" "CDEV"; + match "type" "DESTROY"; + match "cdev" "ums[0-9]+"; + + action "/etc/rc.d/moused stop $cdev"; }; # Firmware download into the ActiveWire board. After the firmware download is diff --git a/usr.sbin/moused/moused.c b/usr.sbin/moused/moused.c index 78e65fa9f52e2..93eec99093685 100644 --- a/usr.sbin/moused/moused.c +++ b/usr.sbin/moused/moused.c @@ -408,6 +408,7 @@ static struct rodentparam { int cfd; /* /dev/consolectl file descriptor */ int mremsfd; /* mouse remote server file descriptor */ int mremcfd; /* mouse remote client file descriptor */ + int is_removable; /* set if device is removable, like USB */ long clickthreshold; /* double click speed in msec */ long button2timeout; /* 3 button emulation timeout */ mousehw_t hw; /* mouse device hardware information */ @@ -434,6 +435,7 @@ static struct rodentparam { .cfd = -1, .mremsfd = -1, .mremcfd = -1, + .is_removable = 0, .clickthreshold = DFLT_CLICKTHRESHOLD, .button2timeout = DFLT_BUTTON2TIMEOUT, .accelx = 1.0, @@ -570,7 +572,6 @@ main(int argc, char *argv[]) int c; int i; int j; - static int retry; for (i = 0; i < MOUSE_MAXBUTTON; ++i) mstate[i] = &bstate[i]; @@ -876,10 +877,8 @@ main(int argc, char *argv[]) usage(); } - retry = 1; - if (strncmp(rodent.portname, "/dev/ums", 8) == 0) { - retry = 5; - } + if (strncmp(rodent.portname, "/dev/ums", 8) == 0) + rodent.is_removable = 1; for (;;) { if (setjmp(env) == 0) { @@ -888,13 +887,8 @@ main(int argc, char *argv[]) signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); signal(SIGUSR1, pause_mouse); - for (i = 0; i < retry; ++i) { - if (i > 0) - sleep(2); - rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK); - if (rodent.mfd != -1 || errno != ENOENT) - break; - } + + rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK); if (rodent.mfd == -1) logerr(1, "unable to open %s", rodent.portname); if (r_identify() == MOUSE_PROTO_UNKNOWN) { @@ -944,6 +938,8 @@ main(int argc, char *argv[]) if (rodent.cfd != -1) close(rodent.cfd); rodent.mfd = rodent.cfd = -1; + if (rodent.is_removable) + exit(0); } /* NOT REACHED */ -- cgit v1.3 From c3f86a256c6e55f2d6407c34323fa525c0743827 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 24 Sep 2012 23:57:16 +0000 Subject: Attila Bogar reported a bug in mountd when multiple export entries with different security flavors are in the exports(5) file. For that case, mountd replies with the security flavors of the last entry and not the correct one for the client host. This patch fixes that by storing separate copies of the flavors for each host/net case, plus a default one for the case where no hosts/nets are specified on an entry in the exports(5) file. Unlike the patch in the PR, it replies with the security flavors for the entry instead of merging the security flavors for all the entries and replying with that. Tested by: attila.bogar at linguamatics.com PR: kern/164933 MFC after: 2 weeks --- usr.sbin/mountd/mountd.c | 71 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 14 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c index ffc0a09e6d715..b066fc748b5dd 100644 --- a/usr.sbin/mountd/mountd.c +++ b/usr.sbin/mountd/mountd.c @@ -117,6 +117,8 @@ struct exportlist { char *ex_indexfile; int ex_numsecflavors; int ex_secflavors[MAXSECFLAVORS]; + int ex_defnumsecflavors; + int ex_defsecflavors[MAXSECFLAVORS]; }; /* ex_flag bits */ #define EX_LINKED 0x1 @@ -136,6 +138,8 @@ struct grouplist { int gr_type; union grouptypes gr_ptr; struct grouplist *gr_next; + int gr_numsecflavors; + int gr_secflavors[MAXSECFLAVORS]; }; /* Group types */ #define GT_NULL 0x0 @@ -163,12 +167,13 @@ struct fhreturn { /* Global defs */ char *add_expdir(struct dirlist **, char *, int); void add_dlist(struct dirlist **, struct dirlist *, - struct grouplist *, int); + struct grouplist *, int, struct exportlist *); void add_mlist(char *, char *); int check_dirpath(char *); int check_options(struct dirlist *); int checkmask(struct sockaddr *sa); -int chk_host(struct dirlist *, struct sockaddr *, int *, int *); +int chk_host(struct dirlist *, struct sockaddr *, int *, int *, int *, + int **); static int create_service(struct netconfig *nconf); static void complete_service(struct netconfig *nconf, char *port_str); static void clearout_service(void); @@ -938,6 +943,7 @@ mntsrv(struct svc_req *rqstp, SVCXPRT *transp) char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; int bad = 0, defset, hostset; sigset_t sighup_mask; + int numsecflavors, *secflavorsp; sigemptyset(&sighup_mask); sigaddset(&sighup_mask, SIGHUP); @@ -1000,9 +1006,11 @@ mntsrv(struct svc_req *rqstp, SVCXPRT *transp) sigprocmask(SIG_BLOCK, &sighup_mask, NULL); ep = ex_search(&fsb.f_fsid); hostset = defset = 0; - if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || + if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, + &numsecflavors, &secflavorsp) || ((dp = dirp_search(ep->ex_dirl, dirpath)) && - chk_host(dp, saddr, &defset, &hostset)) || + chk_host(dp, saddr, &defset, &hostset, &numsecflavors, + &secflavorsp)) || (defset && scan_tree(ep->ex_defdir, saddr) == 0 && scan_tree(ep->ex_dirl, saddr) == 0))) { if (bad) { @@ -1012,10 +1020,15 @@ mntsrv(struct svc_req *rqstp, SVCXPRT *transp) sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); return; } - if (hostset & DP_HOSTSET) + if (hostset & DP_HOSTSET) { fhr.fhr_flag = hostset; - else + fhr.fhr_numsecflavors = numsecflavors; + fhr.fhr_secflavors = secflavorsp; + } else { fhr.fhr_flag = defset; + fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; + fhr.fhr_secflavors = ep->ex_defsecflavors; + } fhr.fhr_vers = rqstp->rq_vers; /* Get the file handle */ memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); @@ -1028,8 +1041,6 @@ mntsrv(struct svc_req *rqstp, SVCXPRT *transp) sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); return; } - fhr.fhr_numsecflavors = ep->ex_numsecflavors; - fhr.fhr_secflavors = ep->ex_secflavors; if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, (caddr_t)&fhr)) syslog(LOG_ERR, "can't send reply"); @@ -1881,11 +1892,21 @@ hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, ep->ex_defdir = dp; if (grp == (struct grouplist *)NULL) { ep->ex_defdir->dp_flag |= DP_DEFSET; + /* Save the default security flavors list. */ + ep->ex_defnumsecflavors = ep->ex_numsecflavors; + if (ep->ex_numsecflavors > 0) + memcpy(ep->ex_defsecflavors, ep->ex_secflavors, + sizeof(ep->ex_secflavors)); } else while (grp) { hp = get_ht(); hp->ht_grp = grp; hp->ht_next = ep->ex_defdir->dp_hosts; ep->ex_defdir->dp_hosts = hp; + /* Save the security flavors list for this host set. */ + grp->gr_numsecflavors = ep->ex_numsecflavors; + if (ep->ex_numsecflavors > 0) + memcpy(grp->gr_secflavors, ep->ex_secflavors, + sizeof(ep->ex_secflavors)); grp = grp->gr_next; } } else { @@ -1895,7 +1916,7 @@ hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, */ while (dp) { dp2 = dp->dp_left; - add_dlist(&ep->ex_dirl, dp, grp, flags); + add_dlist(&ep->ex_dirl, dp, grp, flags, ep); dp = dp2; } } @@ -1907,7 +1928,7 @@ hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, */ void add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, - int flags) + int flags, struct exportlist *ep) { struct dirlist *dp; struct hostlist *hp; @@ -1917,10 +1938,10 @@ add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, if (dp) { cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); if (cmp > 0) { - add_dlist(&dp->dp_left, newdp, grp, flags); + add_dlist(&dp->dp_left, newdp, grp, flags, ep); return; } else if (cmp < 0) { - add_dlist(&dp->dp_right, newdp, grp, flags); + add_dlist(&dp->dp_right, newdp, grp, flags, ep); return; } else free((caddr_t)newdp); @@ -1939,10 +1960,20 @@ add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, hp->ht_grp = grp; hp->ht_next = dp->dp_hosts; dp->dp_hosts = hp; + /* Save the security flavors list for this host set. */ + grp->gr_numsecflavors = ep->ex_numsecflavors; + if (ep->ex_numsecflavors > 0) + memcpy(grp->gr_secflavors, ep->ex_secflavors, + sizeof(ep->ex_secflavors)); grp = grp->gr_next; } while (grp); } else { dp->dp_flag |= DP_DEFSET; + /* Save the default security flavors list. */ + ep->ex_defnumsecflavors = ep->ex_numsecflavors; + if (ep->ex_numsecflavors > 0) + memcpy(ep->ex_defsecflavors, ep->ex_secflavors, + sizeof(ep->ex_secflavors)); } } @@ -1971,7 +2002,7 @@ dirp_search(struct dirlist *dp, char *dirp) */ int chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, - int *hostsetp) + int *hostsetp, int *numsecflavors, int **secflavorsp) { struct hostlist *hp; struct grouplist *grp; @@ -1990,6 +2021,12 @@ chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, if (!sacmp(ai->ai_addr, saddr, NULL)) { *hostsetp = (hp->ht_flag | DP_HOSTSET); + if (numsecflavors != NULL) { + *numsecflavors = + grp->gr_numsecflavors; + *secflavorsp = + grp->gr_secflavors; + } return (1); } } @@ -2000,6 +2037,12 @@ chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, (struct sockaddr *) &grp->gr_ptr.gt_net.nt_mask)) { *hostsetp = (hp->ht_flag | DP_HOSTSET); + if (numsecflavors != NULL) { + *numsecflavors = + grp->gr_numsecflavors; + *secflavorsp = + grp->gr_secflavors; + } return (1); } break; @@ -2021,7 +2064,7 @@ scan_tree(struct dirlist *dp, struct sockaddr *saddr) if (dp) { if (scan_tree(dp->dp_left, saddr)) return (1); - if (chk_host(dp, saddr, &defset, &hostset)) + if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) return (1); if (scan_tree(dp->dp_right, saddr)) return (1); -- cgit v1.3 From b424efd5b11685876e5e8bb564068cee4a2bff4b Mon Sep 17 00:00:00 2001 From: Matthew D Fleming Date: Thu, 27 Sep 2012 23:31:19 +0000 Subject: Fix usr.bin/ and usr.sbin/ build with a 64-bit ino_t. Original code by: Gleb Kurtsou --- usr.bin/find/ls.c | 2 +- usr.sbin/lpr/lpr/lpr.c | 5 +++-- usr.sbin/makefs/ffs/ffs_alloc.c | 5 +++-- usr.sbin/quot/quot.c | 6 +++--- usr.sbin/snapinfo/snapinfo.c | 3 ++- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'usr.sbin') diff --git a/usr.bin/find/ls.c b/usr.bin/find/ls.c index 44d1852430e12..082fd64118459 100644 --- a/usr.bin/find/ls.c +++ b/usr.bin/find/ls.c @@ -63,7 +63,7 @@ printlong(char *name, char *accpath, struct stat *sb) { char modep[15]; - (void)printf("%6lu %8"PRId64" ", (u_long) sb->st_ino, sb->st_blocks); + (void)printf("%6ju %8"PRId64" ", (uintmax_t)sb->st_ino, sb->st_blocks); (void)strmode(sb->st_mode, modep); (void)printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, MAXLOGNAME - 1, user_from_uid(sb->st_uid, 0), MAXLOGNAME - 1, diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c index 9ae72f6110cc3..af7c807a1e084 100644 --- a/usr.sbin/lpr/lpr/lpr.c +++ b/usr.sbin/lpr/lpr/lpr.c @@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -386,8 +387,8 @@ main(int argc, char *argv[]) continue; /* file unreasonable */ if (sflag && (cp = linked(arg)) != NULL) { - (void) snprintf(buf, sizeof(buf), "%u %u", statb.st_dev, - statb.st_ino); + (void)snprintf(buf, sizeof(buf), "%u %ju", + statb.st_dev, (uintmax_t)statb.st_ino); card('S', buf); if (format == 'p') card('T', title ? title : arg); diff --git a/usr.sbin/makefs/ffs/ffs_alloc.c b/usr.sbin/makefs/ffs/ffs_alloc.c index f676a39af3c77..afab8699509b0 100644 --- a/usr.sbin/makefs/ffs/ffs_alloc.c +++ b/usr.sbin/makefs/ffs/ffs_alloc.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include "makefs.h" @@ -439,8 +440,8 @@ ffs_blkfree(struct inode *ip, daddr_t bno, long size) } cg = dtog(fs, bno); if (bno >= fs->fs_size) { - warnx("bad block %lld, ino %llu", (long long)bno, - (unsigned long long)ip->i_number); + warnx("bad block %lld, ino %ju", (long long)bno, + (uintmax_t)ip->i_number); return; } error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)), diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c index 979eef402d588..ac90da6a1cb58 100644 --- a/usr.sbin/quot/quot.c +++ b/usr.sbin/quot/quot.c @@ -484,8 +484,8 @@ static void donames(int fd, struct fs *super, char *name) { int c; - ino_t inode; ino_t maxino; + uintmax_t inode; union dinode *dp; maxino = super->fs_ncg * super->fs_ipg - 1; @@ -493,9 +493,9 @@ donames(int fd, struct fs *super, char *name) while ((c = getchar()) != EOF && (c < '0' || c > '9')) while ((c = getchar()) != EOF && c != '\n'); ungetc(c,stdin); - while (scanf("%u",&inode) == 1) { + while (scanf("%ju", &inode) == 1) { if (inode > maxino) { - warnx("illegal inode %d",inode); + warnx("illegal inode %ju", inode); return; } errno = 0; diff --git a/usr.sbin/snapinfo/snapinfo.c b/usr.sbin/snapinfo/snapinfo.c index 45882e094e764..23c3b4b2fe8e4 100644 --- a/usr.sbin/snapinfo/snapinfo.c +++ b/usr.sbin/snapinfo/snapinfo.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -149,7 +150,7 @@ compare_function(const char *path, const struct stat *st, int flags, printf("\tsnapshot "); printf("%s", path); if (verbose) - printf(" (inode %d)", st->st_ino); + printf(" (inode %ju)", (uintmax_t)st->st_ino); printf("\n"); if (!cont_search) return (EEXIST); -- cgit v1.3 From 9b0f0cc5bbe973cf3fc80ffcbfbcd87a0e5a284e Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 28 Sep 2012 01:39:25 +0000 Subject: Sanitize varname argument in f_sysrc_find. This is as much for security as it is for sanity. Reviewed by: jilles Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/share/sysrc.subr | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index dc231d4f71f96..f8824a86ee88a 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -49,6 +49,17 @@ f_include_lang $BSDCFG_LIBE/include/messages.subr SUCCESS=0 FAILURE=1 +# +# Valid characters that can appear in an sh(1) variable name +# +# Please note that the character ranges A-Z and a-z should be avoided because +# these can include accent characters (which are not valid in a variable name). +# For example, A-Z matches any character that sorts after A but before Z, +# including A and Z. Although ASCII order would make more sense, that is not +# how it works. +# +VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" + ############################################################ FUNCTIONS # f_clean_env [ --except $varname ... ] @@ -243,14 +254,16 @@ f_sysrc_get_default() # f_sysrc_find() { - local varname="$1" + local varname="${1%%[!$VALID_VARNAME_CHARS]*}" local regex="^[[:space:]]*$varname=" local rc_conf_files="$( f_sysrc_get rc_conf_files )" local conf_files= local file # Check parameters - [ "$varname" ] || return $FAILURE + case "$varname" in + ""|[0-9]*) return $FAILURE + esac # # If RC_CONFS is defined, set $rc_conf_files to an explicit -- cgit v1.3 From ba670ce0239ee18cc7bc30bd3119f0e82234a71a Mon Sep 17 00:00:00 2001 From: Kevin Lo Date: Fri, 28 Sep 2012 07:51:30 +0000 Subject: Make sure that each va_start has one and only one matching va_end, especially in error cases. --- cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c | 1 + cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c | 2 ++ contrib/telnet/telnetd/state.c | 4 +++- usr.bin/csup/proto.c | 12 +++++++++--- usr.sbin/pkg_install/lib/exec.c | 1 + usr.sbin/pw/pwupd.c | 1 + 6 files changed, 17 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c index 839ce0628aced..df34eece397fd 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c @@ -678,6 +678,7 @@ dt_printf(dtrace_hdl_t *dtp, FILE *fp, const char *format, ...) dtp->dt_buffered_offs += needed; assert(dtp->dt_buffered_buf[dtp->dt_buffered_offs] == '\0'); + va_end(ap); return (0); } diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c index 0db2dfd1b53da..2b802a529a70b 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c @@ -368,6 +368,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) case ENOSPC: case EDQUOT: zfs_verror(hdl, EZFS_NOSPC, fmt, ap); + va_end(ap); return (-1); case EEXIST: @@ -467,6 +468,7 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) case ENOSPC: case EDQUOT: zfs_verror(hdl, EZFS_NOSPC, fmt, ap); + va_end(ap); return (-1); case EAGAIN: diff --git a/contrib/telnet/telnetd/state.c b/contrib/telnet/telnetd/state.c index 300421f6dec43..9cad0c5aa0642 100644 --- a/contrib/telnet/telnetd/state.c +++ b/contrib/telnet/telnetd/state.c @@ -1600,8 +1600,10 @@ output_data(const char *format, ...) char *buf; va_start(args, format); - if ((len = vasprintf(&buf, format, args)) == -1) + if ((len = vasprintf(&buf, format, args)) == -1) { + va_end(args); return -1; + } output_datalen(buf, len); va_end(args); free(buf); diff --git a/usr.bin/csup/proto.c b/usr.bin/csup/proto.c index 145deb35d918b..28841ddf0c2e8 100644 --- a/usr.bin/csup/proto.c +++ b/usr.bin/csup/proto.c @@ -789,18 +789,24 @@ proto_printf(struct stream *wr, const char *format, ...) case '%': n = stream_write(wr, "%", 1); - if (n == -1) + if (n == -1) { + va_end(ap); return (-1); + } break; } - if (rv == -1) + if (rv == -1) { + va_end(ap); return (-1); + } fmt = cp + 1; } if (*fmt != '\0') { rv = stream_printf(wr, "%s", fmt); - if (rv == -1) + if (rv == -1) { + va_end(ap); return (-1); + } } done: va_end(ap); diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c index c95ac47fa9dcb..0804501182b68 100644 --- a/usr.sbin/pkg_install/lib/exec.c +++ b/usr.sbin/pkg_install/lib/exec.c @@ -93,6 +93,7 @@ vpipe(const char *fmt, ...) fp = popen(cmd, "r"); if (fp == NULL) { warnx("popen() failed"); + va_end(args); return NULL; } get_string(rp, MAXPATHLEN, fp); diff --git a/usr.sbin/pw/pwupd.c b/usr.sbin/pw/pwupd.c index cb8456d9e7c86..1e20cc4765a6d 100644 --- a/usr.sbin/pw/pwupd.c +++ b/usr.sbin/pw/pwupd.c @@ -102,6 +102,7 @@ pwdb(char *arg,...) if (WEXITSTATUS(i)) i = EIO; } + va_end(ap); return i; } -- cgit v1.3 From eacee75a76ddc1edecb4c2520c88ff91c0c27e93 Mon Sep 17 00:00:00 2001 From: Isabell Long Date: Fri, 28 Sep 2012 11:11:42 +0000 Subject: Add a note to portsnap(8) about the behaviour of the example cron command. PR: docs/171759 Submitted by: Paul Hoffman (phoffman at proper dot com) Approved by: cperciva, gabor (mentor) MFC after: 3 days --- usr.sbin/portsnap/portsnap/portsnap.8 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/portsnap/portsnap/portsnap.8 b/usr.sbin/portsnap/portsnap/portsnap.8 index f0b362803d85c..bb3daccc1d363 100644 --- a/usr.sbin/portsnap/portsnap/portsnap.8 +++ b/usr.sbin/portsnap/portsnap/portsnap.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 5, 2012 +.Dd September 28, 2012 .Dt PORTSNAP 8 .Os FreeBSD .Sh NAME @@ -180,6 +180,23 @@ can quickly be extracted into If your clock is set to UTC, please pick a random time other than 3AM, to avoid overly imposing an uneven load on the server(s) hosting the snapshots. +.Pp +Note that running +.Nm +.Cm cron +or +.Nm +.Cm fetch +does not apply the changes that were received: they only only download +them. +To apply the changes, you must follow these commands with +.Nm +.Cm update . +The +.Nm +.Cm update +command is normally run by hand at a time when you are sure that +no one is manually working in the ports tree. .It Running .Nm -- cgit v1.3 From 6e95460966bfdadfa65fb4a48a3ef92bb570ffa6 Mon Sep 17 00:00:00 2001 From: Isabell Long Date: Fri, 28 Sep 2012 11:40:59 +0000 Subject: Change "only only" to "only" in portsnap(8). Spotted by: Ruslan Mahmatkhanov Approved by: gabor (mentor, implicit) --- usr.sbin/portsnap/portsnap/portsnap.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/portsnap/portsnap/portsnap.8 b/usr.sbin/portsnap/portsnap/portsnap.8 index bb3daccc1d363..cd0ddf2dec145 100644 --- a/usr.sbin/portsnap/portsnap/portsnap.8 +++ b/usr.sbin/portsnap/portsnap/portsnap.8 @@ -187,7 +187,7 @@ Note that running or .Nm .Cm fetch -does not apply the changes that were received: they only only download +does not apply the changes that were received: they only download them. To apply the changes, you must follow these commands with .Nm -- cgit v1.3 From 1f843bc05983a51b6f19350bbc09b1f0b2b34312 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 29 Sep 2012 03:41:21 +0000 Subject: Allow deferred word-splitting via f_sysrc_get() by allowing $IFS in the "clean-room" environment used to query rc.conf(5) parameters. This brings bsdconfig(8)'s sysrc.subr in-line with both the sysrc(8) manual [provided by sysutils/sysrc] and sysrc(8)'s own sysrc.subr (now identical to bsdconfig(8)'s sysrc.subr as of this patch). Finally, this will allow a clean import of sysutils/sysrc (sans sysrc.subr, already provided here). Reviewed by: jilles Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/share/sysrc.subr | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index f8824a86ee88a..ca47067acbcc7 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -143,7 +143,7 @@ f_sysrc_get() # Clear the environment of all variables, preventing the # expansion of normals such as `PS1', `TERM', etc. # - f_clean_env --except RC_CONFS RC_DEFAULTS + f_clean_env --except IFS RC_CONFS RC_DEFAULTS . "$RC_DEFAULTS" > /dev/null 2>&1 @@ -451,8 +451,7 @@ f_sysrc_set() if [ "${RC_CONFS+set}" ]; then file="${RC_CONFS%%[$IFS]*}" else - file=$( f_sysrc_get rc_conf_files ) - file="${file%%[$IFS]*}" + file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' ) fi fi -- cgit v1.3 From 3afe9c0efee3474dac2811ae5c1a9bf088242a49 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Sun, 30 Sep 2012 03:21:21 +0000 Subject: Document the security.bsd.unprivileged_idprio in idprio(1) in addition to rtprio(2). Approved by: wblock MFC after: 3 days --- usr.sbin/rtprio/rtprio.1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/rtprio/rtprio.1 b/usr.sbin/rtprio/rtprio.1 index 567a1c437ee79..22d064513a827 100644 --- a/usr.sbin/rtprio/rtprio.1 +++ b/usr.sbin/rtprio/rtprio.1 @@ -30,7 +30,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 23, 1994 +.Dd September 29, 2012 .Dt RTPRIO 1 .Os .Sh NAME @@ -113,6 +113,14 @@ highest priority of 0 means "the current process". .Pp Only root is allowed to set realtime or idle priority for a process. +A user may modify the idle priority of their own processes if the +.Xr sysctl 8 +variable +.Va security.bsd.unprivileged_idprio +is set to non-zero. +Note that this increases the chance that a deadlock can occur +if a process locks a required resource and then does +not get to run. .Sh EXIT STATUS If .Nm -- cgit v1.3 From e6e9c14bdf48840c047ee51ea896e25ba162def3 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Mon, 1 Oct 2012 16:42:07 +0000 Subject: Remove useless atoi(3), previous strspn(3) makes sure that a_name->val is a number. This also allow pw user show to work as expected. PR: bin/172112 Submitted by: "Ilya A. Arkhipov" MFC after: 1 month --- usr.sbin/pw/pw_user.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c index b59789c9f5761..fd3136cdb709f 100644 --- a/usr.sbin/pw/pw_user.c +++ b/usr.sbin/pw/pw_user.c @@ -314,8 +314,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * know. */ if (mode != M_ADD && pwd == NULL - && strspn(a_name->val, "0123456789") == strlen(a_name->val) - && atoi(a_name->val) > 0) { /* Assume uid */ + && strspn(a_name->val, "0123456789") == strlen(a_name->val)) { (a_uid = a_name)->ch = 'u'; a_name = NULL; } -- cgit v1.3 From e7e5ec8773b3fbae5744fb9b50133ca0884a0f1f Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Mon, 1 Oct 2012 17:00:33 +0000 Subject: Do not treat empty name as an uid 0 Reported by: Robert Bonomi --- usr.sbin/pw/pw_user.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c index fd3136cdb709f..1b72cbdb70eef 100644 --- a/usr.sbin/pw/pw_user.c +++ b/usr.sbin/pw/pw_user.c @@ -314,7 +314,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * know. */ if (mode != M_ADD && pwd == NULL - && strspn(a_name->val, "0123456789") == strlen(a_name->val)) { + && strspn(a_name->val, "0123456789") == strlen(a_name->val) + && *a_name->val) { (a_uid = a_name)->ch = 'u'; a_name = NULL; } -- cgit v1.3 From 9281629f0e43aaf48a309266f4c118684e44921a Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 2 Oct 2012 09:19:28 +0000 Subject: Fix build with DEBUGGING disabled. --- usr.sbin/cron/cron/cron.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 52cdcc8612a80..8524ff9dfcb83 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -51,16 +51,20 @@ struct pidfh *pfh; static void usage() { +#if DEBUGGING char **dflags; +#endif fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " "[-m mailto] [-s] [-o] [-x debugflag[,...]]\n"); +#if DEBUGGING fprintf(stderr, "\ndebugflags: "); for(dflags = DebugFlagNames; *dflags; dflags++) { fprintf(stderr, "%s ", *dflags); } fprintf(stderr, "\n"); +#endif exit(ERROR_EXIT); } -- cgit v1.3 From 24e4c29c6e6c393f9c79ed2e5769a0ed841cca23 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 2 Oct 2012 09:23:16 +0000 Subject: Fix build with LOG_FILE undefined and DEBUGGING disabled. --- usr.sbin/cron/lib/misc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/lib/misc.c b/usr.sbin/cron/lib/misc.c index 5a08ad0b03596..afed07f3ee83b 100644 --- a/usr.sbin/cron/lib/misc.c +++ b/usr.sbin/cron/lib/misc.c @@ -391,7 +391,9 @@ log_it(username, xpid, event, detail) char *event; char *detail; { +#if defined(LOG_FILE) || DEBUGGING PID_T pid = xpid; +#endif #if defined(LOG_FILE) char *msg; TIME_T now = time((TIME_T) 0); -- cgit v1.3 From b94db03dca3630b03ff65e45d5f868f02388ac43 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 2 Oct 2012 09:53:50 +0000 Subject: mdoc: point to an existing cross reference. --- usr.sbin/fifolog/fifolog_create/fifolog.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/fifolog/fifolog_create/fifolog.1 b/usr.sbin/fifolog/fifolog_create/fifolog.1 index d0be0ce9bb3ac..69e8fbeebc2e8 100644 --- a/usr.sbin/fifolog/fifolog_create/fifolog.1 +++ b/usr.sbin/fifolog/fifolog_create/fifolog.1 @@ -203,7 +203,7 @@ One particular useful use of is with .Xr syslogd 8 using a line such as this in -.Xr /etc/syslog.conf 5 : +.Xr syslog.conf 5 : .Pp .Dl "*.* |fifolog_writer /var/log/syslog_fifolog" .Sh HISTORY -- cgit v1.3 From 4544286921adcf8c4dd39f19141091d6fafcefb8 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Tue, 2 Oct 2012 14:48:03 +0000 Subject: Fix alignment related warnings reported by gcc Switch from old style arguments to modern C remove WARNS as a result tested with "make universe" Approved by: cperciva MFC after: 1 week --- usr.sbin/ip6addrctl/Makefile | 2 -- usr.sbin/ip6addrctl/ip6addrctl.c | 44 ++++++++++++++++------------------------ 2 files changed, 17 insertions(+), 29 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ip6addrctl/Makefile b/usr.sbin/ip6addrctl/Makefile index 93a299056f994..cd2510eb0e19a 100644 --- a/usr.sbin/ip6addrctl/Makefile +++ b/usr.sbin/ip6addrctl/Makefile @@ -3,6 +3,4 @@ PROG= ip6addrctl MAN= ip6addrctl.8 -WARNS?= 2 - .include diff --git a/usr.sbin/ip6addrctl/ip6addrctl.c b/usr.sbin/ip6addrctl/ip6addrctl.c index 532a096497321..7514522b852b1 100644 --- a/usr.sbin/ip6addrctl/ip6addrctl.c +++ b/usr.sbin/ip6addrctl/ip6addrctl.c @@ -71,12 +71,10 @@ static void plen2mask(struct sockaddr_in6 *, int); static void set_policy(void); static void add_policy(char *, char *, char *); static void delete_policy(char *); -static void flush_policy(); +static void flush_policy(void); int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char *argv[]) { TAILQ_INIT(&policyhead); @@ -107,11 +105,11 @@ main(argc, argv) } static void -get_policy() +get_policy(void) { int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; size_t l; - char *buf; + struct in6_addrpolicy *buf; struct in6_addrpolicy *pol, *ep; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { @@ -131,8 +129,8 @@ get_policy() /* NOTREACHED */ } - ep = (struct in6_addrpolicy *)(buf + l); - for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { + ep = buf + l/sizeof(*buf); + for (pol = buf; pol + 1 <= ep; pol++) { struct policyqueue *new; if ((new = malloc(sizeof(*new))) == NULL) @@ -145,7 +143,7 @@ get_policy() } static void -dump_policy() +dump_policy(void) { size_t addrlen; char addrbuf[NI_MAXHOST]; @@ -201,8 +199,7 @@ dump_policy() } while (0); static void -make_policy_fromfile(conf) - char *conf; +make_policy_fromfile(char *conf) { char line[_POSIX2_LINE_MAX], *cp; char *addrstr; @@ -257,9 +254,7 @@ make_policy_fromfile(conf) } static int -parse_prefix(prefix0, pol) - const char *prefix0; - struct in6_addrpolicy *pol; +parse_prefix(const char *prefix0, struct in6_addrpolicy *pol) { int e = 0, plen; char *prefix, *plenstr; @@ -299,11 +294,9 @@ parse_prefix(prefix0, pol) } static void -plen2mask(mask, plen) - struct sockaddr_in6 *mask; - int plen; +plen2mask(struct sockaddr_in6 *mask, int plen) { - u_char *cp = (char *)&mask->sin6_addr; + u_char *cp = (unsigned char *)&mask->sin6_addr; memset(mask, 0, sizeof(*mask)); mask->sin6_family = AF_INET6; /* just in case */ @@ -316,7 +309,7 @@ plen2mask(mask, plen) } static void -set_policy() +set_policy(void) { struct policyqueue *ent; int s; @@ -334,8 +327,7 @@ set_policy() } static int -mask2plen(mask) - struct sockaddr_in6 *mask; +mask2plen(struct sockaddr_in6 *mask) { int masklen, final = 0; u_char *p, *lim; @@ -394,8 +386,7 @@ mask2plen(mask) } static void -add_policy(prefix, prec, label) - char *prefix, *prec, *label; +add_policy(char *prefix, char *prec, char *label) { struct in6_addrpolicy p; int s; @@ -416,8 +407,7 @@ add_policy(prefix, prec, label) } static void -delete_policy(prefix) - char *prefix; +delete_policy(char *prefix) { struct in6_addrpolicy p; int s; @@ -436,7 +426,7 @@ delete_policy(prefix) } static void -flush_policy() +flush_policy(void) { struct policyqueue *ent; int s; @@ -454,7 +444,7 @@ flush_policy() } static void -usage() +usage(void) { fprintf(stderr, "usage: ip6addrctl [show]\n"); fprintf(stderr, " ip6addrctl add " -- cgit v1.3 From 2a357efa9c63442476e9be4ef165c1785de50790 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Wed, 3 Oct 2012 02:32:47 +0000 Subject: Import sysutils/sysrc from the ports tree (current version 5.1). Importing disconnected under the WITH_BSDCONFIG flag (a good idea since this version of sysrc(8) indeed requires the `sysrc.subr' module installed by bsdconfig(8)). Multiple reasons sysrc should not simply continue to live in ports. The most important being that it is tightly coupled with the base. Approved by: adrian (co-mentor) --- usr.sbin/Makefile | 1 + usr.sbin/bsdconfig/share/sysrc.subr | 4 +- usr.sbin/sysrc/Makefile | 13 + usr.sbin/sysrc/sysrc | 618 ++++++++++++++++++++++++++++++++++++ usr.sbin/sysrc/sysrc.8 | 290 +++++++++++++++++ 5 files changed, 924 insertions(+), 2 deletions(-) create mode 100644 usr.sbin/sysrc/Makefile create mode 100644 usr.sbin/sysrc/sysrc create mode 100644 usr.sbin/sysrc/sysrc.8 (limited to 'usr.sbin') diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 19c19c9631c3f..a915cab60eb31 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -147,6 +147,7 @@ SUBDIR+= bluetooth .if ${MK_BSDCONFIG} != "no" SUBDIR+= bsdconfig +SUBDIR+= sysrc .endif .if ${MK_BSNMP} != "no" diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index ca47067acbcc7..1ee70e6bf4633 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -29,10 +29,10 @@ if [ ! "$_SYSRC_SUBR" ]; then _SYSRC_SUBR=1 ############################################################ INCLUDES BSDCFG_SHARE="/usr/share/bsdconfig" -. $BSDCFG_SHARE/common.subr || exit 1 +[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1 BSDCFG_LIBE="/usr/libexec/bsdconfig" -f_include_lang $BSDCFG_LIBE/include/messages.subr +[ ! "$_SYSRC_JAILED" ] && f_include_lang $BSDCFG_LIBE/include/messages.subr ############################################################ CONFIGURATION diff --git a/usr.sbin/sysrc/Makefile b/usr.sbin/sysrc/Makefile new file mode 100644 index 0000000000000..38c52ee96256b --- /dev/null +++ b/usr.sbin/sysrc/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +NO_OBJ= + +SCRIPTS= sysrc + +MAN= sysrc.8 + +beforeinstall: + mkdir -p ${DESTDIR}${SCRIPTSDIR} + mkdir -p ${DESTDIR}${MANDIR}8 + +.include diff --git a/usr.sbin/sysrc/sysrc b/usr.sbin/sysrc/sysrc new file mode 100644 index 0000000000000..24a304db36e58 --- /dev/null +++ b/usr.sbin/sysrc/sysrc @@ -0,0 +1,618 @@ +#!/bin/sh +#- +# Copyright (c) 2010-2012 Devin Teske +# 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. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1 +[ "$_SYSRC_SUBR" ] || f_include $BSDCFG_SHARE/sysrc.subr + +############################################################ CONFIGURATION + +# +# Default verbosity. +# +: ${SYSRC_VERBOSE:=} + +############################################################ GLOBALS + +# +# Options +# +DELETE= +DESCRIBE= +IGNORE_UNKNOWNS= +JAIL= +QUIET= +ROOTDIR= +SHOW_ALL= +SHOW_EQUALS= +SHOW_FILE= +SHOW_NAME=1 +SHOW_VALUE=1 + +############################################################ FUNCTIONS + +# die [ $fmt [ $opts ... ]] +# +# Optionally print a message to stderr before exiting with failure status. +# +die() +{ + local fmt="$1" + [ $# -gt 0 ] && shift 1 + [ "$fmt" ] && f_err "$fmt\n" "$@" + + exit $FAILURE +} + +# usage +# +# Prints a short syntax statement and exits. +# +usage() +{ + f_err "Usage: %s [OPTIONS] name[=value] ...\n" "$pgm" + f_err "Try \`%s --help' for more information.\n" "$pgm" + die +} + +# help +# +# Prints a full syntax statement and exits. +# +help() +{ + local optfmt="\t%-11s%s\n" + local envfmt="\t%-17s%s\n" + + f_err "Usage: %s [OPTIONS] name[=value] ...\n" "$pgm" + + f_err "OPTIONS:\n" + f_err "$optfmt" "-a" \ + "Dump a list of all non-default configuration variables." + f_err "$optfmt" "-A" \ + "Dump a list of all configuration variables (incl. defaults)." + f_err "$optfmt" "-d" \ + "Print a description of the given variable." + f_err "$optfmt" "-D" \ + "Show default value(s) only (this is the same as setting" + f_err "$optfmt" "" \ + "RC_CONFS to NULL or passing \`-f' with a NULL file-argument)." + f_err "$optfmt" "-e" \ + "Print query results as \`var=value' (useful for producing" + f_err "$optfmt" "" \ + "output to be fed back in). Ignored if \`-n' is specified." + f_err "$optfmt" "-f file" \ + "Operate on the specified file(s) instead of rc_conf_files." + f_err "$optfmt" "" \ + "Can be specified multiple times for additional files." + f_err "$optfmt" "-F" \ + "Show only the last rc.conf(5) file each directive is in." + f_err "$optfmt" "-h" \ + "Print a short usage statement to stderr and exit." + f_err "$optfmt" "--help" \ + "Print this message to stderr and exit." + f_err "$optfmt" "-i" \ + "Ignore unknown variables." + f_err "$optfmt" "-j jail" \ + "The jid or name of the jail to operate within (overrides" + f_err "$optfmt" "" \ + "\`-R dir'; requires jexec(8))." + f_err "$optfmt" "-n" \ + "Show only variable values, not their names." + f_err "$optfmt" "-N" \ + "Show only variable names, not their values." + f_err "$optfmt" "-q" \ + "Quiet. Ignore previous \`-v' and/or SYSRC_VERBOSE." + f_err "$optfmt" "-R dir" \ + "Operate within the root directory \`dir' rather than \`/'." + f_err "$optfmt" "-v" \ + "Verbose. Print the pathname of the specific rc.conf(5)" + f_err "$optfmt" "" \ + "file where the directive was found." + f_err "$optfmt" "-x" \ + "Remove variable(s) from specified file(s)." + f_err "\n" + + f_err "ENVIRONMENT:\n" + f_err "$envfmt" "RC_CONFS" \ + "Override default rc_conf_files (even if set to NULL)." + f_err "$envfmt" "RC_DEFAULTS" \ + "Location of \`/etc/defaults/rc.conf' file." + f_err "$envfmt" "SYSRC_VERBOSE" \ + "Default verbosity. Set to non-NULL to enable." + + die +} + +# jail_depend +# +# Dump dependencies such as language-file variables and include files to stdout +# to be piped-into sh(1) running via jexec(8)/chroot(8). As a security measure, +# this prevents existing language files and library files from being loaded in +# the jail. This also relaxes the requirement to have these files in every jail +# before sysrc can be used on said jail. +# +jail_depend() +{ + # + # Indicate that we are jailed + # + echo export _SYSRC_JAILED=1 + + # + # Print i18n language variables (their current values are sanitized + # and re-printed for interpretation so that the i18n language files + # do not need to exist within the jail). + # + local var val + for var in \ + msg_cannot_create_permission_denied \ + msg_permission_denied \ + msg_previous_syntax_errors \ + ; do + val=$( eval echo \"\$$var\" | + awk '{ gsub(/'\''/, "'\''\\'\'\''"); print }' ) + echo $var="'$val'" + done + + # + # Print include dependencies + # + cat $BSDCFG_SHARE/common.subr + cat $BSDCFG_SHARE/sysrc.subr +} + +############################################################ MAIN SOURCE + +# +# Perform sanity checks +# +[ $# -gt 0 ] || usage + +# +# Check for `--help' command-line option +# +( # Operate in sub-shell to protect $@ in parent + while [ $# -gt 0 ]; do + case "$1" in + --help) exit 1;; + -[fRj]) # These flags take an argument + shift 1;; + esac + shift 1 + done + exit 0 +) || help + +# +# Process command-line flags +# +while getopts aAdDef:Fhij:nNqR:vxX flag; do + case "$flag" in + a) SHOW_ALL=${SHOW_ALL:-1};; + A) SHOW_ALL=2;; + d) DESCRIBE=1;; + D) RC_CONFS=;; + e) SHOW_EQUALS=1;; + f) RC_CONFS="$RC_CONFS${RC_CONFS:+ }$OPTARG";; + F) SHOW_FILE=1;; + h) usage;; + i) IGNORE_UNKNOWNS=1;; + j) [ "$OPTARG" ] || die \ + "%s: Missing or null argument to \`-j' flag" "$pgm" + JAIL="$OPTARG";; + n) SHOW_NAME=;; + N) SHOW_VALUE=;; + q) QUIET=1 SYSRC_VERBOSE=;; + R) [ "$OPTARG" ] || die \ + "%s: Missing or null argument to \`-R' flag" "$pgm" + ROOTDIR="$OPTARG";; + v) SYSRC_VERBOSE=1 QUIET=;; + x) DELETE=${DELETE:-1};; + X) DELETE=2;; + \?) usage;; + esac +done +shift $(( $OPTIND - 1 )) + +# +# [More] Sanity checks (e.g., "sysrc --") +# +[ $# -eq 0 -a ! "$SHOW_ALL" ] && usage + +# +# Taint-check all rc.conf(5) files +# +errmsg="$pgm: Exiting due to previous syntax errors" +if ( : ${RC_CONFS?} ) > /dev/null 2>&1; then + ( for i in $RC_CONFS; do + [ -e "$i" ] || continue + /bin/sh -n "$i" || exit $FAILURE + done + exit $SUCCESS + ) || die "$errmsg" +else + /bin/sh -n "$RC_DEFAULTS" || die "$errmsg" + ( . "$RC_DEFAULTS" + for i in $rc_conf_files; do + [ -e "$i" ] || continue + /bin/sh -n "$i" || exit $FAILURE + done + exit $SUCCESS + ) || die "$errmsg" +fi + +# +# Process `-x' (and secret `-X') command-line options +# +errmsg="$pgm: \`-x' option incompatible with \`-a'/\`-A' options" +errmsg="$errmsg (use \`-X' to override)" +if [ "$DELETE" -a "$SHOW_ALL" ]; then + [ "$DELETE" = "2" ] || die "$errmsg" +fi + +# +# Process `-e', `-n', and `-N' command-line options +# +SEP=': ' +[ "$SHOW_EQUALS" ] && SEP='="' +[ "$SHOW_NAME" ] || SHOW_EQUALS= +[ "$SYSRC_VERBOSE" = "0" ] && SYSRC_VERBOSE= +if [ ! "$SHOW_VALUE" ]; then + SHOW_NAME=1 + SHOW_EQUALS= +fi + +# +# Process `-j jail' and `-R dir' command-line options +# +if [ "$JAIL" -o "$ROOTDIR" ]; then + # + # Reconstruct the arguments that we want to carry-over + # + args=" + ${SYSRC_VERBOSE:+-v} + ${QUIET:+-q} + $( [ "$DELETE" = "1" ] && echo \ -x ) + $( [ "$DELETE" = "2" ] && echo \ -X ) + $( [ "$SHOW_ALL" = "1" ] && echo \ -a ) + $( [ "$SHOW_ALL" = "2" ] && echo \ -A ) + ${DESCRIBE:+-d} + ${SHOW_EQUALS:+-e} + ${IGNORE_UNKNOWNS:+-i} + $( [ "$SHOW_NAME" ] || echo \ -n ) + $( [ "$SHOW_VALUE" ] || echo \ -N ) + $( [ "$SHOW_FILE" ] && echo \ -F ) + " + if ( : ${RC_CONFS?} ) > /dev/null 2>&1; then + args="$args -f '$RC_CONFS'" + fi + for arg in "$@"; do + args="$args '$arg'" + done + + # + # If both are supplied, `-j jail' supercedes `-R dir' + # + if [ "$JAIL" ]; then + # + # Re-execute ourselves with sh(1) via jexec(8) + # + ( echo set -- $args + jail_depend + cat $0 + ) | env - RC_DEFAULTS="$RC_DEFAULTS" \ + /usr/sbin/jexec "$JAIL" /bin/sh + exit $? + elif [ "$ROOTDIR" ]; then + # + # Make sure that the root directory specified is not to any + # running jails. + # + # NOTE: To maintain backward compatibility with older jails on + # older systems, we will not perform this check if either the + # jls(1) or jexec(8) utilities are missing. + # + if f_have jexec && f_have jls; then + jid="`jls jid path | \ + ( + while read JID JROOT; do + [ "$JROOT" = "$ROOTDIR" ] || continue + echo $JID + done + )`" + + # + # If multiple running jails match the specified root + # directory, exit with error. + # + if [ "$jid" -a "${jid%[$IFS]*}" != "$jid" ]; then + die "%s: %s: %s" "$pgm" "$ROOTDIR" \ + "$( echo "Multiple jails claim this" \ + "directory as their root." \ + "(use \`-j jail' instead)" )" + fi + + # + # If only a single running jail matches the specified + # root directory, implicitly use `-j jail'. + # + if [ "$jid" ]; then + # + # Re-execute outselves with sh(1) via jexec(8) + # + ( echo set -- $args + jail_depend + cat $0 + ) | env - RC_DEFAULTS="$RC_DEFAULTS" \ + /usr/sbin/jexec "$jid" /bin/sh + exit $? + fi + + # Otherwise, fall through and allow chroot(8) + fi + + # + # Re-execute ourselves with sh(1) via chroot(8) + # + ( echo set -- $args + jail_depend + cat $0 + ) | env - RC_DEFAULTS="$RC_DEFAULTS" \ + /usr/sbin/chroot "$ROOTDIR" /bin/sh + exit $? + fi +fi + +# +# Process `-a' or `-A' command-line options +# +if [ "$SHOW_ALL" ]; then + # + # Get a list of variables that are currently set in the rc.conf(5) + # files (included `/etc/defaults/rc.conf') by performing a call to + # source_rc_confs() in a clean environment. + # + ( # Operate in a sub-shell to protect the parent environment + # + # Set which variables we want to preserve in the environment. + # Append the pipe-character (|) to the list of internal field + # separation (IFS) characters, allowing us to use the below + # list both as an extended grep (-E) pattern and argument list + # (required to first get f_clean_env() to preserve these in the + # environment and then later to prune them from the list of + # variables produced by set(1)). + # + IFS="$IFS|" + EXCEPT="IFS|EXCEPT|PATH|RC_DEFAULTS|OPTIND|DESCRIBE|SEP" + EXCEPT="$EXCEPT|DELETE|SHOW_ALL|SHOW_EQUALS|SHOW_NAME" + EXCEPT="$EXCEPT|SHOW_VALUE|SHOW_FILE|SYSRC_VERBOSE|RC_CONFS" + EXCEPT="$EXCEPT|pgm|SUCCESS|FAILURE" + EXCEPT="$EXCEPT|f_sysrc_desc_awk|f_sysrc_delete_awk" + + # + # Clean the environment (except for our required variables) + # and then source the required files. + # + f_clean_env --except $EXCEPT + if [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ]; then + . "$RC_DEFAULTS" + + # + # If passed `-a' (rather than `-A'), re-purge the + # environment, removing the rc.conf(5) defaults. + # + [ "$SHOW_ALL" = "1" ] \ + && f_clean_env --except rc_conf_files $EXCEPT + + # + # If `-f file' was passed, set $rc_conf_files to an + # explicit value, modifying the default behavior of + # source_rc_confs(). + # + ( : ${RC_CONFS?} ) > /dev/null 2>&1 && + rc_conf_files="$RC_CONFS" + + source_rc_confs + + # + # If passed `-a' (rather than `-A'), remove + # `rc_conf_files' unless it was defined somewhere + # other than rc.conf(5) defaults. + # + [ "$SHOW_ALL" = "1" -a \ + "$( f_sysrc_find rc_conf_files )" = "$RC_DEFAULTS" \ + ] \ + && unset rc_conf_files + fi + + for NAME in $( set | + awk -F= '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' | + grep -Ev "^($EXCEPT)$" + ); do + # + # If enabled, describe rather than expand value + # + if [ "$DESCRIBE" ]; then + echo "$NAME: $( f_sysrc_desc "$NAME" )" + continue + fi + + # + # If `-F' is passed, find it and move on + # + if [ "$SHOW_FILE" ]; then + [ "$SHOW_NAME" ] && echo -n "$NAME: " + f_sysrc_find "$NAME" + continue + fi + + # + # If `-X' is passed, delete the variables + # + if [ "$DELETE" = "2" ]; then + f_sysrc_delete "$NAME" + continue + fi + + [ "$SYSRC_VERBOSE" ] && \ + echo -n "$( f_sysrc_find "$NAME" ): " + + # + # If `-N' is passed, simplify the output + # + if [ ! "$SHOW_VALUE" ]; then + echo "$NAME" + continue + fi + + echo "${SHOW_NAME:+$NAME$SEP}$( + f_sysrc_get "$NAME" )${SHOW_EQUALS:+\"}" + + done + ) + + # + # Ignore the remainder of positional arguments. + # + exit $SUCCESS +fi + +# +# Process command-line arguments +# +while [ $# -gt 0 ]; do + NAME="${1%%=*}" + + [ "$DESCRIBE" ] && \ + echo "$NAME: $( f_sysrc_desc "$NAME" )" + + case "$1" in + *=*) + # + # Like sysctl(8), if both `-d' AND "name=value" is passed, + # first describe, then attempt to set + # + + if [ "$SYSRC_VERBOSE" ]; then + file=$( f_sysrc_find "$NAME" ) + [ "$file" = "$RC_DEFAULTS" -o ! "$file" ] && \ + file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' ) + echo -n "$file: " + fi + + # + # If `-x' or `-X' is passed, delete the variable and ignore the + # desire to set some value + # + if [ "$DELETE" ]; then + f_sysrc_delete "$NAME" + shift 1 + continue + fi + + # + # If `-N' is passed, simplify the output + # + if [ ! "$SHOW_VALUE" ]; then + echo "$NAME" + f_sysrc_set "$NAME" "${1#*}" + else + if [ "$SHOW_FILE" ]; then + before=$( f_sysrc_find "$NAME" ) + else + before=$( f_sysrc_get "$NAME" ) + fi + if f_sysrc_set "$NAME" "${1#*=}"; then + if [ "$SHOW_FILE" ]; then + after=$( f_sysrc_find "$NAME" ) + echo -n "${SHOW_NAME:+$NAME$SEP}" + echo -n "$before${SHOW_EQUALS:+\"}" + echo " -> $after" + else + after=$( f_sysrc_get "$NAME" ) + echo -n "${SHOW_NAME:+$NAME$SEP}" + echo "$before -> $after" + fi + fi + fi + ;; + *) + if ! IGNORED="$( f_sysrc_get "$NAME?" )"; then + [ "$IGNORE_UNKNOWNS" ] \ + || echo "$pgm: unknown variable '$NAME'" + shift 1 + continue + fi + + # + # Like sysctl(8), when `-d' is passed, + # desribe it rather than expanding it + # + + if [ "$DESCRIBE" ]; then + shift 1 + continue + fi + + # + # If `-x' or `-X' is passed, delete the variable + # + if [ "$DELETE" ]; then + f_sysrc_delete "$NAME" + shift 1 + continue + fi + + # + # If `-F' is passed, find it and move on + # + if [ "$SHOW_FILE" ]; then + [ "$SHOW_NAME" ] && echo -n "$NAME: " + f_sysrc_find "$NAME" + shift 1 + continue + fi + + [ "$SYSRC_VERBOSE" ] && \ + echo -n "$( f_sysrc_find "$NAME" ): " + + # + # If `-N' is passed, simplify the output + # + if [ ! "$SHOW_VALUE" ]; then + echo "$NAME" + else + echo "${SHOW_NAME:+$NAME$SEP}$( + f_sysrc_get "$NAME" )${SHOW_EQUALS:+\"}" + fi + esac + shift 1 +done diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 new file mode 100644 index 0000000000000..19ffe935d16cd --- /dev/null +++ b/usr.sbin/sysrc/sysrc.8 @@ -0,0 +1,290 @@ +.\" Copyright (c) 2011-2012 Devin Teske +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Aug 24, 2012 +.Dt SYSRC 8 +.Os +.Sh NAME +.Nm sysrc +.Nd safely edit system rc files +.Sh SYNOPSIS +.Nm +.Op Fl dDeFhinNqvx +.Op Fl f Ar file +.Op Fl j Ar jail | Fl R Ar dir +.Ar name Ns Op = Ns Ar value +.Ar ... +.Nm +.Op Fl dDeFhinNqvx +.Op Fl f Ar file +.Op Fl j Ar jail | Fl R Ar dir +.Fl a | A +.Sh DESCRIPTION +The +.Nm +utility retrieves +.Xr rc.conf 5 +variables from the collection of system rc files and allows processes with +appropriate privilege to change values in a safe and effective manner. +.Pp +The following options are available: +.Bl -tag -width indent+ +.It Fl a +Dump a list of all non-default configuration variables. +.It Fl A +Dump a list of all configuration variables +.Pq incl. defaults . +.It Fl d +Print a description of the given variable. +.It Fl D +Show default value(s) only (this is the same as setting RC_CONFS to NULL or +passing `-f' with a NULL file-argument). +.It Fl e +Print query results as +.Ql var=value +.Pq useful for producing output to be fed back in . +Ignored if +.Fl n +is specified. +.It Fl f Ar file +Operate on the specified file(s) instead of the files obtained by reading the +.Sq rc_conf_files +entry in the +.Ev RC_DEFAULTS +file. +This option can be specified multiple times for additional files. +.It Fl F +Show only the last +.Xr rc.conf 5 +file each directive is in. +.It Fl h +Print a short usage message to stderr and exit. +.It Fl -help +Print a full usage statement to stderr and exit. +.It Fl i +Ignore unknown variables. +.It Fl j Ar jail +The +.Ar jid +or name of the +.Ar jail +to operate within +.Pq overrides So Fl R Ar dir Sc ; requires Xr jexec 8 . +.It Fl n +Show only variable values, not their names. +.It Fl N +Show only variable names, not their values. +.It Fl q +Quiet. Ignore previous +.Fl v +and/or +.Ev SYSRC_VERBOSE . +.It Fl R Ar dir +Operate within the root directory +.Pq Sq Ar dir +rather than +.Pq Sq / . +.It Fl v +Verbose. Print the pathname of the specific +.Xr rc.conf 5 +file where the directive was found. +.It Fl x +Remove variable(s) from specified file(s). +.El +.Pp +This utility works similar to +.Xr sysctl 8 . +It shares the `-e' and `-n' options +.Pq detailed above +and also has the same +.Ql name[=value] +syntax for querying/setting configuration options. +.Pp +However, while +.Xr sysctl 8 +serves to query/modify MIBs in the entrant kernel, +.Xr sysrc 8 +instead works on values in the system +.Xr rc.conf 5 +configuration files. +.Pp +The list of system configuration files is configured in the file +.Ql /etc/defaults/rc.conf +within the variable +.Ql rc_conf_files , +which by-default contains a space-separated list of pathnames. On all FreeBSD +systems, this defaults to the value "/etc/rc.conf /etc/rc.conf.local". Each +pathname is sourced in-order upon startup. It is in the same fashion that +.Nm +sources the configuration files before returning the value of the given +variable. +.Pp +When supplied a variable name, +.Nm +will return the value of the variable. If the variable does not appear in any +of the configured +.Ql rc_conf_files , +an error is printed and error status is returned. +.Pp +When changing values of a given variable, it does not matter if the variable +appears in any of the +.Ql rc_conf_files +or not. If the variable does not appear in any of the files, it is appended to +the end of the first pathname in the +.Ql rc_conf_files +variable. Otherwise, +.Nm +will replace only the last-occurrence in the last-file found to contain the +variable. This gets the value to take effect next boot without heavily +modifying these integral files (yet taking care not to allow the file to +grow unwieldy should +.Nm +be called repeatedly). +.Sh ENVIRONMENT +The following environment variables are referenced by +.Nm : +.Bl -tag -width ".Ev SYSRC_VERBOSE" +.It Ev RC_CONFS +Override default +.Ql rc_conf_files +.Pq even if set to NULL . +.It Ev RC_DEFAULTS +Location of +.Ql /etc/defaults/rc.conf +file. +.It Ev SYSRC_VERBOSE +Default verbosity. Set to non-NULL to enable. +.El +.Sh DEPENDENCIES +The following standard commands are required by +.Nm : +.Pp +.Xr awk 1 , +.Xr cat 1 , +.Xr chmod 1 , +.Xr chown 8 , +.Xr env 1 , +.Xr grep 1 , +.Xr jexec 8 , +.Xr jls 1 , +.Xr mktemp 1 , +.Xr mv 1 , +.Xr rm 1 , +.Xr sh 1 , +.Xr stat 1 , +.Xr tail 1 +.Sh FILES +.Bl -tag -width ".Pa /etc/defaults/rc.conf" -compact +.It Pa /etc/defaults/rc.conf +.It Pa /etc/rc.conf +.It Pa /etc/rc.conf.local +.El +.Sh EXAMPLES +Below are some simple examples of how +.Nm +can be used to query certain values from the +.Xr rc.conf 5 +collection of system configuration files: +.Pp +.Nm +sshd_enable +.Dl returns the value of $sshd_enable, usually YES or NO . +.Pp +.Nm +defaultrouter +.Dl returns IP address of default router Pq if configured . +.Pp +Working on other files, such as +.Xr crontab 5 : +.Pp +.Nm +-f /etc/crontab MAILTO +.Dl returns the value of the MAILTO setting Pq if configured . +.Pp +In addition to the above syntax, +.Nm +also supports inline +.Xr sh 1 +PARAMETER expansion for changing the way values are reported, shown below: +.Pp +.Nm +\&'hostname%%.*' +.Dl returns $hostname up to (but not including) first `.' . +.Pp +.Nm +\&'network_interfaces%%[$IFS]*' +.Dl returns first word of $network_interfaces . +.Pp +.Nm +\&'ntpdate_flags##*[$IFS]' +.Dl returns last word of $ntpdate_flags (time server address) . +.Pp +.Nm +usbd_flags-"default" +.Dl returns $usbd_flags or "default" if unset or NULL . +.Pp +.Nm +cloned_interfaces+"alternate" +.Dl returns "alternate" if $cloned_interfaces is set . +.Pp +.Nm +\&'#kern_securelevel' +.Dl returns length in characters of $kern_securelevel . +.Pp +.Nm +\&'hostname?' +.Dl returns NULL and error status 2 if $hostname is unset Pq or if set, returns the value of $hostname with no error status . +.Pp +.Nm +\&'hostname:?' +.Dl returns NULL and error status 2 if $hostname is unset or NULL Pq or if set and non-NULL, returns value without error status . +.Sh LIMITATIONS +The +.Nm +utility presently does not support the +.Ql rc.conf.d +collection of system configuration files +.Pq which requires a service name to be known during execution . +.Pp +This will be corrected by a future enhancement. +.Sh SEE ALSO +.Xr jail 8 , +.Xr jexec 8 , +.Xr jls 1 , +.Xr rc 8 , +.Xr rc.conf 5 , +.Xr sysctl 8 +.Sh HISTORY +A +.Nm +utility first appeared in +.Bx 10.0 . +.Sh AUTHORS +.An Devin Teske Aq dteske@FreeBSD.org +.Sh THANKS TO +Brandon Gooch, Garrett Cooper, Julian Elischer, Pawel Jakub Dawidek, +Cyrille Lefevre, Ross West, Stefan Esser, Marco Steinbach, and Jilles Tjoelker +for suggestions and help. -- cgit v1.3 From f41e2a4d49fd27d2976d1459787ac0e6870f528c Mon Sep 17 00:00:00 2001 From: Niclas Zeising Date: Wed, 3 Oct 2012 21:50:53 +0000 Subject: Remove whitespace at EOL Approved by: joel (mentor) --- usr.sbin/sysrc/sysrc.8 | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 index 19ffe935d16cd..9fc930678f60d 100644 --- a/usr.sbin/sysrc/sysrc.8 +++ b/usr.sbin/sysrc/sysrc.8 @@ -55,7 +55,7 @@ The following options are available: .It Fl a Dump a list of all non-default configuration variables. .It Fl A -Dump a list of all configuration variables +Dump a list of all configuration variables .Pq incl. defaults . .It Fl d Print a description of the given variable. @@ -66,14 +66,14 @@ passing `-f' with a NULL file-argument). Print query results as .Ql var=value .Pq useful for producing output to be fed back in . -Ignored if +Ignored if .Fl n is specified. .It Fl f Ar file -Operate on the specified file(s) instead of the files obtained by reading the -.Sq rc_conf_files -entry in the -.Ev RC_DEFAULTS +Operate on the specified file(s) instead of the files obtained by reading the +.Sq rc_conf_files +entry in the +.Ev RC_DEFAULTS file. This option can be specified multiple times for additional files. .It Fl F @@ -89,9 +89,9 @@ Ignore unknown variables. .It Fl j Ar jail The .Ar jid -or name of the -.Ar jail -to operate within +or name of the +.Ar jail +to operate within .Pq overrides So Fl R Ar dir Sc ; requires Xr jexec 8 . .It Fl n Show only variable values, not their names. @@ -103,13 +103,13 @@ Quiet. Ignore previous and/or .Ev SYSRC_VERBOSE . .It Fl R Ar dir -Operate within the root directory +Operate within the root directory .Pq Sq Ar dir -rather than +rather than .Pq Sq / . .It Fl v -Verbose. Print the pathname of the specific -.Xr rc.conf 5 +Verbose. Print the pathname of the specific +.Xr rc.conf 5 file where the directive was found. .It Fl x Remove variable(s) from specified file(s). @@ -133,7 +133,7 @@ configuration files. .Pp The list of system configuration files is configured in the file .Ql /etc/defaults/rc.conf -within the variable +within the variable .Ql rc_conf_files , which by-default contains a space-separated list of pathnames. On all FreeBSD systems, this defaults to the value "/etc/rc.conf /etc/rc.conf.local". Each @@ -172,14 +172,14 @@ Override default .Ql rc_conf_files .Pq even if set to NULL . .It Ev RC_DEFAULTS -Location of +Location of .Ql /etc/defaults/rc.conf file. .It Ev SYSRC_VERBOSE Default verbosity. Set to non-NULL to enable. .El .Sh DEPENDENCIES -The following standard commands are required by +The following standard commands are required by .Nm : .Pp .Xr awk 1 , -- cgit v1.3 From 872d9e390677102fcee7718d804b851634c19acd Mon Sep 17 00:00:00 2001 From: Niclas Zeising Date: Wed, 3 Oct 2012 21:56:16 +0000 Subject: Sort manual page cross references. Approved by: joel (mentor) --- usr.sbin/sysrc/sysrc.8 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 index 9fc930678f60d..c35adad141aca 100644 --- a/usr.sbin/sysrc/sysrc.8 +++ b/usr.sbin/sysrc/sysrc.8 @@ -185,17 +185,18 @@ The following standard commands are required by .Xr awk 1 , .Xr cat 1 , .Xr chmod 1 , -.Xr chown 8 , .Xr env 1 , .Xr grep 1 , -.Xr jexec 8 , .Xr jls 1 , .Xr mktemp 1 , .Xr mv 1 , .Xr rm 1 , .Xr sh 1 , .Xr stat 1 , -.Xr tail 1 +.Xr tail 1 , +.Xr chown 8 +and +.Xr jexec 8 . .Sh FILES .Bl -tag -width ".Pa /etc/defaults/rc.conf" -compact .It Pa /etc/defaults/rc.conf @@ -271,11 +272,11 @@ collection of system configuration files .Pp This will be corrected by a future enhancement. .Sh SEE ALSO +.Xr jls 1 , +.Xr rc.conf 5 , .Xr jail 8 , .Xr jexec 8 , -.Xr jls 1 , .Xr rc 8 , -.Xr rc.conf 5 , .Xr sysctl 8 .Sh HISTORY A -- cgit v1.3 From 50936af6e148edc420bc831f7a0be27df0631f28 Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Thu, 4 Oct 2012 06:33:03 +0000 Subject: Fix HISTORY. --- usr.sbin/sysrc/sysrc.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 index c35adad141aca..abc543055ed2c 100644 --- a/usr.sbin/sysrc/sysrc.8 +++ b/usr.sbin/sysrc/sysrc.8 @@ -282,7 +282,7 @@ This will be corrected by a future enhancement. A .Nm utility first appeared in -.Bx 10.0 . +.Fx 10.0 . .Sh AUTHORS .An Devin Teske Aq dteske@FreeBSD.org .Sh THANKS TO -- cgit v1.3 From 01591cd5409bc8eb5c2c077e83955dbc1fbd2a85 Mon Sep 17 00:00:00 2001 From: Jamie Gritton Date: Thu, 4 Oct 2012 18:59:46 +0000 Subject: Move properly to the next parameter when jailparam_init fails (i.e. on an unknown parameter), to avoid freeing bogus pointers. --- usr.sbin/jail/config.c | 1 + 1 file changed, 1 insertion(+) (limited to 'usr.sbin') diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c index ec96c0c95e909..d0b7d266b518e 100644 --- a/usr.sbin/jail/config.c +++ b/usr.sbin/jail/config.c @@ -690,6 +690,7 @@ import_params(struct cfjail *j) if (jailparam_init(jp, p->name) < 0) { error = -1; jail_warnx(j, "%s", jail_errmsg); + jp++; continue; } if (TAILQ_EMPTY(&p->val)) -- cgit v1.3 From 338666582fa4c4cf556e45ac27d74c0cffb099d2 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Thu, 4 Oct 2012 20:00:32 +0000 Subject: Display the matrix of inter-domain distances in the SLIT table. This is used to complement the SRAT table on NUMA machines. MFC after: 1 week --- usr.sbin/acpi/acpidump/acpi.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/acpi/acpidump/acpi.c b/usr.sbin/acpi/acpidump/acpi.c index 8a05ef1e73262..85e5e0e88bbd0 100644 --- a/usr.sbin/acpi/acpidump/acpi.c +++ b/usr.sbin/acpi/acpidump/acpi.c @@ -63,6 +63,7 @@ static void acpi_handle_madt(ACPI_TABLE_HEADER *sdp); static void acpi_handle_ecdt(ACPI_TABLE_HEADER *sdp); static void acpi_handle_hpet(ACPI_TABLE_HEADER *sdp); static void acpi_handle_mcfg(ACPI_TABLE_HEADER *sdp); +static void acpi_handle_slit(ACPI_TABLE_HEADER *sdp); static void acpi_print_srat_cpu(uint32_t apic_id, uint32_t proximity_domain, uint32_t flags); static void acpi_print_srat_memory(ACPI_SRAT_MEM_AFFINITY *mp); @@ -518,6 +519,33 @@ acpi_handle_mcfg(ACPI_TABLE_HEADER *sdp) printf(END_COMMENT); } +static void +acpi_handle_slit(ACPI_TABLE_HEADER *sdp) +{ + ACPI_TABLE_SLIT *slit; + UINT64 i, j; + + printf(BEGIN_COMMENT); + acpi_print_sdt(sdp); + slit = (ACPI_TABLE_SLIT *)sdp; + printf("\tLocality Count=%jd\n", slit->LocalityCount); + printf("\n\t "); + for (i = 0; i < slit->LocalityCount; i++) + printf(" %3jd", i); + printf("\n\t +"); + for (i = 0; i < slit->LocalityCount; i++) + printf("----"); + printf("\n"); + for (i = 0; i < slit->LocalityCount; i++) { + printf("\t %3jd |", i); + for (j = 0; j < slit->LocalityCount; j++) + printf(" %3d", + slit->Entry[i * slit->LocalityCount + j]); + printf("\n"); + } + printf(END_COMMENT); +} + static void acpi_print_srat_cpu(uint32_t apic_id, uint32_t proximity_domain, uint32_t flags) @@ -1092,6 +1120,8 @@ acpi_handle_rsdt(ACPI_TABLE_HEADER *rsdp) acpi_handle_ecdt(sdp); else if (!memcmp(sdp->Signature, ACPI_SIG_MCFG, 4)) acpi_handle_mcfg(sdp); + else if (!memcmp(sdp->Signature, ACPI_SIG_SLIT, 4)) + acpi_handle_slit(sdp); else if (!memcmp(sdp->Signature, ACPI_SIG_SRAT, 4)) acpi_handle_srat(sdp); else if (!memcmp(sdp->Signature, ACPI_SIG_TCPA, 4)) -- cgit v1.3 From 3aec794146b86a9e7190e71c96cecc66d018bf92 Mon Sep 17 00:00:00 2001 From: Doug Barton Date: Fri, 5 Oct 2012 05:01:42 +0000 Subject: Remove references to CVS so that people will stop bringing it up For -p: The localtime update should have been excluded in the first place The make.conf comparison has been OBE for some time now, and there is no src.conf equivalent to share/examples/make.conf, so remove the whole thing. Update copyright --- usr.sbin/mergemaster/mergemaster.sh | 68 ++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 31 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/mergemaster/mergemaster.sh b/usr.sbin/mergemaster/mergemaster.sh index eb10bbaeb2886..1723fee24e1af 100755 --- a/usr.sbin/mergemaster/mergemaster.sh +++ b/usr.sbin/mergemaster/mergemaster.sh @@ -5,8 +5,8 @@ # Compare files created by /usr/src/etc/Makefile (or the directory # the user specifies) with the currently installed copies. -# Copyright 1998-2011 Douglas Barton -# dougb@FreeBSD.org +# Copyright (c) 1998-2012 Douglas Barton, All rights reserved +# Please see detailed copyright below # $FreeBSD$ @@ -532,9 +532,9 @@ if [ -t 0 ]; then esac fi -# Define what CVS $Id tag to look for to aid portability. +# Define what $Id tag to look for to aid portability. # -CVS_ID_TAG=FreeBSD +ID_TAG=FreeBSD delete_temproot () { rm -rf "${TEMPROOT}" 2>/dev/null @@ -1095,17 +1095,17 @@ for COMPFILE in `find . -type f | sort`; do case "${STRICT}" in '' | [Nn][Oo]) - # Compare CVS $Id's first so if the file hasn't been modified + # Compare $Id's first so if the file hasn't been modified # local changes will be ignored. # If the files have the same $Id, delete the one in temproot so the # user will have less to wade through if files are left to merge by hand. # - CVSID1=`grep "[$]${CVS_ID_TAG}:" ${DESTDIR}${COMPFILE#.} 2>/dev/null` - CVSID2=`grep "[$]${CVS_ID_TAG}:" ${COMPFILE} 2>/dev/null` || CVSID2=none + ID1=`grep "[$]${ID_TAG}:" ${DESTDIR}${COMPFILE#.} 2>/dev/null` + ID2=`grep "[$]${ID_TAG}:" ${COMPFILE} 2>/dev/null` || ID2=none - case "${CVSID2}" in - "${CVSID1}") - echo " *** Temp ${COMPFILE} and installed have the same CVS Id, deleting" + case "${ID2}" in + "${ID1}") + echo " *** Temp ${COMPFILE} and installed have the same Id, deleting" rm "${COMPFILE}" ;; esac @@ -1334,7 +1334,7 @@ case "${NEED_PWD_MKDB}" in ;; esac -if [ -e "${DESTDIR}/etc/localtime" ]; then # Ignore if TZ == UTC +if [ -e "${DESTDIR}/etc/localtime" -a -z "${PRE_WORLD}" ]; then # Ignore if TZ == UTC echo '' [ -n "${DESTDIR}" ] && tzs_args="-C ${DESTDIR}" if [ -f "${DESTDIR}/var/db/zoneinfo" ]; then @@ -1380,29 +1380,35 @@ case "${COMP_CONFS}" in ;; esac -case "${PRE_WORLD}" in -'') ;; -*) - MAKE_CONF="${SOURCEDIR}/share/examples/etc/make.conf" - - (echo '' - echo '*** Comparing make variables' - echo '' - echo "*** From ${DESTDIR}/etc/make.conf" - echo "*** From ${MAKE_CONF}" - - for MAKE_VAR in `grep -i ^[a-z] ${DESTDIR}/etc/make.conf | cut -d '=' -f 1`; do - echo '' - grep -w ^${MAKE_VAR} ${DESTDIR}/etc/make.conf - grep -w ^#${MAKE_VAR} ${MAKE_CONF} || - echo ' * No example variable with this name' - done) | ${PAGER} - ;; -esac - if [ -n "${PRESERVE_FILES}" ]; then find -d $PRESERVE_FILES_DIR -type d -empty -delete 2>/dev/null rmdir $PRESERVE_FILES_DIR 2>/dev/null fi exit 0 + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Copyright (c) 1998-2012 Douglas Barton +# 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. -- cgit v1.3 From ffe9c13e2ab7679b5c2e5118066c66d4c05f77eb Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Fri, 5 Oct 2012 07:51:21 +0000 Subject: Provide ability for printing and decoding pfsync(4) traffic. This doesn't mean supporting IFT_PFSYNC (which I hope will eventually die). This means decoding packets with IP protocol of 240 caught on any normal interface like Ethernet. The code is based on couple of files from OpenBSD, significantly modified by myself. Parser differentiates for four levels of verbosity: no -v, -v, -vv and -vvv. We don't yet forward this code upstream, because currently it strongly relies on if_pfsync.h and even on pfvar.h. I hope that this can be fixed in future. Reviewed by: gnn, delphij --- contrib/tcpdump/interface.h | 1 + contrib/tcpdump/ipproto.c | 1 + contrib/tcpdump/print-ip.c | 4 + contrib/tcpdump/print-pfsync.c | 451 ++++++++++++++++++++++++++++++++++++++ usr.sbin/tcpdump/tcpdump/Makefile | 1 + 5 files changed, 458 insertions(+) create mode 100644 contrib/tcpdump/print-pfsync.c (limited to 'usr.sbin') diff --git a/contrib/tcpdump/interface.h b/contrib/tcpdump/interface.h index 18668a21f3773..133037ab17f22 100644 --- a/contrib/tcpdump/interface.h +++ b/contrib/tcpdump/interface.h @@ -183,6 +183,7 @@ extern void dvmrp_print(const u_char *, u_int); extern void egp_print(const u_char *, u_int); extern u_int enc_if_print(const struct pcap_pkthdr *, const u_char *); extern u_int pflog_if_print(const struct pcap_pkthdr *, const u_char *); +extern void pfsync_ip_print(const u_char *, u_int); extern u_int arcnet_if_print(const struct pcap_pkthdr *, const u_char *); extern u_int arcnet_linux_if_print(const struct pcap_pkthdr *, const u_char *); extern u_int token_print(const u_char *, u_int, u_int); diff --git a/contrib/tcpdump/ipproto.c b/contrib/tcpdump/ipproto.c index cbb9bf3f74e61..e5d328c6edc3c 100755 --- a/contrib/tcpdump/ipproto.c +++ b/contrib/tcpdump/ipproto.c @@ -56,6 +56,7 @@ const struct tok ipproto_values[] = { { IPPROTO_SCTP, "SCTP" }, { IPPROTO_MOBILITY, "Mobility" }, { IPPROTO_CARP, "CARP" }, + { IPPROTO_PFSYNC, "pfsync" }, { 0, NULL } }; diff --git a/contrib/tcpdump/print-ip.c b/contrib/tcpdump/print-ip.c index 989623386f06a..1ef4ee3522788 100644 --- a/contrib/tcpdump/print-ip.c +++ b/contrib/tcpdump/print-ip.c @@ -483,6 +483,10 @@ again: pgm_print(ipds->cp, ipds->len, (const u_char *)ipds->ip); break; + case IPPROTO_PFSYNC: + pfsync_ip_print(ipds->cp, ipds->len); + break; + default: if ((proto = getprotobynumber(ipds->nh)) != NULL) ND_PRINT((ndo, " %s", proto->p_name)); diff --git a/contrib/tcpdump/print-pfsync.c b/contrib/tcpdump/print-pfsync.c new file mode 100644 index 0000000000000..7a6da89cba3c3 --- /dev/null +++ b/contrib/tcpdump/print-pfsync.c @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2012 Gleb Smirnoff + * Copyright (c) 2002 Michael Shalayeff + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 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 OR HIS RELATIVES 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 MIND, 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. + * + * $OpenBSD: print-pfsync.c,v 1.38 2012/09/19 13:50:36 mikeb Exp $ + * $OpenBSD: pf_print_state.c,v 1.11 2012/07/08 17:48:37 lteo Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include /* XXX */ +#include +#include +#define TCPSTATES +#include + +#include + +#include "interface.h" +#include "addrtoname.h" + +static void pfsync_print(struct pfsync_header *, const u_char *, u_int); +static void print_src_dst(const struct pfsync_state_peer *, + const struct pfsync_state_peer *, uint8_t); +static void print_state(struct pfsync_state *); + +#ifdef notyet +void +pfsync_if_print(u_char *user, const struct pcap_pkthdr *h, + register const u_char *p) +{ + u_int caplen = h->caplen; + + ts_print(&h->ts); + + if (caplen < PFSYNC_HDRLEN) { + printf("[|pfsync]"); + goto out; + } + + pfsync_print((struct pfsync_header *)p, + p + sizeof(struct pfsync_header), + caplen - sizeof(struct pfsync_header)); +out: + if (xflag) { + default_print((const u_char *)p, caplen); + } + putchar('\n'); +} +#endif /* notyet */ + +void +pfsync_ip_print(const u_char *bp, u_int len) +{ + struct pfsync_header *hdr = (struct pfsync_header *)bp; + + if (len < PFSYNC_HDRLEN) + printf("[|pfsync]"); + else + pfsync_print(hdr, bp + sizeof(struct pfsync_header), + len - sizeof(struct pfsync_header)); +} + +struct pfsync_actions { + const char *name; + size_t len; + void (*print)(const void *); +}; + +static void pfsync_print_clr(const void *); +static void pfsync_print_state(const void *); +static void pfsync_print_ins_ack(const void *); +static void pfsync_print_upd_c(const void *); +static void pfsync_print_upd_req(const void *); +static void pfsync_print_del_c(const void *); +static void pfsync_print_bus(const void *); +static void pfsync_print_tdb(const void *); + +struct pfsync_actions actions[] = { + { "clear all", sizeof(struct pfsync_clr), pfsync_print_clr }, + { "insert", sizeof(struct pfsync_state), pfsync_print_state }, + { "insert ack", sizeof(struct pfsync_ins_ack), pfsync_print_ins_ack }, + { "update", sizeof(struct pfsync_ins_ack), pfsync_print_state }, + { "update compressed", sizeof(struct pfsync_upd_c), + pfsync_print_upd_c }, + { "request uncompressed", sizeof(struct pfsync_upd_req), + pfsync_print_upd_req }, + { "delete", sizeof(struct pfsync_state), pfsync_print_state }, + { "delete compressed", sizeof(struct pfsync_del_c), + pfsync_print_del_c }, + { "frag insert", 0, NULL }, + { "frag delete", 0, NULL }, + { "bulk update status", sizeof(struct pfsync_bus), + pfsync_print_bus }, + { "tdb", 0, pfsync_print_tdb }, + { "eof", 0, NULL }, +}; + +static void +pfsync_print(struct pfsync_header *hdr, const u_char *bp, u_int len) +{ + struct pfsync_subheader *subh; + int count, plen, i; + u_int alen; + + plen = ntohs(hdr->len); + + printf("PFSYNCv%d len %d", hdr->version, plen); + + if (hdr->version != PFSYNC_VERSION) + return; + + plen -= sizeof(*hdr); + + while (plen > 0) { + if (len < sizeof(*subh)) + break; + + subh = (struct pfsync_subheader *)bp; + bp += sizeof(*subh); + len -= sizeof(*subh); + plen -= sizeof(*subh); + + if (subh->action >= PFSYNC_ACT_MAX) { + printf("\n act UNKNOWN id %d", subh->action); + return; + } + + count = ntohs(subh->count); + printf("\n %s count %d", actions[subh->action].name, count); + alen = actions[subh->action].len; + + if (subh->action == PFSYNC_ACT_EOF) + return; + + if (actions[subh->action].print == NULL) { + printf("\n unimplemented action %hhu", subh->action); + return; + } + + for (i = 0; i < count; i++) { + if (len < alen) { + len = 0; + break; + } + + if (vflag) + actions[subh->action].print(bp); + + bp += alen; + len -= alen; + plen -= alen; + } + } + + if (plen > 0) { + printf("\n ..."); + return; + } + if (plen < 0) { + printf("\n invalid header length"); + return; + } + if (len > 0) + printf("\n invalid packet length"); +} + +static void +pfsync_print_clr(const void *bp) +{ + const struct pfsync_clr *clr = bp; + + printf("\n\tcreatorid: %08x", htonl(clr->creatorid)); + if (clr->ifname[0] != '\0') + printf(" interface: %s", clr->ifname); +} + +static void +pfsync_print_state(const void *bp) +{ + struct pfsync_state *st = (struct pfsync_state *)bp; + + putchar('\n'); + print_state(st); +} + +static void +pfsync_print_ins_ack(const void *bp) +{ + const struct pfsync_ins_ack *iack = bp; + + printf("\n\tid: %016jx creatorid: %08x", (uintmax_t )be64toh(iack->id), + ntohl(iack->creatorid)); +} + +static void +pfsync_print_upd_c(const void *bp) +{ + const struct pfsync_upd_c *u = bp; + + printf("\n\tid: %016jx creatorid: %08x", (uintmax_t )be64toh(u->id), + ntohl(u->creatorid)); + if (vflag > 2) { + printf("\n\tTCP? :"); + print_src_dst(&u->src, &u->dst, IPPROTO_TCP); + } +} + +static void +pfsync_print_upd_req(const void *bp) +{ + const struct pfsync_upd_req *ur = bp; + + printf("\n\tid: %016jx creatorid: %08x", (uintmax_t )be64toh(ur->id), + ntohl(ur->creatorid)); +} + +static void +pfsync_print_del_c(const void *bp) +{ + const struct pfsync_del_c *d = bp; + + printf("\n\tid: %016jx creatorid: %08x", (uintmax_t )be64toh(d->id), + ntohl(d->creatorid)); +} + +static void +pfsync_print_bus(const void *bp) +{ + const struct pfsync_bus *b = bp; + uint32_t endtime; + int min, sec; + const char *status; + + endtime = ntohl(b->endtime); + sec = endtime % 60; + endtime /= 60; + min = endtime % 60; + endtime /= 60; + + switch (b->status) { + case PFSYNC_BUS_START: + status = "start"; + break; + case PFSYNC_BUS_END: + status = "end"; + break; + default: + status = "UNKNOWN"; + break; + } + + printf("\n\tcreatorid: %08x age: %.2u:%.2u:%.2u status: %s", + htonl(b->creatorid), endtime, min, sec, status); +} + +static void +pfsync_print_tdb(const void *bp) +{ + const struct pfsync_tdb *t = bp; + + printf("\n\tspi: 0x%08x rpl: %ju cur_bytes: %ju", + ntohl(t->spi), (uintmax_t )be64toh(t->rpl), + (uintmax_t )be64toh(t->cur_bytes)); +} + +static void +print_host(struct pf_addr *addr, uint16_t port, sa_family_t af, + const char *proto) +{ + char buf[48]; + + if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL) + printf("?"); + else + printf("%s", buf); + + if (port) + printf(".%hu", ntohs(port)); +} + +static void +print_seq(const struct pfsync_state_peer *p) +{ + if (p->seqdiff) + printf("[%u + %u](+%u)", ntohl(p->seqlo), + ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff)); + else + printf("[%u + %u]", ntohl(p->seqlo), + ntohl(p->seqhi) - ntohl(p->seqlo)); +} + +static void +print_src_dst(const struct pfsync_state_peer *src, + const struct pfsync_state_peer *dst, uint8_t proto) +{ + + if (proto == IPPROTO_TCP) { + if (src->state <= TCPS_TIME_WAIT && + dst->state <= TCPS_TIME_WAIT) + printf(" %s:%s", tcpstates[src->state], + tcpstates[dst->state]); + else if (src->state == PF_TCPS_PROXY_SRC || + dst->state == PF_TCPS_PROXY_SRC) + printf(" PROXY:SRC"); + else if (src->state == PF_TCPS_PROXY_DST || + dst->state == PF_TCPS_PROXY_DST) + printf(" PROXY:DST"); + else + printf(" ", + src->state, dst->state); + if (vflag > 1) { + printf("\n\t"); + print_seq(src); + if (src->wscale && dst->wscale) + printf(" wscale %u", + src->wscale & PF_WSCALE_MASK); + printf(" "); + print_seq(dst); + if (src->wscale && dst->wscale) + printf(" wscale %u", + dst->wscale & PF_WSCALE_MASK); + } + } else if (proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES && + dst->state < PFUDPS_NSTATES) { + const char *states[] = PFUDPS_NAMES; + + printf(" %s:%s", states[src->state], states[dst->state]); + } else if (proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES && + dst->state < PFOTHERS_NSTATES) { + /* XXX ICMP doesn't really have state levels */ + const char *states[] = PFOTHERS_NAMES; + + printf(" %s:%s", states[src->state], states[dst->state]); + } else { + printf(" %u:%u", src->state, dst->state); + } +} + +static void +print_state(struct pfsync_state *s) +{ + struct pfsync_state_peer *src, *dst; + struct pfsync_state_key *sk, *nk; + int min, sec; + + if (s->direction == PF_OUT) { + src = &s->src; + dst = &s->dst; + sk = &s->key[PF_SK_STACK]; + nk = &s->key[PF_SK_WIRE]; + if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) + sk->port[0] = nk->port[0]; + } else { + src = &s->dst; + dst = &s->src; + sk = &s->key[PF_SK_WIRE]; + nk = &s->key[PF_SK_STACK]; + if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) + sk->port[1] = nk->port[1]; + } + printf("\t%s ", s->ifname); + printf("proto %u ", s->proto); + + print_host(&nk->addr[1], nk->port[1], s->af, NULL); + if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) || + nk->port[1] != sk->port[1]) { + printf(" ("); + print_host(&sk->addr[1], sk->port[1], s->af, NULL); + printf(")"); + } + if (s->direction == PF_OUT) + printf(" -> "); + else + printf(" <- "); + print_host(&nk->addr[0], nk->port[0], s->af, NULL); + if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) || + nk->port[0] != sk->port[0]) { + printf(" ("); + print_host(&sk->addr[0], sk->port[0], s->af, NULL); + printf(")"); + } + + print_src_dst(src, dst, s->proto); + + if (vflag > 1) { + uint64_t packets[2]; + uint64_t bytes[2]; + uint32_t creation = ntohl(s->creation); + uint32_t expire = ntohl(s->expire); + + sec = creation % 60; + creation /= 60; + min = creation % 60; + creation /= 60; + printf("\n\tage %.2u:%.2u:%.2u", creation, min, sec); + sec = expire % 60; + expire /= 60; + min = expire % 60; + expire /= 60; + printf(", expires in %.2u:%.2u:%.2u", expire, min, sec); + + bcopy(s->packets[0], &packets[0], sizeof(uint64_t)); + bcopy(s->packets[1], &packets[1], sizeof(uint64_t)); + bcopy(s->bytes[0], &bytes[0], sizeof(uint64_t)); + bcopy(s->bytes[1], &bytes[1], sizeof(uint64_t)); + printf(", %ju:%ju pkts, %ju:%ju bytes", + be64toh(packets[0]), be64toh(packets[1]), + be64toh(bytes[0]), be64toh(bytes[1])); + if (s->anchor != ntohl(-1)) + printf(", anchor %u", ntohl(s->anchor)); + if (s->rule != ntohl(-1)) + printf(", rule %u", ntohl(s->rule)); + } + if (vflag > 1) { + uint64_t id; + + bcopy(&s->id, &id, sizeof(uint64_t)); + printf("\n\tid: %016jx creatorid: %08x", + (uintmax_t )be64toh(id), ntohl(s->creatorid)); + } +} diff --git a/usr.sbin/tcpdump/tcpdump/Makefile b/usr.sbin/tcpdump/tcpdump/Makefile index 84fa1f631f816..1074ce9736ddb 100644 --- a/usr.sbin/tcpdump/tcpdump/Makefile +++ b/usr.sbin/tcpdump/tcpdump/Makefile @@ -25,6 +25,7 @@ SRCS = addrtoname.c af.c checksum.c cpack.c gmpls.c oui.c gmt2local.c \ print-lwapp.c print-lwres.c print-mobile.c print-mpls.c print-msdp.c \ print-mpcp.c \ print-nfs.c print-ntp.c print-null.c print-olsr.c print-ospf.c \ + print-pfsync.c \ print-pgm.c print-pim.c print-ppi.c print-ppp.c print-pppoe.c \ print-pptp.c print-radius.c print-raw.c print-rip.c \ print-rpki-rtr.c \ -- cgit v1.3 From 9a09c4db0e71fdb97cecfbc459bb4ace85fb0edf Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sat, 6 Oct 2012 05:54:41 +0000 Subject: Remove trailing whitespace. --- usr.sbin/sysrc/sysrc.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 index abc543055ed2c..1043d0741cc2f 100644 --- a/usr.sbin/sysrc/sysrc.8 +++ b/usr.sbin/sysrc/sysrc.8 @@ -87,7 +87,7 @@ Print a full usage statement to stderr and exit. .It Fl i Ignore unknown variables. .It Fl j Ar jail -The +The .Ar jid or name of the .Ar jail -- cgit v1.3 From be15d0e4dc9223f6f9b99b9c82db8c41134dc3c2 Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sat, 6 Oct 2012 06:02:29 +0000 Subject: Minor mdoc fix. --- usr.sbin/sysrc/sysrc.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 index 1043d0741cc2f..4b6e22cd6abb6 100644 --- a/usr.sbin/sysrc/sysrc.8 +++ b/usr.sbin/sysrc/sysrc.8 @@ -126,7 +126,7 @@ syntax for querying/setting configuration options. However, while .Xr sysctl 8 serves to query/modify MIBs in the entrant kernel, -.Xr sysrc 8 +.Nm instead works on values in the system .Xr rc.conf 5 configuration files. -- cgit v1.3 From 3dacd67e46e80e9c5d2604dbff76247ef6be1c2c Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sat, 6 Oct 2012 06:07:33 +0000 Subject: mdoc: begin sentences on a new line. --- usr.sbin/sysrc/sysrc.8 | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc.8 b/usr.sbin/sysrc/sysrc.8 index 4b6e22cd6abb6..43ba8858a14c5 100644 --- a/usr.sbin/sysrc/sysrc.8 +++ b/usr.sbin/sysrc/sysrc.8 @@ -98,7 +98,8 @@ Show only variable values, not their names. .It Fl N Show only variable names, not their values. .It Fl q -Quiet. Ignore previous +Quiet. +Ignore previous .Fl v and/or .Ev SYSRC_VERBOSE . @@ -108,7 +109,8 @@ Operate within the root directory rather than .Pq Sq / . .It Fl v -Verbose. Print the pathname of the specific +Verbose. +Print the pathname of the specific .Xr rc.conf 5 file where the directive was found. .It Fl x @@ -135,16 +137,20 @@ The list of system configuration files is configured in the file .Ql /etc/defaults/rc.conf within the variable .Ql rc_conf_files , -which by-default contains a space-separated list of pathnames. On all FreeBSD -systems, this defaults to the value "/etc/rc.conf /etc/rc.conf.local". Each -pathname is sourced in-order upon startup. It is in the same fashion that +which by-default contains a space-separated list of pathnames. +On all FreeBSD +systems, this defaults to the value "/etc/rc.conf /etc/rc.conf.local". +Each +pathname is sourced in-order upon startup. +It is in the same fashion that .Nm sources the configuration files before returning the value of the given variable. .Pp When supplied a variable name, .Nm -will return the value of the variable. If the variable does not appear in any +will return the value of the variable. +If the variable does not appear in any of the configured .Ql rc_conf_files , an error is printed and error status is returned. @@ -152,13 +158,16 @@ an error is printed and error status is returned. When changing values of a given variable, it does not matter if the variable appears in any of the .Ql rc_conf_files -or not. If the variable does not appear in any of the files, it is appended to +or not. +If the variable does not appear in any of the files, it is appended to the end of the first pathname in the .Ql rc_conf_files -variable. Otherwise, +variable. +Otherwise, .Nm will replace only the last-occurrence in the last-file found to contain the -variable. This gets the value to take effect next boot without heavily +variable. +This gets the value to take effect next boot without heavily modifying these integral files (yet taking care not to allow the file to grow unwieldy should .Nm @@ -176,7 +185,8 @@ Location of .Ql /etc/defaults/rc.conf file. .It Ev SYSRC_VERBOSE -Default verbosity. Set to non-NULL to enable. +Default verbosity. +Set to non-NULL to enable. .El .Sh DEPENDENCIES The following standard commands are required by -- cgit v1.3 From 0815243c3937b61db8b5a10cc652900d4df4acb0 Mon Sep 17 00:00:00 2001 From: Marcel Moolenaar Date: Sat, 6 Oct 2012 20:01:05 +0000 Subject: Add support for bmake. This includes: 1. Don't do upgrade_checks when using bmake. As long as we have WITH_BMAKE, there's a bootstrap complication in ths respect. Avoid it. Make the necessary changes to have upgrade_checks work wth bmake anyway. 2. Remove the use of -E. It's not needed in our build because we use ?= for the respective variables, which means that we'll take the environment value (if any) anyway. 3. Properly declare phony targets as phony as bmake is a lot smarter (and thus agressive) about build avoidance. 4. Make sure CLEANFILES is complete and use it on .NOPATH. bmake is a lot smarter about build avoidance and should not find files we generate in the source tree. We should not have files in the repository we want to generate, but this is an easier way to cross this hurdle. 5. Have behavior under bmake the same as it is under make with respect to halting when sub-commands fail. Add "set -e" to compound commands so that bmake is informed when sub-commands fail. 6. Make sure crunchgen uses the same make as the rest of the build. This is important when the make utility isn't called make (but bmake for example). 7. While here, add support for using MAKEOBJDIR to set the object tree location. It's the second alternative bmake looks for when determining the actual object directory (= .OBJDIR). Submitted by: Simon Gerraty Submitted by: John Van Horne --- Makefile | 2 ++ Makefile.inc1 | 2 +- gnu/usr.bin/cc/cc_int/Makefile | 3 +++ gnu/usr.bin/cc/include/Makefile | 4 +--- kerberos5/Makefile | 2 +- kerberos5/tools/asn1_compile/Makefile | 2 +- kerberos5/tools/slc/Makefile | 2 +- lib/clang/include/Makefile | 4 +--- share/mk/bsd.crunchgen.mk | 2 ++ share/mk/bsd.dep.mk | 7 +++++-- share/mk/bsd.obj.mk | 7 +++++++ share/mk/bsd.subdir.mk | 4 ++-- share/mk/bsd.sys.mk | 15 +++++++++++++++ share/mk/sys.mk | 15 +++++++++++++++ tools/build/make_check/Makefile | 23 +++++++++++++++++++++-- usr.sbin/crunch/examples/Makefile | 6 +++--- 16 files changed, 81 insertions(+), 19 deletions(-) (limited to 'usr.sbin') diff --git a/Makefile b/Makefile index 1303e0ea18bde..6d070ee8417b2 100644 --- a/Makefile +++ b/Makefile @@ -280,12 +280,14 @@ kernel: buildkernel installkernel # for building the world. # upgrade_checks: +.if !defined(.PARSEDIR) @if ! (cd ${.CURDIR}/tools/build/make_check && \ PATH=${PATH} ${BINMAKE} obj >/dev/null 2>&1 && \ PATH=${PATH} ${BINMAKE} >/dev/null 2>&1); \ then \ (cd ${.CURDIR} && ${MAKE} make); \ fi +.endif # # Upgrade make(1) to the current version using the installed diff --git a/Makefile.inc1 b/Makefile.inc1 index 821416d1c649f..a9b60886eb124 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -330,7 +330,7 @@ LIB32WMAKEENV+= MAKEOBJDIRPREFIX=${OBJTREE}/lib32 \ LIB32WMAKE= ${LIB32WMAKEENV} ${MAKE} -DNO_CPU_CFLAGS -DCOMPAT_32BIT \ -DWITHOUT_BIND -DWITHOUT_MAN -DWITHOUT_INFO \ - -DWITHOUT_HTML -DNO_CTF -DNO_LINT -ECC -ECXX -EAS -ELD \ + -DWITHOUT_HTML -DNO_CTF -DNO_LINT \ DESTDIR=${LIB32TMP} LIB32IMAKE= ${LIB32WMAKE:NINSTALL=*:NDESTDIR=*:N_LDSCRIPTROOT=*} -DNO_INCS .endif diff --git a/gnu/usr.bin/cc/cc_int/Makefile b/gnu/usr.bin/cc/cc_int/Makefile index 7082c3ea4238a..e6051e14e4013 100644 --- a/gnu/usr.bin/cc/cc_int/Makefile +++ b/gnu/usr.bin/cc/cc_int/Makefile @@ -98,4 +98,7 @@ SRCS+= ${OBJS-all:R:S/$/.c/g} toplev.o: toplev.c ${CC} ${CFLAGS} -DTARGET_NAME=\"${GCC_TARGET}\" -c ${.IMPSRC} +# make sure we don't find .o's in ../cc_tools/ +CLEANFILES+= ${OBJS-all} + .include diff --git a/gnu/usr.bin/cc/include/Makefile b/gnu/usr.bin/cc/include/Makefile index 1807a4836b320..169826246bb02 100644 --- a/gnu/usr.bin/cc/include/Makefile +++ b/gnu/usr.bin/cc/include/Makefile @@ -21,6 +21,4 @@ mm_malloc.h: pmm_malloc.h @cp ${.ALLSRC} ${.TARGET} CLEANFILES+= mm_malloc.h -.include -.include -.include +.include diff --git a/kerberos5/Makefile b/kerberos5/Makefile index d87fa1682cb4b..401957b32f259 100644 --- a/kerberos5/Makefile +++ b/kerberos5/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -SUBDIR= doc lib libexec usr.bin usr.sbin +SUBDIR= doc lib libexec tools usr.bin usr.sbin # These are the programs which depend on Kerberos. KPROGS= lib/libpam \ diff --git a/kerberos5/tools/asn1_compile/Makefile b/kerberos5/tools/asn1_compile/Makefile index 94174c4e637b1..194642136a0da 100644 --- a/kerberos5/tools/asn1_compile/Makefile +++ b/kerberos5/tools/asn1_compile/Makefile @@ -25,7 +25,7 @@ SRCS= \ CFLAGS+=-I${KRB5DIR}/lib/roken -I${KRB5DIR}/lib/asn1 -I. -CLEANFILES= roken.h +CLEANFILES= roken.h lex.c parse.c roken.h: make-roken > ${.TARGET} diff --git a/kerberos5/tools/slc/Makefile b/kerberos5/tools/slc/Makefile index 6e909d97761fa..d1b752f464054 100644 --- a/kerberos5/tools/slc/Makefile +++ b/kerberos5/tools/slc/Makefile @@ -12,7 +12,7 @@ SRCS= roken.h \ CFLAGS+=-I${KRB5DIR}/lib/roken -I${KRB5DIR}/lib/sl -I${KRB5DIR}/lib/vers -I. -CLEANFILES= roken.h +CLEANFILES= roken.h slc-gram.c slc-lex.c roken.h: ${MAKE_ROKEN} > ${.TARGET} diff --git a/lib/clang/include/Makefile b/lib/clang/include/Makefile index 9b2670a2f35df..93d91c7dba802 100644 --- a/lib/clang/include/Makefile +++ b/lib/clang/include/Makefile @@ -30,6 +30,4 @@ INCS= altivec.h \ xmmintrin.h \ xopintrin.h -.include -.include -.include +.include diff --git a/share/mk/bsd.crunchgen.mk b/share/mk/bsd.crunchgen.mk index 80d337f8b1603..95f1aa3be03d9 100644 --- a/share/mk/bsd.crunchgen.mk +++ b/share/mk/bsd.crunchgen.mk @@ -38,6 +38,8 @@ OUTPUTS=$(OUTMK) $(OUTC) $(PROG).cache CRUNCHOBJS= ${.OBJDIR} .if defined(MAKEOBJDIRPREFIX) CANONICALOBJDIR:= ${MAKEOBJDIRPREFIX}${.CURDIR} +.elif defined(MAKEOBJDIR) && ${MAKEOBJDIR:M/*} != "" +CANONICALOBJDIR:=${MAKEOBJDIR} .else CANONICALOBJDIR:= /usr/obj${.CURDIR} .endif diff --git a/share/mk/bsd.dep.mk b/share/mk/bsd.dep.mk index 5e4ec0b3086e5..deac736c2d375 100644 --- a/share/mk/bsd.dep.mk +++ b/share/mk/bsd.dep.mk @@ -102,8 +102,8 @@ ${_YC} y.tab.h: ${_YSRC} CLEANFILES+= y.tab.c y.tab.h .elif !empty(YFLAGS:M-d) .for _YH in ${_YC:R}.h -.ORDER: ${_YC} ${_YH} -${_YC} ${_YH}: ${_YSRC} +${_YH}: ${_YC} +${_YC}: ${_YSRC} ${YACC} ${YFLAGS} -o ${_YC} ${.ALLSRC} SRCS+= ${_YH} CLEANFILES+= ${_YH} @@ -123,6 +123,9 @@ ${_YC:R}.o: ${_YC} .if defined(SRCS) depend: beforedepend ${DEPENDFILE} afterdepend +# Tell bmake not to look for generated files via .PATH +.NOPATH: ${DEPENDFILE} + # Different types of sources are compiled with slightly different flags. # Split up the sources, and filter out headers and non-applicable flags. MKDEP_CFLAGS= ${CFLAGS:M-nostdinc*} ${CFLAGS:M-[BIDU]*} ${CFLAGS:M-std=*} \ diff --git a/share/mk/bsd.obj.mk b/share/mk/bsd.obj.mk index bd8f2aabc2189..634261eba5949 100644 --- a/share/mk/bsd.obj.mk +++ b/share/mk/bsd.obj.mk @@ -44,6 +44,8 @@ ____: .if defined(MAKEOBJDIRPREFIX) CANONICALOBJDIR:=${MAKEOBJDIRPREFIX}${.CURDIR} +.elif defined(MAKEOBJDIR) && ${MAKEOBJDIR:M/*} != "" +CANONICALOBJDIR:=${MAKEOBJDIR} .else CANONICALOBJDIR:=/usr/obj${.CURDIR} .endif @@ -116,6 +118,11 @@ cleanobj: clean cleandepend .endif @if [ -L ${.CURDIR}/obj ]; then rm -f ${.CURDIR}/obj; fi +# Tell bmake not to look for generated files via .PATH +.if !empty(CLEANFILES) +.NOPATH: ${CLEANFILES} +.endif + .if !target(clean) clean: .if defined(CLEANFILES) && !empty(CLEANFILES) diff --git a/share/mk/bsd.subdir.mk b/share/mk/bsd.subdir.mk index dc5af72ff175a..a66ab9bf9fce1 100644 --- a/share/mk/bsd.subdir.mk +++ b/share/mk/bsd.subdir.mk @@ -42,7 +42,7 @@ distribute: _SUBDIR: .USE .if defined(SUBDIR) && !empty(SUBDIR) && !defined(NO_SUBDIR) - @${_+_}for entry in ${SUBDIR}; do \ + @${_+_}set -e; for entry in ${SUBDIR}; do \ if test -d ${.CURDIR}/$${entry}.${MACHINE_ARCH}; then \ ${ECHODIR} "===> ${DIRPRFX}$${entry}.${MACHINE_ARCH} (${.TARGET:realinstall=install})"; \ edir=$${entry}.${MACHINE_ARCH}; \ @@ -79,7 +79,7 @@ ${__stage}${__target}: _SUBDIR .endif .endfor ${__target}: - ${_+_}cd ${.CURDIR}; ${MAKE} build${__target}; ${MAKE} install${__target} + ${_+_}set -e; cd ${.CURDIR}; ${MAKE} build${__target}; ${MAKE} install${__target} .endfor .if !target(install) diff --git a/share/mk/bsd.sys.mk b/share/mk/bsd.sys.mk index cc752a26804ae..847e571e0d172 100644 --- a/share/mk/bsd.sys.mk +++ b/share/mk/bsd.sys.mk @@ -126,3 +126,18 @@ CFLAGS+= ${SSP_CFLAGS} # Allow user-specified additional warning flags CFLAGS+= ${CWARNFLAGS} + + +# Tell bmake not to mistake standard targets for things to be searched for +# or expect to ever be up-to-date. +PHONY_NOTMAIN = afterdepend afterinstall all beforedepend beforeinstall \ + beforelinking build build-tools buildfiles buildincludes \ + checkdpadd clean cleandepend cleandir cleanobj configure \ + depend dependall distclean distribute exe extract fetch \ + html includes install installfiles installincludes lint \ + obj objlink objs objwarn patch realall realdepend \ + realinstall regress subdir-all subdir-depend subdir-install \ + tags whereobj + +.PHONY: ${PHONY_NOTMAIN} +.NOTMAIN: ${PHONY_NOTMAIN} diff --git a/share/mk/sys.mk b/share/mk/sys.mk index 3c43ff4c21a29..0e8dc95acb907 100644 --- a/share/mk/sys.mk +++ b/share/mk/sys.mk @@ -16,6 +16,10 @@ unix ?= We run FreeBSD, not UNIX. MACHINE_CPUARCH=${MACHINE_ARCH:C/mips(n32|64)?(el)?/mips/:C/arm(v6)?(eb)?/arm/:C/powerpc64/powerpc/} .endif +# Set any local definitions first. Place this early, but it needs +# MACHINE_CPUARCH to be defined. +.sinclude + # If the special target .POSIX appears (without prerequisites or # commands) before the first noncomment line in the makefile, make shall # process the makefile as specified by the Posix 1003.2 specification. @@ -324,8 +328,19 @@ SHELL= ${__MAKE_SHELL} # XXX hint for bsd.port.mk OBJFORMAT?= elf +# Tell bmake to expand -V VAR by default +.MAKE.EXPAND_VARIABLES= yes + +.if !defined(.PARSEDIR) +# We are not bmake, which is more aggressive about searching .PATH +# It is sometime necessary to curb its enthusiasm with .NOPATH +# The following allows us to quietly ignore .NOPATH when not using bmake. +.NOTMAIN: .NOPATH +.NOPATH: + # Toggle on warnings .WARN: dirsyntax +.endif .endif diff --git a/tools/build/make_check/Makefile b/tools/build/make_check/Makefile index 346b4bb068566..3a0f6ad2a956b 100644 --- a/tools/build/make_check/Makefile +++ b/tools/build/make_check/Makefile @@ -56,8 +56,10 @@ all: @echo "ok 14 shell # Test shell detected no regression." @${SMAKE} shell_1 || ${SMAKE} failure @echo "ok 15 shell_1 # Test shell_1 detected no regression." +.if !defined(.PARSEDIR) @${SMAKE} shell_2 || ${SMAKE} failure @echo "ok 16 shell_2 # Test shell_2 detected no regression." +.endif .if make(C_check) C_check: @@ -92,17 +94,34 @@ lhs_expn: # to determine its value; that was not always the case. .undef notdef notdef: -.if defined(notdef) && ${notdef:U} +.if defined(notdef) && ${notdef:M/} .endif .endif .if make(modifiers) -# See if make(1) supports the C modifier. +.if defined(.PARSEDIR) +# check if bmake can expand plain variables +.MAKE.EXPAND_VARIABLES= yes +x!= ${SMAKE} -V .CURDIR:H modifiers: +.if ${.CURDIR:H} != "$x" + @false +.endif +.else +# See if make(1) supports the C modifier. +modifiers: dollarV @if ${SMAKE} -V .CURDIR:C/.// 2>&1 >/dev/null | \ grep -q "Unknown modifier 'C'"; then \ false; \ fi + +# check that make -V '${VAR}' works +V_expn != V_OK=ok ${SMAKE} -r -f /dev/null -V '$${V_OK}' +dollarV: +.if ${V_expn} == "" + @false +.endif +.endif .endif .if make(arith_expr) diff --git a/usr.sbin/crunch/examples/Makefile b/usr.sbin/crunch/examples/Makefile index 6dd49acc1a158..120830196c66c 100644 --- a/usr.sbin/crunch/examples/Makefile +++ b/usr.sbin/crunch/examples/Makefile @@ -19,14 +19,14 @@ all: $(CRUNCHED) exe: $(CRUNCHED) $(OUTPUTS): $(CONF) - crunchgen ${.CURDIR}/$(CONF) + MAKE=${MAKE} crunchgen ${.CURDIR}/$(CONF) $(CRUNCHED): $(OUTPUTS) submake submake: - make -f $(OUTMK) + ${MAKE} -f $(OUTMK) objs: - make -f $(OUTMK) objs + ${MAKE} -f $(OUTMK) objs cleandir: rm -f $(CLEANDIRFILES) -- cgit v1.3 From 4d36343e3a6a93f2c5f6b92c1c9cd8a1535795bd Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sun, 7 Oct 2012 19:53:04 +0000 Subject: Minor mdoc fixes. --- usr.sbin/bsdconfig/bsdconfig.8 | 2 +- usr.sbin/chroot/chroot.8 | 2 +- usr.sbin/mountd/exports.5 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig.8 b/usr.sbin/bsdconfig/bsdconfig.8 index 3222ee7a2c67f..5bb3e73d7e9f1 100644 --- a/usr.sbin/bsdconfig/bsdconfig.8 +++ b/usr.sbin/bsdconfig/bsdconfig.8 @@ -129,7 +129,7 @@ Generate a graphviz language file .Pq printed on stdout visualizing the -.Xr bsdconfig 8 +.Nm menu, include, and shortcut structure and relationships. See .Dq bsdconfig dot -h for more details. diff --git a/usr.sbin/chroot/chroot.8 b/usr.sbin/chroot/chroot.8 index d847175b32c70..e5f9f444aaf38 100644 --- a/usr.sbin/chroot/chroot.8 +++ b/usr.sbin/chroot/chroot.8 @@ -60,7 +60,7 @@ options are given, the user, group and group list of the process are set to these values after the -.Xr chroot 8 +.Nm has taken place. .Sh ENVIRONMENT The following environment variable is referenced by diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5 index 3a8950fd0d394..75a5121d66733 100644 --- a/usr.sbin/mountd/exports.5 +++ b/usr.sbin/mountd/exports.5 @@ -101,7 +101,7 @@ This line does not export any file system, but simply marks where the root of the server's directory tree is for NFSv4 clients. The exported file systems for NFSv4 are specified via the other lines in the -.Xr exports 5 +.Nm file in the same way as for NFSv2 and NFSv3. The pathnames must not have any symbolic links in them and should not have any -- cgit v1.3 From c388f51ab412a577c0e007c952249b8b2a647e56 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Wed, 10 Oct 2012 14:47:46 +0000 Subject: - Fix the error message when a dependency string is not provided to reference a missing dependency rather than a missing compile command. - Don't append a newline to the auto-generated compile command. The compile command has a newline appended when it is later output to the Makefile. MFC after: 2 weeks --- usr.sbin/config/mkmakefile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c index 8a7f55af6efb2..324ad3b7bfbe9 100644 --- a/usr.sbin/config/mkmakefile.c +++ b/usr.sbin/config/mkmakefile.c @@ -431,7 +431,7 @@ nextparam: next_quoted_word(fp, wd); if (wd == 0) { fprintf(stderr, - "%s: %s missing compile command string.\n", + "%s: %s missing dependency string.\n", fname, this); exit(1); } @@ -762,7 +762,7 @@ do_rules(FILE *f) break; } snprintf(cmd, sizeof(cmd), - "${%s_%c%s}\n", ftype, + "${%s_%c%s}", ftype, toupper(och), ftp->f_flags & NOWERROR ? "_NOWERROR" : ""); compilewith = cmd; -- cgit v1.3 From b79d36786c03f20a5f891f8961324183b02671f8 Mon Sep 17 00:00:00 2001 From: Nick Hibma Date: Fri, 12 Oct 2012 22:48:33 +0000 Subject: Some 3G modems return the wrong signature in echo packets and make it impossible to use LQR/ECHO. They return want_magic instead. With this change it is now possible to use enable lqr set lqrperiod 5 enable echo set echoperiod 5 in your ppp.conf file. MFC after: 3 days --- usr.sbin/ppp/lqr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/ppp/lqr.c b/usr.sbin/ppp/lqr.c index 7aca17a130643..7ce1513d1fcc1 100644 --- a/usr.sbin/ppp/lqr.c +++ b/usr.sbin/ppp/lqr.c @@ -108,7 +108,8 @@ lqr_RecvEcho(struct fsm *fp, struct mbuf *bp) * die as a result. */ } - if (lqr.signature == SIGNATURE) { + if (lqr.signature == SIGNATURE + || lqr.signature == lcp->want_magic) { /* some implementations return the wrong magic */ /* careful not to update lqm.echo.seq_recv with older values */ if ((hdlc->lqm.echo.seq_recv > (u_int32_t)0 - 5 && lqr.sequence < 5) || (hdlc->lqm.echo.seq_recv <= (u_int32_t)0 - 5 && -- cgit v1.3 From 25408c853d9ecb2e76b9e38407338f86ecb8a55c Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 13 Oct 2012 03:56:33 +0000 Subject: SVN r240684 broke the ability of the dot module to map include dependencies. Teach the dot module about the new location these includes moved to (as part of r240684) and clean things up a bit. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/dot/dot | 112 ++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 46 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/dot/dot b/usr.sbin/bsdconfig/dot/dot index 47df6b7c30422..1a9032471f27c 100755 --- a/usr.sbin/bsdconfig/dot/dot +++ b/usr.sbin/bsdconfig/dot/dot @@ -229,6 +229,11 @@ done # [Optionally] Calculate list of include files # if [ "$SHOW_INCLUDES" ]; then + print_includes_awk=' + BEGIN { regex = "^f_include \\$BSDCFG_SHARE/" } + ( $0 ~ regex ) { sub(regex, ""); print } + ' # END-QUOTE + # # Build list of files in which to search for includes # @@ -249,17 +254,8 @@ if [ "$SHOW_INCLUDES" ]; then include_file_list= for file in $file_list; do include_file_list="$include_file_list $( - awk -v file="$file" -v item="${file%%/*}" ' - BEGIN { regex = "^f_include \\$BSDCFG_LIBE/" } - ( $0 ~ regex "include/" ) { - sub(regex, "") - print - } - ( $0 ~ regex "\\$APP_DIR/include/" ) { - sub(regex "\\$APP_DIR", item) - print - } - ' $file + awk -v file="$file" -v item="${file%%/*}" \ + "$print_includes_awk" $file )" done @@ -277,17 +273,8 @@ if [ "$SHOW_INCLUDES" ]; then # for file in $include_file_list; do include_file_list="$include_file_list $( - awk -v file="$file" -v item="${file%%/*}" ' - BEGIN { regex = "^f_include \\$BSDCFG_LIBE/" } - ( $0 ~ regex "include/" ) { - sub(regex, "") - print - } - ( $0 ~ regex "\\$APP_DIR/include/" ) { - sub(regex "\\$APP_DIR", item) - print - } - ' $file + awk -v file="$file" -v item="${file%%/*}" \ + "$print_includes_awk" $BSDCFG_SHARE/$file )" done @@ -431,7 +418,7 @@ if [ "$SHOW_INCLUDES" ]; then printf '\t\tconstraint = false;\n' for include_file in $include_file_list; do print_node "$include_file" \ - "label = \"${include_file#*include/}\"" + "label = \"${include_file##*/}\"" done end_nodelist fi @@ -444,6 +431,7 @@ if [ "$SHOW_INCLUDES" ]; then shape=oval color=black fillcolor=white style=filled edge_color=grey begin_nodelist "$shape" "$color" "$fillcolor" "$style" print_node edge "style = dashed" "color = $edge_color" + print_node edge "label = \"\\T\"" "fontsize = 9" file_list=$( for file in \ $BSDCONFIG \ @@ -452,6 +440,8 @@ if [ "$SHOW_INCLUDES" ]; then $cmd_program_list \ $include_file_list \ ; do + [ -f "$BSDCFG_SHARE/$file" ] && + echo $BSDCFG_SHARE/$file [ -e "$file" ] && echo $file done | sort -u ) @@ -460,31 +450,17 @@ if [ "$SHOW_INCLUDES" ]; then grep -qlI f_include $file || continue awk \ - -v file="$file" \ - -v item="${file%%/*}" \ + -v file="${file#$BSDCFG_SHARE/}" \ -v bsdconfig="$BSDCONFIG" \ ' - BEGIN { regex = "^f_include \\$BSDCFG_LIBE/" } - ( $0 ~ regex "include/" ) { + BEGIN { regex = "^f_include \\$BSDCFG_SHARE/" } + ( $0 ~ regex ) { sub(regex, "") if ( file == bsdconfig ) sub(".*/", "", file) printf "\t\t\"%s\" -> \"%s\";\n", $0, file } - ( $0 ~ regex "\\$APP_DIR/include/" ) { - sub(regex "\\$APP_DIR", item) - if ( file == bsdconfig ) sub(".*/", "", file) - printf "\t\t\"%s\" -> \"%s\";\n", $0, file - } ' $file - done | sort | awk ' - BEGIN { found = 0 } - { - # If already found or no-match... just spew - if ( found ) { print; next } - if ( $0 !~ /^[[:space:]]*"include\// ) { print; next } - printf "\t\tedge [ label = \"\\T\", fontsize = 9 ];\n" - print; found = 1 - }' + done | sort end_nodelist fi @@ -554,14 +530,58 @@ printf '\t\tlabel = "bsdconfig(8)";\n' printf '\t\ttooltip = "bsdconfig(8)";\n' print_node "bsdconfig" if [ "$SHOW_INCLUDES" ]; then - printf '\t\tsubgraph "cluster_bsdconfig_includes" {\n' + printf '\t\tsubgraph "cluster_includes" {\n' printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_includes" printf '\t\t\tlabel = "%s";\n' "$msg_includes" + for include_file in $include_file_list; do - case "$include_file" in - include/*) printf '\t\t\t"%s";\n' "$include_file";; - esac - done + echo $include_file + done | awk -v bgcolor="$bgcolor_bsdconfig" ' + BEGIN { created = 0 } + function end_subgraph() { printf "\t\t\t};\n" } + ( $0 !~ "/" ) { + if ( ! created ) + { + printf "\t\t\tsubgraph \"%s\" {\n", + "cluster_bsdconfig_includes" + printf "\t\t\t\tbgcolor = \"%s\";\n", bgcolor + printf "\t\t\t\tlabel = \"bsdconfig\";\n" + created++ + } + printf "\t\t\t\t\"%s\";\n", $1 + } + END { created && end_subgraph() }' + + for include_file in $include_file_list; do + echo $include_file + done | awk ' + BEGIN { created = 0 } + function end_subgraph() { printf "\t\t\t};\n" } + ( $0 ~ "/" ) { + include_dir_tmp = $1 + sub("/[^/]*$", "", include_dir_tmp) + gsub(/[^[:alnum:]_]/, "_", include_dir_tmp) + + if ( created && include_dir != include_dir_tmp ) + { + end_subgraph() + created = 0 + } + + if ( ! created ) + { + include_dir = include_dir_tmp + printf "\t\t\tsubgraph \"cluster_%s_includes\" {\n", + include_dir + printf "\t\t\t\tbgcolor = \"white\";\n" + printf "\t\t\t\tlabel = \"%s\";\n", include_dir + created++ + } + + printf "\t\t\t\t\"%s\";\n", $1 + } + END { created && end_subgraph() }' + printf '\t\t};\n' fi end_nodelist -- cgit v1.3 From a01d461b70933d6eab44ccef091f10b90afb816a Mon Sep 17 00:00:00 2001 From: Joel Dahl Date: Sun, 14 Oct 2012 10:12:32 +0000 Subject: Remove cvs/cvsup reference. --- usr.sbin/portsnap/portsnap/portsnap.8 | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/portsnap/portsnap/portsnap.8 b/usr.sbin/portsnap/portsnap/portsnap.8 index cd0ddf2dec145..da94efc4dcfbc 100644 --- a/usr.sbin/portsnap/portsnap/portsnap.8 +++ b/usr.sbin/portsnap/portsnap/portsnap.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 28, 2012 +.Dd October 14, 2012 .Dt PORTSNAP 8 .Os FreeBSD .Sh NAME @@ -55,10 +55,6 @@ In a normal update operation, .Nm will routinely restore modified files to their unmodified state and delete unrecognized local files. -This behavior is different to -.Xr cvs 1 -and -.Xr cvsup 1 . .Sh OPTIONS The following options are supported: .Bl -tag -width "-f conffile" -- cgit v1.3 From 42cdd52718460a4172ee6e51c6686a0cdf094554 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sun, 14 Oct 2012 23:45:56 +0000 Subject: Mirror the changes made in SVN r240798: Replace "( : ${var?} )" syntax with better "[ ${var+set} ]" syntax. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/sysrc/sysrc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/sysrc/sysrc b/usr.sbin/sysrc/sysrc index 24a304db36e58..e1c15d63dbb65 100644 --- a/usr.sbin/sysrc/sysrc +++ b/usr.sbin/sysrc/sysrc @@ -251,7 +251,7 @@ shift $(( $OPTIND - 1 )) # Taint-check all rc.conf(5) files # errmsg="$pgm: Exiting due to previous syntax errors" -if ( : ${RC_CONFS?} ) > /dev/null 2>&1; then +if [ "${RC_CONFS+set}" ]; then ( for i in $RC_CONFS; do [ -e "$i" ] || continue /bin/sh -n "$i" || exit $FAILURE @@ -311,7 +311,7 @@ if [ "$JAIL" -o "$ROOTDIR" ]; then $( [ "$SHOW_VALUE" ] || echo \ -N ) $( [ "$SHOW_FILE" ] && echo \ -F ) " - if ( : ${RC_CONFS?} ) > /dev/null 2>&1; then + if [ "${RC_CONFS+set}" ]; then args="$args -f '$RC_CONFS'" fi for arg in "$@"; do @@ -437,8 +437,7 @@ if [ "$SHOW_ALL" ]; then # explicit value, modifying the default behavior of # source_rc_confs(). # - ( : ${RC_CONFS?} ) > /dev/null 2>&1 && - rc_conf_files="$RC_CONFS" + [ "${RC_CONFS+set}" ] && rc_conf_files="$RC_CONFS" source_rc_confs -- cgit v1.3 From c548eb5cadf07fe8a268ff35cb7a03bcc56689d3 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 15 Oct 2012 00:17:16 +0000 Subject: Add a new '-S' option to mountd, which tells it to suspend execution of the nfsd threads while it is reloading the exports. This avoids clients from getting intermittent access errors when the exports are being reloaded non-atomically. It is not an ideal solution, since requests will back up while the nfsd threads are suspended. Also, when this option is used, if mountd crashes while reloading exports, mountd will have to be restarted to get the nfsd threads to resume execution. This has been tested by Vincent Hoffman (vince at unsane.co.uk) and John Hickey (jh at deterlab.net). The nfse patch offers a more comprehensive solution for this issue. PR: kern/9619, kern/131342 Reviewed by: kib MFC after: 2 weeks --- usr.sbin/mountd/mountd.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c index b066fc748b5dd..543fc14f9cc3b 100644 --- a/usr.sbin/mountd/mountd.c +++ b/usr.sbin/mountd/mountd.c @@ -246,6 +246,7 @@ static int mallocd_svcport = 0; static int *sock_fd; static int sock_fdcnt; static int sock_fdpos; +static int suspend_nfsd = 0; int opt_flags; static int have_v6 = 1; @@ -311,7 +312,7 @@ main(int argc, char **argv) else close(s); - while ((c = getopt(argc, argv, "2deh:lnop:r")) != -1) + while ((c = getopt(argc, argv, "2deh:lnop:rS")) != -1) switch (c) { case '2': force_v2 = 1; @@ -363,6 +364,9 @@ main(int argc, char **argv) out_of_mem(); } break; + case 'S': + suspend_nfsd = 1; + break; default: usage(); }; @@ -921,7 +925,7 @@ usage(void) { fprintf(stderr, "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p ] [-r] " - "[-h ] [export_file ...]\n"); + "[-S] [-h ] [export_file ...]\n"); exit(1); } @@ -1659,6 +1663,8 @@ get_exportlist(void) int done; struct nfsex_args eargs; + if (suspend_nfsd != 0) + (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); v4root_dirpath[0] = '\0'; bzero(&export, sizeof(export)); export.ex_flags = MNT_DELEXPORT; @@ -1787,6 +1793,9 @@ get_exportlist(void) */ if (run_v4server > 0 && has_publicfh == 0) (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); + + /* Resume the nfsd. If they weren't suspended, this is harmless. */ + (void)nfssvc(NFSSVC_RESUMENFSD, NULL); } /* -- cgit v1.3 From e6f3cb32cca25bb73a60b32df4b9afc0112e2ac6 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 15 Oct 2012 00:24:23 +0000 Subject: Add a description for the '-S' option to the mountd man page. This is a content change. Reviewed by: kib MFC after: 2 weeks --- usr.sbin/mountd/mountd.8 | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/mountd/mountd.8 b/usr.sbin/mountd/mountd.8 index 2bc670c92ddad..fe73743304f8f 100644 --- a/usr.sbin/mountd/mountd.8 +++ b/usr.sbin/mountd/mountd.8 @@ -28,7 +28,7 @@ .\" @(#)mountd.8 8.4 (Berkeley) 4/28/95 .\" $FreeBSD$ .\" -.Dd April 23, 2011 +.Dd October 14, 2012 .Dt MOUNTD 8 .Os .Sh NAME @@ -38,7 +38,7 @@ mount requests .Sh SYNOPSIS .Nm -.Op Fl 2delnor +.Op Fl 2delnorS .Op Fl h Ar bindip .Op Fl p Ar port .Op Ar exportsfile ... @@ -131,6 +131,19 @@ flag. Specify an alternate location for the exports file. More than one exports file can be specified. +.It Fl S +Tell mountd to suspend/resume execution of the nfsd threads whenever +the exports list is being reloaded. +This avoids intermittent access +errors for clients that do NFS RPCs while the exports are being +reloaded, but introduces a delay in RPC response while the reload +is in progress. +If +.Nm +crashes while an exports load is in progress, +.Nm +must be restarted to get the nfsd threads running again, if this +option is used. .El .Pp When -- cgit v1.3 From 2543786a37e37dcf9c42db54681d36f7359542eb Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Mon, 15 Oct 2012 08:21:49 +0000 Subject: Add per-second scheduling into the cron(8). Right now it's only available via the new @every_second shortcut. ENOTIME to implement crontab(5) format extensions to allow more flexible scheduling. In order to address some concerns expressed by Terry Lambert while discussing the topic few years ago, about per-second cron possibly causing some bad effects on /etc/crontab by stat()ing it every second instead of every minute now (i.e. atime update), only check that database needs to be reloaded on every 60-th loop run. This should be close enough to the current behaviour. Add "@every_minute" shortcut while I am here. MFC after: 1 month --- usr.sbin/cron/cron/cron.c | 26 +++++++++++++++++--------- usr.sbin/cron/cron/cron.h | 5 +++++ usr.sbin/cron/crontab/crontab.5 | 2 ++ usr.sbin/cron/lib/entry.c | 21 +++++++++++++++++++++ 4 files changed, 45 insertions(+), 9 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 8524ff9dfcb83..011458a537f1d 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -98,6 +98,7 @@ main(argc, argv) char *argv[]; { cron_db database; + int runnum; ProgramName = argv[0]; @@ -149,21 +150,24 @@ main(argc, argv) load_database(&database); run_reboot_jobs(&database); cron_sync(); + runnum = 0; while (TRUE) { # if DEBUGGING /* if (!(DebugFlags & DTEST)) */ # endif /*DEBUGGING*/ cron_sleep(&database); - load_database(&database); + if (runnum % 60 == 0) + load_database(&database); /* do this iteration */ cron_tick(&database); - /* sleep 1 minute + /* sleep 1 second */ - TargetTime += 60; + TargetTime += 1; + runnum += 1; } } @@ -194,22 +198,23 @@ cron_tick(db) static time_t diff = 0, /* time difference in seconds from the last offset change */ difflimit = 0; /* end point for the time zone correction */ struct tm otztm; /* time in the old time zone */ - int otzminute, otzhour, otzdom, otzmonth, otzdow; + int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; register struct tm *tm = localtime(&TargetTime); - register int minute, hour, dom, month, dow; + register int second, minute, hour, dom, month, dow; register user *u; register entry *e; /* make 0-based values out of these so we can use them as indicies */ + second = tm->tm_sec -FIRST_SECOND; minute = tm->tm_min -FIRST_MINUTE; hour = tm->tm_hour -FIRST_HOUR; dom = tm->tm_mday -FIRST_DOM; month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; dow = tm->tm_wday -FIRST_DOW; - Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", - getpid(), minute, hour, dom, month, dow)) + Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", + getpid(), second, minute, hour, dom, month, dow)) if (dst_enabled && last_time != 0 && TargetTime > last_time /* exclude stepping back */ @@ -262,6 +267,7 @@ cron_tick(db) /* make 0-based values out of these so we can use them as indicies */ + otzsecond = otztm.tm_sec -FIRST_SECOND; otzminute = otztm.tm_min -FIRST_MINUTE; otzhour = otztm.tm_hour -FIRST_HOUR; otzdom = otztm.tm_mday -FIRST_DOM; @@ -283,7 +289,8 @@ cron_tick(db) e->uid, e->gid, e->cmd)) if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { - if (bit_test(e->minute, otzminute) + if (bit_test(e->second, otzsecond) + && bit_test(e->minute, otzminute) && bit_test(e->hour, otzhour) && bit_test(e->month, otzmonth) && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) @@ -302,7 +309,8 @@ cron_tick(db) continue; } - if (bit_test(e->minute, minute) + if (bit_test(e->second, second) + && bit_test(e->minute, minute) && bit_test(e->hour, hour) && bit_test(e->month, month) && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h index 38eb33b0c6f58..387c72bbed090 100644 --- a/usr.sbin/cron/cron/cron.h +++ b/usr.sbin/cron/cron/cron.h @@ -124,6 +124,10 @@ LineNumber = ln; \ } +#define FIRST_SECOND 0 +#define LAST_SECOND 59 +#define SECOND_COUNT (LAST_SECOND - FIRST_SECOND + 1) + #define FIRST_MINUTE 0 #define LAST_MINUTE 59 #define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) @@ -165,6 +169,7 @@ typedef struct _entry { #endif char **envp; char *cmd; + bitstr_t bit_decl(second, SECOND_COUNT); bitstr_t bit_decl(minute, MINUTE_COUNT); bitstr_t bit_decl(hour, HOUR_COUNT); bitstr_t bit_decl(dom, DOM_COUNT); diff --git a/usr.sbin/cron/crontab/crontab.5 b/usr.sbin/cron/crontab/crontab.5 index 458175e7a48d2..9be37df264e5f 100644 --- a/usr.sbin/cron/crontab/crontab.5 +++ b/usr.sbin/cron/crontab/crontab.5 @@ -232,6 +232,8 @@ string meaning @daily Run once a day, "0 0 * * *". @midnight (same as @daily) @hourly Run once an hour, "0 * * * *". +@every_minute Run once a minute, "*/1 * * * *". +@every_second Run once a second. .Ed .Sh EXAMPLE CRON FILE .Bd -literal diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index ea015da62704e..9c1106a3d735f 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -151,6 +151,7 @@ load_entry(file, error_func, pw, envp) e->flags |= WHEN_REBOOT; } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ Debug(DPARS, ("load_entry()...yearly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); @@ -159,6 +160,7 @@ load_entry(file, error_func, pw, envp) e->flags |= DOW_STAR; } else if (!strcmp("monthly", cmd)) { Debug(DPARS, ("load_entry()...monthly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); @@ -167,6 +169,7 @@ load_entry(file, error_func, pw, envp) e->flags |= DOW_STAR; } else if (!strcmp("weekly", cmd)) { Debug(DPARS, ("load_entry()...weekly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); @@ -175,6 +178,7 @@ load_entry(file, error_func, pw, envp) bit_set(e->dow, 0); } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { Debug(DPARS, ("load_entry()...daily shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); @@ -182,11 +186,28 @@ load_entry(file, error_func, pw, envp) bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("hourly", cmd)) { Debug(DPARS, ("load_entry()...hourly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + } else if (!strcmp("every_minute", cmd)) { + Debug(DPARS, ("load_entry()...every_minute shortcut\n")) + bit_set(e->second, 0); + bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); + bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); + bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); + bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); + bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + } else if (!strcmp("every_second", cmd)) { + Debug(DPARS, ("load_entry()...every_second shortcut\n")) + bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1)); + bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); + bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); + bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); + bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); + bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else { ecode = e_timespec; goto eof; -- cgit v1.3 From 55793cdccf0defd3ebcb3abe6c39747dd1fb91ef Mon Sep 17 00:00:00 2001 From: Attilio Rao Date: Tue, 16 Oct 2012 09:59:10 +0000 Subject: Disconnect non-MPSAFE PORTALFS from the build in preparation for dropping GIANT from VFS. This is not targeted for MFC. --- etc/defaults/rc.conf | 2 +- etc/mtree/BSD.usr.dist | 2 -- include/Makefile | 2 +- sbin/mount/mount.8 | 2 -- sbin/mount/mount.c | 2 +- share/doc/smm/01.setup/3.t | 6 ++---- share/doc/smm/01.setup/4.t | 1 - share/examples/Makefile | 3 --- share/man/man7/hier.7 | 2 -- sys/boot/forth/loader.conf | 1 - sys/conf/NOTES | 6 +----- sys/conf/files | 2 -- sys/conf/options | 1 - sys/kern/Make.tags.inc | 2 -- sys/modules/Makefile | 1 - usr.sbin/Makefile | 1 - usr.sbin/crunch/examples/really-big.conf | 2 +- 17 files changed, 7 insertions(+), 31 deletions(-) (limited to 'usr.sbin') diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index b6bc27c1e1a75..1dad35be5bfc7 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -92,7 +92,7 @@ fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails. fsck_y_flags="" # Additional flags for fsck -y background_fsck="YES" # Attempt to run fsck in the background where possible. background_fsck_delay="60" # Time to wait (seconds) before starting the fsck. -netfs_types="nfs:NFS oldnfs:OLDNFS smbfs:SMB portalfs:PORTAL nwfs:NWFS" # Net filesystems. +netfs_types="nfs:NFS oldnfs:OLDNFS smbfs:SMB nwfs:NWFS" # Net filesystems. extra_netfs_types="NO" # List of network extra filesystem types for delayed # mount at startup (or NO). diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index 426e0f9c9a635..6c2ee065826ab 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -289,8 +289,6 @@ .. pf .. - portal - .. ppi .. ppp diff --git a/include/Makefile b/include/Makefile index 573a661598999..89bcb9117e592 100644 --- a/include/Makefile +++ b/include/Makefile @@ -44,7 +44,7 @@ LSUBDIRS= cam/ata cam/scsi \ dev/ofw dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \ dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \ fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/ntfs fs/nullfs \ - ${_fs_nwfs} fs/portalfs fs/procfs fs/smbfs fs/udf fs/unionfs \ + ${_fs_nwfs} fs/procfs fs/smbfs fs/udf fs/unionfs \ geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \ geom/mirror geom/mountver geom/multipath geom/nop \ geom/raid geom/raid3 geom/shsec geom/stripe geom/virstor \ diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 index c1ce669fa517d..fea0171820a30 100644 --- a/sbin/mount/mount.8 +++ b/sbin/mount/mount.8 @@ -450,7 +450,6 @@ However, for the following file system types: .Cm nwfs , .Cm nullfs , .Cm oldnfs , -.Cm portalfs , .Cm smbfs , .Cm udf , and @@ -548,7 +547,6 @@ support for a particular file system might be provided either on a static .Xr mount_ntfs 8 , .Xr mount_nullfs 8 , .Xr mount_nwfs 8 , -.Xr mount_portalfs 8 , .Xr mount_smbfs 8 , .Xr mount_udf 8 , .Xr mount_unionfs 8 , diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c index ca81795c0d691..63d53b069d878 100644 --- a/sbin/mount/mount.c +++ b/sbin/mount/mount.c @@ -143,7 +143,7 @@ use_mountprog(const char *vfstype) unsigned int i; const char *fs[] = { "cd9660", "mfs", "msdosfs", "nfs", "ntfs", - "nwfs", "nullfs", "oldnfs", "portalfs", "smbfs", "udf", "unionfs", + "nwfs", "nullfs", "oldnfs", "smbfs", "udf", "unionfs", NULL }; diff --git a/share/doc/smm/01.setup/3.t b/share/doc/smm/01.setup/3.t index 5b0afd4775ca8..fb7be963f079d 100644 --- a/share/doc/smm/01.setup/3.t +++ b/share/doc/smm/01.setup/3.t @@ -1237,10 +1237,8 @@ location that uses a different password file. .PP Other new filesystems that may be stacked include the loopback filesystem .Xr mount_lofs (8), -the kernel filesystem -.Xr mount_kernfs (8), -and the portal filesystem -.Xr mount_portal (8). +and the kernel filesystem +.Xr mount_kernfs (8). .PP The buffer cache in the kernel is now organized as a file block cache rather than a device block cache. diff --git a/share/doc/smm/01.setup/4.t b/share/doc/smm/01.setup/4.t index 25f9efa42b8bd..149aab04467b2 100644 --- a/share/doc/smm/01.setup/4.t +++ b/share/doc/smm/01.setup/4.t @@ -89,7 +89,6 @@ miscfs miscellaneous filesystems broken down as follows kernfs filesystem access to kernel data structures lofs loopback filesystem nullfs another loopback filesystem - portal associate processes with filesystem locations specfs device special files umapfs provide alternate uid/gid mappings dev generic device drivers (SCSI, vnode, concatenated disk) diff --git a/share/examples/Makefile b/share/examples/Makefile index 30f381a1c9445..36887ebdb89c6 100644 --- a/share/examples/Makefile +++ b/share/examples/Makefile @@ -26,7 +26,6 @@ LDIRS= BSD_daemon \ netgraph \ nwclient \ perfmon \ - portal \ ppi \ ppp \ printing \ @@ -135,8 +134,6 @@ XFILES= BSD_daemon/FreeBSD.pfa \ perfmon/Makefile \ perfmon/README \ perfmon/perfmon.c \ - portal/README \ - portal/portal.conf \ ppi/Makefile \ ppi/ppilcd.c \ ppp/chap-auth \ diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7 index 2989eb4e386d9..856d9462b9217 100644 --- a/share/man/man7/hier.7 +++ b/share/man/man7/hier.7 @@ -258,8 +258,6 @@ NTFS file system loopback file system .It Pa nwfs/ NetWare file system -.It Pa portalfs/ -portal file system .It Pa procfs/ process file system .It Pa smbfs/ diff --git a/sys/boot/forth/loader.conf b/sys/boot/forth/loader.conf index a5d044d8fe0d9..70df14938f785 100644 --- a/sys/boot/forth/loader.conf +++ b/sys/boot/forth/loader.conf @@ -172,7 +172,6 @@ nfsserver_load="NO" # NFS server ntfs_load="NO" # NTFS ntfs_iconv_load="NO" # NTFS iconv character support nullfs_load="NO" # Null filesystem -portalfs_load="NO" # Portal filesystem procfs_load="NO" # Process filesystem reiserfs_load="NO" # ReiserFS unionfs_load="NO" # Union filesystem diff --git a/sys/conf/NOTES b/sys/conf/NOTES index b323f2331708e..14e5c0557f0a5 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -988,10 +988,7 @@ options ZERO_COPY_SOCKETS # time. Some people still prefer to statically compile other # filesystems as well. # -# NB: The PORTAL filesystem is known to be buggy, and WILL panic your -# system if you attempt to do anything with it. It is included here -# as an incentive for some enterprising soul to sit down and fix it. -# The UNION filesystem was known to be buggy in the past. It is now +# NB: The UNION filesystem was known to be buggy in the past. It is now # being actively maintained, although there are still some issues being # resolved. # @@ -1019,7 +1016,6 @@ options NTFS options NULLFS #NULL filesystem # Broken (depends on NCP): #options NWFS #NetWare filesystem -options PORTALFS #Portal filesystem options PROCFS #Process filesystem (requires PSEUDOFS) options PSEUDOFS #Pseudo-filesystem framework options PSEUDOFS_TRACE #Debugging support for PSEUDOFS diff --git a/sys/conf/files b/sys/conf/files index 3e1a9f0fade73..2a745b937d7f2 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2387,8 +2387,6 @@ fs/nwfs/nwfs_node.c optional nwfs fs/nwfs/nwfs_subr.c optional nwfs fs/nwfs/nwfs_vfsops.c optional nwfs fs/nwfs/nwfs_vnops.c optional nwfs -fs/portalfs/portal_vfsops.c optional portalfs -fs/portalfs/portal_vnops.c optional portalfs fs/procfs/procfs.c optional procfs fs/procfs/procfs_ctl.c optional procfs fs/procfs/procfs_dbregs.c optional procfs diff --git a/sys/conf/options b/sys/conf/options index a7ac3111a1429..4248b846da8ba 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -217,7 +217,6 @@ NANDFS opt_dontuse.h NTFS opt_dontuse.h NULLFS opt_dontuse.h NWFS opt_dontuse.h -PORTALFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h REISERFS opt_dontuse.h diff --git a/sys/kern/Make.tags.inc b/sys/kern/Make.tags.inc index 9ef7250dafb24..28dc0f77aa8cb 100644 --- a/sys/kern/Make.tags.inc +++ b/sys/kern/Make.tags.inc @@ -31,7 +31,6 @@ COMM= ${SYS}/dev/advansys/*.[ch] \ ${SYS}/fs/ntfs/*.[ch] \ ${SYS}/fs/nullfs/*.[ch] \ ${SYS}/fs/nwfs/*.[ch] \ - ${SYS}/fs/portalfs/*.[ch] \ ${SYS}/fs/procfs/*.[ch] \ ${SYS}/fs/smbfs/*.[ch] \ ${SYS}/fs/udf/*.[ch] \ @@ -92,7 +91,6 @@ COMMDIR2= ${SYS}/dev/advansys \ ${SYS}/fs/fifofs \ ${SYS}/fs/msdosfs \ ${SYS}/fs/nullfs \ - ${SYS}/fs/portalfs \ ${SYS}/fs/procfs \ ${SYS}/fs/specfs \ ${SYS}/fs/unionfs \ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index cc71d1a06039e..e6d10db81816c 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -263,7 +263,6 @@ SUBDIR= \ ${_pfsync} \ plip \ ${_pmc} \ - portalfs \ ppbus \ ppc \ ppi \ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index a915cab60eb31..296856b7e9ee2 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -46,7 +46,6 @@ SUBDIR= adduser \ mixer \ mlxcontrol \ mountd \ - mount_portalfs \ mptutil \ mtest \ mtree \ diff --git a/usr.sbin/crunch/examples/really-big.conf b/usr.sbin/crunch/examples/really-big.conf index ab8a939e613af..fbd7f03f9a3e3 100644 --- a/usr.sbin/crunch/examples/really-big.conf +++ b/usr.sbin/crunch/examples/really-big.conf @@ -25,7 +25,7 @@ srcdirs /usr/src/sbin progs badsect bim clri disklabel dmesg dump dumpfs fdisk fsck halt progs ifconfig init mknod modload modunload mount mount_isofs -progs mount_lofs mount_msdosfs mount_portalfs mountd +progs mount_lofs mount_msdosfs mountd progs newfs nfsd nfsiod ping quotacheck reboot restore route routed savecore progs shutdown swapon ttyflags tunefs umount # shell scripts: fastboot -- cgit v1.3 From 890278c188e8b5372e94086a1afa988b154bc341 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Tue, 16 Oct 2012 21:34:02 +0000 Subject: Properly handle non-keyword case by setting e->second to 0. --- usr.sbin/cron/lib/entry.c | 1 + 1 file changed, 1 insertion(+) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index 9c1106a3d735f..2ed62fe702594 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -222,6 +222,7 @@ load_entry(file, error_func, pw, envp) } } else { Debug(DPARS, ("load_entry()...about to parse numerics\n")) + bit_set(e->second, 0); ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file); -- cgit v1.3 From 7bd028861b6ed5fae2ed4283df8a8ead17779d85 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Wed, 17 Oct 2012 00:44:34 +0000 Subject: o Use nanosleep(2) to sleep exact amount of time till the next second, not multiple of 1 second, which results in actual time to drift back and forth every run within 1 second of the actual action has been set for. Suggested by: Ian Lepore o Schedule the first run in 1 second after starting up, not on the boundary of the next minute, which results in the every_second jobs not being run. --- usr.sbin/cron/cron/cron.c | 54 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 011458a537f1d..5f3e7fd5c501d 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -341,37 +341,73 @@ cron_tick(db) */ static void cron_sync() { +#if 0 register struct tm *tm; +#endif - TargetTime = time((time_t*)0); + TargetTime = time((time_t*)0) + 1; +#if 0 tm = localtime(&TargetTime); TargetTime += (60 - tm->tm_sec); +#endif } +static int +timeval_subtract(struct timespec *result, struct timeval *x, struct timeval *y) +{ + int nsec; + + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > 1000000) { + nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = (x->tv_usec - y->tv_usec) * 1000; + + /* Return difference in seconds */ + return (x->tv_sec - y->tv_sec); +} static void cron_sleep(db) cron_db *db; { - int seconds_to_wait = 0; + int seconds_to_wait; + int rval; + struct timeval ctime, ttime; + struct timespec stime, remtime; /* * Loop until we reach the top of the next minute, sleep when possible. */ for (;;) { - seconds_to_wait = (int) (TargetTime - time((time_t*)0)); + gettimeofday(&ctime, NULL); + ttime.tv_sec = TargetTime; + ttime.tv_usec = 0; + timeval_subtract(&stime, &ttime, &ctime); /* * If the seconds_to_wait value is insane, jump the cron */ - if (seconds_to_wait < -600 || seconds_to_wait > 600) { + if (stime.tv_sec < -600 || stime.tv_sec > 600) { cron_clean(db); cron_sync(); continue; } + seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : stime.tv_sec; + Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", getpid(), (long)TargetTime, seconds_to_wait)) @@ -380,13 +416,19 @@ cron_sleep(db) * to run, break */ - if (seconds_to_wait <= 0) + if (stime.tv_sec < 0) break; if (job_runqueue() == 0) { Debug(DSCH, ("[%d] sleeping for %d seconds\n", getpid(), seconds_to_wait)) - sleep(seconds_to_wait); + for (;;) { + rval = nanosleep(&stime, &remtime); + if (rval == 0 || errno != EINTR) + break; + stime.tv_sec = remtime.tv_sec; + stime.tv_nsec = remtime.tv_nsec; + } } } } -- cgit v1.3 From e6116d5b8e55f021844ac2331aa533a1a114fc8f Mon Sep 17 00:00:00 2001 From: Attilio Rao Date: Wed, 17 Oct 2012 11:16:17 +0000 Subject: Disconnect non-MPSAFE NWFS from the build in preparation for dropping GIANT from VFS. In addition, disconnect also netncp, which is a base requirement for NWFS. In the possibility of a future maintenance of the code and later readd to the FreeBSD base, maybe we should think about a better location for netncp. I'm not entirely sure the / top location is actually right, however I will let network people to comment on that more specifically. This is not targeted for MFC. --- MAINTAINERS | 2 -- etc/defaults/rc.conf | 2 +- etc/mtree/BSD.include.dist | 4 ---- etc/mtree/BSD.usr.dist | 2 -- include/Makefile | 7 +------ lib/Makefile | 4 ---- lib/libprocstat/Makefile | 5 ----- lib/libprocstat/common_kvm.h | 1 - lib/libprocstat/libprocstat.c | 3 --- rescue/rescue/Makefile | 4 ---- sbin/mount/mount.8 | 2 -- sbin/mount/mount.c | 2 +- share/examples/Makefile | 3 --- share/man/man5/src.conf.5 | 2 -- share/man/man7/hier.7 | 2 -- share/mk/bsd.libnames.mk | 3 --- share/mk/bsd.own.mk | 5 ----- sys/conf/NOTES | 4 ---- sys/conf/files | 15 --------------- sys/conf/options | 2 -- sys/kern/Make.tags.inc | 1 - sys/modules/Makefile | 7 ------- usr.bin/Makefile.amd64 | 4 ---- usr.bin/Makefile.i386 | 4 ---- usr.sbin/Makefile.amd64 | 3 --- usr.sbin/Makefile.i386 | 3 --- 26 files changed, 3 insertions(+), 93 deletions(-) (limited to 'usr.sbin') diff --git a/MAINTAINERS b/MAINTAINERS index aabff8b9418fd..ecf5c1d5ae482 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -74,8 +74,6 @@ drm rnoland Just keep me informed of changes, try not to break it. unifdef(1) fanf Pre-commit review requested. ntp roberto Pre-commit review requested. inetd dwmalone Recommends pre-commit review. -nwfs bp In case of functional changes pre-commit review - requested. contrib/smbfs bp Open for in-tree committs. In case of functional changes pre-commit review requested. contrib/pf mlaier Pre-commit review requested. diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 1dad35be5bfc7..98c89e49e9790 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -92,7 +92,7 @@ fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails. fsck_y_flags="" # Additional flags for fsck -y background_fsck="YES" # Attempt to run fsck in the background where possible. background_fsck_delay="60" # Time to wait (seconds) before starting the fsck. -netfs_types="nfs:NFS oldnfs:OLDNFS smbfs:SMB nwfs:NWFS" # Net filesystems. +netfs_types="nfs:NFS oldnfs:OLDNFS smbfs:SMB" # Net filesystems. extra_netfs_types="NO" # List of network extra filesystem types for delayed # mount at startup (or NO). diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 3d918a73017f0..417b4f449d224 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -168,8 +168,6 @@ .. nullfs .. - nwfs - .. portalfs .. procfs @@ -287,8 +285,6 @@ sig .. .. - netncp - .. netsmb .. nfs diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index 6c2ee065826ab..bc3f4e758b237 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -281,8 +281,6 @@ bluetooth .. .. - nwclient - .. pc-sysinstall .. perfmon diff --git a/include/Makefile b/include/Makefile index 89bcb9117e592..65424200afac9 100644 --- a/include/Makefile +++ b/include/Makefile @@ -44,7 +44,7 @@ LSUBDIRS= cam/ata cam/scsi \ dev/ofw dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \ dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \ fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/ntfs fs/nullfs \ - ${_fs_nwfs} fs/procfs fs/smbfs fs/udf fs/unionfs \ + fs/procfs fs/smbfs fs/udf fs/unionfs \ geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \ geom/mirror geom/mountver geom/multipath geom/nop \ geom/raid geom/raid3 geom/shsec geom/stripe geom/virstor \ @@ -81,11 +81,6 @@ LSUBSUBDIRS+= netgraph/bluetooth/include _netipx= netipx #.endif -.if ${MK_NCP} != "no" -_netncp= netncp -_fs_nwfs= fs/nwfs -.endif - # Define SHARED to indicate whether you want symbolic links to the system # source (``symlinks''), or a separate copy (``copies''). ``symlinks'' is # probably only useful for developers and should be avoided if you do not diff --git a/lib/Makefile b/lib/Makefile index a366f5f84c7f2..ae2592473e2d2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -89,7 +89,6 @@ SUBDIR= ${SUBDIR_ORDERED} \ ${_libmilter} \ ${_libmp} \ ${_libnandfs} \ - ${_libncp} \ ${_libngatm} \ libopie \ libpam \ @@ -189,9 +188,6 @@ _libypclnt= libypclnt .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" -.if ${MK_NCP} != "no" -_libncp= libncp -.endif _libsmb= libsmb _libvgl= libvgl _libproc= libproc diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile index 98044834a193e..d125898be26c6 100644 --- a/lib/libprocstat/Makefile +++ b/lib/libprocstat/Makefile @@ -24,11 +24,6 @@ LDADD= -lkvm -lutil MAN= libprocstat.3 -.if ${MK_NCP} != "no" -CFLAGS+= -DLIBPROCSTAT_NWFS -SRCS+= nwfs.c -.endif - # XXX This is a hack. .if ${MK_CDDL} != "no" CFLAGS+= -DLIBPROCSTAT_ZFS diff --git a/lib/libprocstat/common_kvm.h b/lib/libprocstat/common_kvm.h index d0b5307a007eb..781863ac7b71e 100644 --- a/lib/libprocstat/common_kvm.h +++ b/lib/libprocstat/common_kvm.h @@ -42,7 +42,6 @@ int isofs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int msdosfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int ntfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); -int nwfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int smbfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int udf_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 11a817e090d2c..425aadff7fc9e 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -974,9 +974,6 @@ procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, FSTYPE(msdosfs), FSTYPE(nfs), FSTYPE(ntfs), -#ifdef LIBPROCSTAT_NWFS - FSTYPE(nwfs), -#endif FSTYPE(smbfs), FSTYPE(udf), FSTYPE(ufs), diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile index 8d1ad0f9cc784..ab88108c96b55 100644 --- a/rescue/rescue/Makefile +++ b/rescue/rescue/Makefile @@ -130,10 +130,6 @@ CRUNCH_LIBS+= -lgeom -lbsdxml -lkiconv -lmd -lsbuf -lufs -lz .if ${MACHINE_CPUARCH} == "i386" CRUNCH_PROGS_sbin+= bsdlabel sconfig fdisk CRUNCH_ALIAS_bsdlabel= disklabel -#.if ${MK_NCP} != "no" -#CRUNCH_PROGS+= mount_nwfs -#CRUNCH_LIBS+= -lncp -#.endif #CRUNCH_PROGS+= mount_smbfs #CRUNCH_LIBS+= -lsmb .endif diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 index fea0171820a30..7b819550446c6 100644 --- a/sbin/mount/mount.8 +++ b/sbin/mount/mount.8 @@ -447,7 +447,6 @@ However, for the following file system types: .Cm msdosfs , .Cm nfs , .Cm ntfs , -.Cm nwfs , .Cm nullfs , .Cm oldnfs , .Cm smbfs , @@ -546,7 +545,6 @@ support for a particular file system might be provided either on a static .Xr mount_nfs 8 , .Xr mount_ntfs 8 , .Xr mount_nullfs 8 , -.Xr mount_nwfs 8 , .Xr mount_smbfs 8 , .Xr mount_udf 8 , .Xr mount_unionfs 8 , diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c index 63d53b069d878..7b972192861da 100644 --- a/sbin/mount/mount.c +++ b/sbin/mount/mount.c @@ -143,7 +143,7 @@ use_mountprog(const char *vfstype) unsigned int i; const char *fs[] = { "cd9660", "mfs", "msdosfs", "nfs", "ntfs", - "nwfs", "nullfs", "oldnfs", "smbfs", "udf", "unionfs", + "nullfs", "oldnfs", "smbfs", "udf", "unionfs", NULL }; diff --git a/share/examples/Makefile b/share/examples/Makefile index 36887ebdb89c6..9502e9e4fe1d8 100644 --- a/share/examples/Makefile +++ b/share/examples/Makefile @@ -24,7 +24,6 @@ LDIRS= BSD_daemon \ libvgl \ mdoc \ netgraph \ - nwclient \ perfmon \ ppi \ ppp \ @@ -129,8 +128,6 @@ XFILES= BSD_daemon/FreeBSD.pfa \ netgraph/udp.tunnel \ netgraph/virtual.chain \ netgraph/virtual.lan \ - nwclient/dot.nwfsrc \ - nwclient/nwfs.sh.sample \ perfmon/Makefile \ perfmon/README \ perfmon/perfmon.c \ diff --git a/share/man/man5/src.conf.5 b/share/man/man5/src.conf.5 index db26ac133aa6c..97f79ab948366 100644 --- a/share/man/man5/src.conf.5 +++ b/share/man/man5/src.conf.5 @@ -547,8 +547,6 @@ When set, it also enforces the following options: .Bl -item -compact .It .Va WITHOUT_IPX_SUPPORT -.It -.Va WITHOUT_NCP .El .It Va WITHOUT_IPX_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_IPX_SUPPORT 156932 2006-03-21 07:50:50Z ru diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7 index 856d9462b9217..e6da88241c7b7 100644 --- a/share/man/man7/hier.7 +++ b/share/man/man7/hier.7 @@ -256,8 +256,6 @@ C include files for NFS (Network File System) version 2, 3 and 4 NTFS file system .It Pa nullfs/ loopback file system -.It Pa nwfs/ -NetWare file system .It Pa procfs/ process file system .It Pa smbfs/ diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index 075833b2c6730..aa78dd8509a31 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -97,9 +97,6 @@ LIBMENU?= ${DESTDIR}${LIBDIR}/libmenu.a LIBMILTER?= ${DESTDIR}${LIBDIR}/libmilter.a .endif LIBMP?= ${DESTDIR}${LIBDIR}/libmp.a -.if ${MK_NCP} != "no" -LIBNCP?= ${DESTDIR}${LIBDIR}/libncp.a -.endif LIBNCURSES?= ${DESTDIR}${LIBDIR}/libncurses.a LIBNCURSESW?= ${DESTDIR}${LIBDIR}/libncursesw.a LIBNETGRAPH?= ${DESTDIR}${LIBDIR}/libnetgraph.a diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index db2e7659bd4af..2c1c04e3e97dd 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -374,7 +374,6 @@ __DEFAULT_YES_OPTIONS = \ MAILWRAPPER \ MAKE \ MAN \ - NCP \ NDIS \ NETCAT \ NETGRAPH \ @@ -550,10 +549,6 @@ MK_CLANG:= no MK_GROFF:= no .endif -.if ${MK_IPX} == "no" -MK_NCP:= no -.endif - .if ${MK_MAIL} == "no" MK_MAILWRAPPER:= no MK_SENDMAIL:= no diff --git a/sys/conf/NOTES b/sys/conf/NOTES index fb8235649964b..c7802ef772058 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -570,8 +570,6 @@ options IPSEC_NAT_T #NAT-T support, UDP encap of ESP options IPX #IPX/SPX communications protocols -options NCP #NetWare Core protocol - options NETATALK #Appletalk communications protocols options NETATALKDEBUG #Appletalk debugging @@ -1014,8 +1012,6 @@ options KGSSAPI #Kernel GSSAPI implementation options NTFS options NULLFS #NULL filesystem -# Broken (depends on NCP): -#options NWFS #NetWare filesystem options PROCFS #Process filesystem (requires PSEUDOFS) options PSEUDOFS #Pseudo-filesystem framework options PSEUDOFS_TRACE #Debugging support for PSEUDOFS diff --git a/sys/conf/files b/sys/conf/files index e2aedee9359fc..d734228286ced 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2375,12 +2375,6 @@ fs/ntfs/ntfs_vnops.c optional ntfs fs/nullfs/null_subr.c optional nullfs fs/nullfs/null_vfsops.c optional nullfs fs/nullfs/null_vnops.c optional nullfs -fs/nwfs/nwfs_io.c optional nwfs -fs/nwfs/nwfs_ioctl.c optional nwfs -fs/nwfs/nwfs_node.c optional nwfs -fs/nwfs/nwfs_subr.c optional nwfs -fs/nwfs/nwfs_vfsops.c optional nwfs -fs/nwfs/nwfs_vnops.c optional nwfs fs/procfs/procfs.c optional procfs fs/procfs/procfs_ctl.c optional procfs fs/procfs/procfs_dbregs.c optional procfs @@ -3141,15 +3135,6 @@ netipx/spx_usrreq.c optional ipx netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm netnatm/natm_proto.c optional natm -netncp/ncp_conn.c optional ncp -netncp/ncp_crypt.c optional ncp -netncp/ncp_login.c optional ncp -netncp/ncp_mod.c optional ncp -netncp/ncp_ncp.c optional ncp -netncp/ncp_nls.c optional ncp -netncp/ncp_rq.c optional ncp -netncp/ncp_sock.c optional ncp -netncp/ncp_subr.c optional ncp netpfil/ipfw/dn_heap.c optional inet dummynet netpfil/ipfw/dn_sched_fifo.c optional inet dummynet netpfil/ipfw/dn_sched_prio.c optional inet dummynet diff --git a/sys/conf/options b/sys/conf/options index 246ac0ed8c59b..2abd08c85fe51 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -214,7 +214,6 @@ MSDOSFS opt_dontuse.h NANDFS opt_dontuse.h NTFS opt_dontuse.h NULLFS opt_dontuse.h -NWFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h REISERFS opt_dontuse.h @@ -423,7 +422,6 @@ LIBMCHAIN MBUF_PROFILING MBUF_STRESS_TEST MROUTING opt_mrouting.h -NCP NETATALK opt_atalk.h NFSLOCKD PCBGROUP opt_pcbgroup.h diff --git a/sys/kern/Make.tags.inc b/sys/kern/Make.tags.inc index 28dc0f77aa8cb..079bab555cba9 100644 --- a/sys/kern/Make.tags.inc +++ b/sys/kern/Make.tags.inc @@ -30,7 +30,6 @@ COMM= ${SYS}/dev/advansys/*.[ch] \ ${SYS}/fs/msdosfs/*.[ch] \ ${SYS}/fs/ntfs/*.[ch] \ ${SYS}/fs/nullfs/*.[ch] \ - ${SYS}/fs/nwfs/*.[ch] \ ${SYS}/fs/procfs/*.[ch] \ ${SYS}/fs/smbfs/*.[ch] \ ${SYS}/fs/udf/*.[ch] \ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 77d2bf4df69dd..450b6614def7e 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -247,7 +247,6 @@ SUBDIR= \ ${_nve} \ ${_nvme} \ ${_nvram} \ - ${_nwfs} \ ${_nxge} \ ${_opensolaris} \ oce \ @@ -504,15 +503,9 @@ _mlx4ib= mlx4ib _mlxen= mlxen _mthca= mthca .endif -.if ${MK_NCP} != "no" -_ncp= ncp -.endif _ncv= ncv _ndis= ndis _nsp= nsp -.if ${MK_NCP} != "no" -_nwfs= nwfs -.endif .if ${MK_CDDL} != "no" || defined(ALL_MODULES) _opensolaris= opensolaris .endif diff --git a/usr.bin/Makefile.amd64 b/usr.bin/Makefile.amd64 index 2eba205a7a50b..fee2fb0768118 100644 --- a/usr.bin/Makefile.amd64 +++ b/usr.bin/Makefile.amd64 @@ -1,7 +1,3 @@ # $FreeBSD$ -.if ${MK_NCP} != "no" -SUBDIR+= ncplist -SUBDIR+= ncplogin -.endif SUBDIR+= smbutil diff --git a/usr.bin/Makefile.i386 b/usr.bin/Makefile.i386 index 2eba205a7a50b..fee2fb0768118 100644 --- a/usr.bin/Makefile.i386 +++ b/usr.bin/Makefile.i386 @@ -1,7 +1,3 @@ # $FreeBSD$ -.if ${MK_NCP} != "no" -SUBDIR+= ncplist -SUBDIR+= ncplogin -.endif SUBDIR+= smbutil diff --git a/usr.sbin/Makefile.amd64 b/usr.sbin/Makefile.amd64 index 232831b35c8c0..2856da23ce9c5 100644 --- a/usr.sbin/Makefile.amd64 +++ b/usr.sbin/Makefile.amd64 @@ -17,9 +17,6 @@ SUBDIR+= btxld SUBDIR+= cpucontrol SUBDIR+= kgmon SUBDIR+= lptcontrol -.if ${MK_NCP} != "no" -SUBDIR+= mount_nwfs -.endif SUBDIR+= mount_smbfs SUBDIR+= mptable .if ${MK_NDIS} != "no" diff --git a/usr.sbin/Makefile.i386 b/usr.sbin/Makefile.i386 index f7870dc714b45..800626c06a4b5 100644 --- a/usr.sbin/Makefile.i386 +++ b/usr.sbin/Makefile.i386 @@ -12,9 +12,6 @@ SUBDIR+= cpucontrol SUBDIR+= kgmon SUBDIR+= kgzip SUBDIR+= lptcontrol -.if ${MK_NCP} != "no" -SUBDIR+= mount_nwfs -.endif SUBDIR+= mount_smbfs SUBDIR+= mptable .if ${MK_NDIS} != "no" -- cgit v1.3 From 07e9aac0989a891b02fb00fba98892efd674acb9 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Wed, 17 Oct 2012 20:45:48 +0000 Subject: Revert latest changes to cron, until better version is worked out (I hope). Requested by: few --- usr.sbin/cron/cron/cron.c | 26 +++++++++----------------- usr.sbin/cron/cron/cron.h | 6 +----- usr.sbin/cron/crontab/crontab.5 | 2 -- usr.sbin/cron/lib/entry.c | 21 --------------------- 4 files changed, 10 insertions(+), 45 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 5f3e7fd5c501d..9b4515c598c71 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -98,7 +98,6 @@ main(argc, argv) char *argv[]; { cron_db database; - int runnum; ProgramName = argv[0]; @@ -150,24 +149,21 @@ main(argc, argv) load_database(&database); run_reboot_jobs(&database); cron_sync(); - runnum = 0; while (TRUE) { # if DEBUGGING /* if (!(DebugFlags & DTEST)) */ # endif /*DEBUGGING*/ cron_sleep(&database); - if (runnum % 60 == 0) - load_database(&database); + load_database(&database); /* do this iteration */ cron_tick(&database); - /* sleep 1 second + /* sleep 1 minute */ - TargetTime += 1; - runnum += 1; + TargetTime += 60; } } @@ -198,23 +194,22 @@ cron_tick(db) static time_t diff = 0, /* time difference in seconds from the last offset change */ difflimit = 0; /* end point for the time zone correction */ struct tm otztm; /* time in the old time zone */ - int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; + int otzminute, otzhour, otzdom, otzmonth, otzdow; register struct tm *tm = localtime(&TargetTime); - register int second, minute, hour, dom, month, dow; + register int minute, hour, dom, month, dow; register user *u; register entry *e; /* make 0-based values out of these so we can use them as indicies */ - second = tm->tm_sec -FIRST_SECOND; minute = tm->tm_min -FIRST_MINUTE; hour = tm->tm_hour -FIRST_HOUR; dom = tm->tm_mday -FIRST_DOM; month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; dow = tm->tm_wday -FIRST_DOW; - Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", - getpid(), second, minute, hour, dom, month, dow)) + Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", + getpid(), minute, hour, dom, month, dow)) if (dst_enabled && last_time != 0 && TargetTime > last_time /* exclude stepping back */ @@ -267,7 +262,6 @@ cron_tick(db) /* make 0-based values out of these so we can use them as indicies */ - otzsecond = otztm.tm_sec -FIRST_SECOND; otzminute = otztm.tm_min -FIRST_MINUTE; otzhour = otztm.tm_hour -FIRST_HOUR; otzdom = otztm.tm_mday -FIRST_DOM; @@ -289,8 +283,7 @@ cron_tick(db) e->uid, e->gid, e->cmd)) if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { - if (bit_test(e->second, otzsecond) - && bit_test(e->minute, otzminute) + if (bit_test(e->minute, otzminute) && bit_test(e->hour, otzhour) && bit_test(e->month, otzmonth) && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) @@ -309,8 +302,7 @@ cron_tick(db) continue; } - if (bit_test(e->second, second) - && bit_test(e->minute, minute) + if (bit_test(e->minute, minute) && bit_test(e->hour, hour) && bit_test(e->month, month) && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h index 387c72bbed090..a2413cbd21fc1 100644 --- a/usr.sbin/cron/cron/cron.h +++ b/usr.sbin/cron/cron/cron.h @@ -124,10 +124,6 @@ LineNumber = ln; \ } -#define FIRST_SECOND 0 -#define LAST_SECOND 59 -#define SECOND_COUNT (LAST_SECOND - FIRST_SECOND + 1) - #define FIRST_MINUTE 0 #define LAST_MINUTE 59 #define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) @@ -169,7 +165,6 @@ typedef struct _entry { #endif char **envp; char *cmd; - bitstr_t bit_decl(second, SECOND_COUNT); bitstr_t bit_decl(minute, MINUTE_COUNT); bitstr_t bit_decl(hour, HOUR_COUNT); bitstr_t bit_decl(dom, DOM_COUNT); @@ -181,6 +176,7 @@ typedef struct _entry { #define WHEN_REBOOT 0x04 #define RUN_AT 0x08 #define NOT_UNTIL 0x10 +#define SECONDS_COUNT 0x20 time_t lastrun; } entry; diff --git a/usr.sbin/cron/crontab/crontab.5 b/usr.sbin/cron/crontab/crontab.5 index 9be37df264e5f..458175e7a48d2 100644 --- a/usr.sbin/cron/crontab/crontab.5 +++ b/usr.sbin/cron/crontab/crontab.5 @@ -232,8 +232,6 @@ string meaning @daily Run once a day, "0 0 * * *". @midnight (same as @daily) @hourly Run once an hour, "0 * * * *". -@every_minute Run once a minute, "*/1 * * * *". -@every_second Run once a second. .Ed .Sh EXAMPLE CRON FILE .Bd -literal diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index 2ed62fe702594..7a725c5703416 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -151,7 +151,6 @@ load_entry(file, error_func, pw, envp) e->flags |= WHEN_REBOOT; } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ Debug(DPARS, ("load_entry()...yearly shortcut\n")) - bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); @@ -160,7 +159,6 @@ load_entry(file, error_func, pw, envp) e->flags |= DOW_STAR; } else if (!strcmp("monthly", cmd)) { Debug(DPARS, ("load_entry()...monthly shortcut\n")) - bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); @@ -169,7 +167,6 @@ load_entry(file, error_func, pw, envp) e->flags |= DOW_STAR; } else if (!strcmp("weekly", cmd)) { Debug(DPARS, ("load_entry()...weekly shortcut\n")) - bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); @@ -178,7 +175,6 @@ load_entry(file, error_func, pw, envp) bit_set(e->dow, 0); } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { Debug(DPARS, ("load_entry()...daily shortcut\n")) - bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); @@ -186,27 +182,11 @@ load_entry(file, error_func, pw, envp) bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("hourly", cmd)) { Debug(DPARS, ("load_entry()...hourly shortcut\n")) - bit_set(e->second, 0); bit_set(e->minute, 0); bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); - } else if (!strcmp("every_minute", cmd)) { - Debug(DPARS, ("load_entry()...every_minute shortcut\n")) - bit_set(e->second, 0); - bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); - bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); - bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); - bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); - bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); - } else if (!strcmp("every_second", cmd)) { - Debug(DPARS, ("load_entry()...every_second shortcut\n")) - bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1)); - bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); - bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); - bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); - bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else { ecode = e_timespec; @@ -222,7 +202,6 @@ load_entry(file, error_func, pw, envp) } } else { Debug(DPARS, ("load_entry()...about to parse numerics\n")) - bit_set(e->second, 0); ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file); -- cgit v1.3 From 8675ac22d6acf10cd6c837f8b008176b73be3ef3 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Wed, 17 Oct 2012 21:26:35 +0000 Subject: Remove harmless, but superfluous local change that creeped in along with the r241649. --- usr.sbin/cron/cron/cron.h | 1 - 1 file changed, 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h index a2413cbd21fc1..38eb33b0c6f58 100644 --- a/usr.sbin/cron/cron/cron.h +++ b/usr.sbin/cron/cron/cron.h @@ -176,7 +176,6 @@ typedef struct _entry { #define WHEN_REBOOT 0x04 #define RUN_AT 0x08 #define NOT_UNTIL 0x10 -#define SECONDS_COUNT 0x20 time_t lastrun; } entry; -- cgit v1.3 From 9da4528e6f2ce3e51dbd9a36d0d9280975c3d49e Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Wed, 17 Oct 2012 21:48:45 +0000 Subject: Ask to become root via sudo(8) and give user ability to save this preference. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/include/messages.subr | 6 ++++ usr.sbin/bsdconfig/share/mustberoot.subr | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr index fd3844028ab8f..26d41881123ba 100644 --- a/usr.sbin/bsdconfig/include/messages.subr +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -29,8 +29,12 @@ field_username="Username:" field_password="Password:" hline_alnum_punc_tab_enter="Use alpha-numeric, punctuation, TAB or ENTER" hline_arrows_tab_enter="Press arrows, TAB or ENTER" +msg_always_try_sudo_when_run_as="Always try sudo(8) when run as %s" +msg_becoming_root_via_sudo="Becoming root via sudo(8)..." msg_cancel="Cancel" +msg_cancel_exit="Cancel/Exit" msg_cannot_create_permission_denied="%s: cannot create %s: Permission denied" +msg_created_path="Created %s" msg_directory_not_found="%s: Directory not found." msg_exit="Exit" msg_exit_bsdconfig="Exit bsdconfig" @@ -51,6 +55,8 @@ msg_previous_syntax_errors="%s: Not overwriting \`%s' due to previous syntax err msg_secure_mode_requires_x11="Secure-mode requires X11 (use \`-X')!" msg_secure_mode_requires_root="Secure-mode requires root-access!" msg_sorry_try_again="Sorry, try again." +msg_try_sudo_only_this_once="Try sudo(8) only this once" msg_unknown_user="Unknown user: %s" msg_user_disallowed="User disallowed: %s" msg_yes="Yes" +msg_you_are_not_root_but="You are not root but %s can use sudo(8).\nWhat would you like to do?" diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr index c6514c0d4b1ea..fd37c641db8f9 100644 --- a/usr.sbin/bsdconfig/share/mustberoot.subr +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -82,6 +82,65 @@ f_become_root_via_sudo() f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" + # + # Ask the user if it's OK to become root via sudo(8) and give them + # the option to save this preference (by touch(1)ing a file in the + # user's $HOME directory). + # + local checkpath="${HOME%/}/.bsdconfig_uses_sudo" + if [ ! -e "$checkpath" ]; then + msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" ) + local menu_list=" + 'X' '$msg_cancel_exit' + '1' '$msg' + '2' '$msg_try_sudo_only_this_once' + " # END-QUOTE + msg=$( printf "$msg_you_are_not_root_but" bsdconfig ) + hline="$hline_arrows_tab_enter" + size=$( eval f_dialog_menu_size \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$msg\" \ + \"\$hline\" \ + $menu_list ) + + local dialog_menu mtag retval + dialog_menu=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --menu \"\$msg\" $size \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + retval=$? + setvar DIALOG_MENU_$$ "$dialog_menu" + mtag=$( f_dialog_menutag ) + + [ $retval -eq 0 ] || f_die + + case "$mtag" in + X) # Cancel/Exit + f_die ;; + 1) # Always try sudo(8) when run as $user + local err + if ! err=$( touch "$checkpath" 2>&1 ); then + f_show_msg "%s" "$err" + else + f_show_msg "$msg_created_path" "$checkpath" + fi + esac + else + # + # This user has created the path signing-off on sudo(8)-use + # but let's still give them a short/quick/unobtrusive reminder + # + f_dialog_info "$msg_becoming_root_via_sudo" + [ "$USE_XDIALOG" ] || sleep 0.6 + fi + # # Check sudo(8) access before prompting for password. # -- cgit v1.3 From 27858d0bb46d932b41541a09aa1559315f39ac3f Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Thu, 18 Oct 2012 06:27:03 +0000 Subject: Fully backout latest changes. Pointy hat to: sobomax --- usr.sbin/cron/cron/cron.c | 54 ++++++----------------------------------------- usr.sbin/cron/lib/entry.c | 1 - 2 files changed, 6 insertions(+), 49 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 9b4515c598c71..8524ff9dfcb83 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -333,73 +333,37 @@ cron_tick(db) */ static void cron_sync() { -#if 0 register struct tm *tm; -#endif - TargetTime = time((time_t*)0) + 1; -#if 0 + TargetTime = time((time_t*)0); tm = localtime(&TargetTime); TargetTime += (60 - tm->tm_sec); -#endif } -static int -timeval_subtract(struct timespec *result, struct timeval *x, struct timeval *y) -{ - int nsec; - - /* Perform the carry for the later subtraction by updating y. */ - if (x->tv_usec < y->tv_usec) { - nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; - y->tv_usec -= 1000000 * nsec; - y->tv_sec += nsec; - } - if (x->tv_usec - y->tv_usec > 1000000) { - nsec = (x->tv_usec - y->tv_usec) / 1000000; - y->tv_usec += 1000000 * nsec; - y->tv_sec -= nsec; - } - - /* tv_nsec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_nsec = (x->tv_usec - y->tv_usec) * 1000; - - /* Return difference in seconds */ - return (x->tv_sec - y->tv_sec); -} static void cron_sleep(db) cron_db *db; { - int seconds_to_wait; - int rval; - struct timeval ctime, ttime; - struct timespec stime, remtime; + int seconds_to_wait = 0; /* * Loop until we reach the top of the next minute, sleep when possible. */ for (;;) { - gettimeofday(&ctime, NULL); - ttime.tv_sec = TargetTime; - ttime.tv_usec = 0; - timeval_subtract(&stime, &ttime, &ctime); + seconds_to_wait = (int) (TargetTime - time((time_t*)0)); /* * If the seconds_to_wait value is insane, jump the cron */ - if (stime.tv_sec < -600 || stime.tv_sec > 600) { + if (seconds_to_wait < -600 || seconds_to_wait > 600) { cron_clean(db); cron_sync(); continue; } - seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : stime.tv_sec; - Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", getpid(), (long)TargetTime, seconds_to_wait)) @@ -408,19 +372,13 @@ cron_sleep(db) * to run, break */ - if (stime.tv_sec < 0) + if (seconds_to_wait <= 0) break; if (job_runqueue() == 0) { Debug(DSCH, ("[%d] sleeping for %d seconds\n", getpid(), seconds_to_wait)) - for (;;) { - rval = nanosleep(&stime, &remtime); - if (rval == 0 || errno != EINTR) - break; - stime.tv_sec = remtime.tv_sec; - stime.tv_nsec = remtime.tv_nsec; - } + sleep(seconds_to_wait); } } } diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index 7a725c5703416..ea015da62704e 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -187,7 +187,6 @@ load_entry(file, error_func, pw, envp) bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); - bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else { ecode = e_timespec; goto eof; -- cgit v1.3 From 7f72bb440580489ec6a115320c9e18f4d60d347f Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Thu, 18 Oct 2012 07:55:09 +0000 Subject: Fix typo. Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/share/common.subr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 5305a85e72318..667a8f60092c4 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -156,7 +156,7 @@ f_include() # f_include_lang $file # # Include a language file. Automatically takes $LANG and $LC_ALL into -# considerationg when including $file (suffix ".$LC_ALL" or ".$LANG" will +# consideration when including $file (suffix ".$LC_ALL" or ".$LANG" will # automatically by added prior to loading the language file). # # No error is produced if (a) a language has been requested (by setting either -- cgit v1.3 From 2e564269d0af3703815a10effe03c1f1773c9a46 Mon Sep 17 00:00:00 2001 From: Attilio Rao Date: Thu, 18 Oct 2012 12:04:56 +0000 Subject: Disconnect non-MPSAFE SMBFS from the build in preparation for dropping GIANT from VFS. In addition, disconnect also netsmb, which is a base requirement for SMBFS. In the while SMBFS regular users can use FUSE interface and smbnetfs port to work with their SMBFS partitions. Also, there are ongoing efforts by vendor to support in-kernel smbfs, so there are good chances that it will get relinked once properly locked. This is not targeted for MFC. --- etc/defaults/rc.conf | 2 +- etc/mtree/BSD.include.dist | 2 -- etc/mtree/BSD.usr.dist | 4 --- include/Makefile | 5 ++-- lib/Makefile | 11 -------- lib/libprocstat/Makefile | 1 - lib/libprocstat/common_kvm.h | 1 - lib/libprocstat/libprocstat.c | 1 - rescue/rescue/Makefile | 2 -- sbin/mount/mount.8 | 2 -- sbin/mount/mount.c | 2 +- share/examples/Makefile | 1 - share/examples/etc/README.examples | 1 - share/mk/bsd.libnames.mk | 1 - sys/conf/NOTES | 7 ----- sys/conf/files | 20 ++------------ sys/conf/files.amd64 | 2 +- sys/conf/files.arm | 2 +- sys/conf/files.i386 | 2 +- sys/conf/files.ia64 | 2 +- sys/conf/files.mips | 2 +- sys/conf/files.pc98 | 2 +- sys/conf/files.powerpc | 2 +- sys/conf/files.sparc64 | 2 +- sys/conf/options | 4 --- sys/kern/Make.tags.inc | 1 - sys/kern/sys_generic.c | 56 -------------------------------------- sys/modules/Makefile | 6 ---- sys/sys/socketvar.h | 2 -- usr.bin/Makefile.amd64 | 1 - usr.bin/Makefile.i386 | 1 - usr.bin/Makefile.ia64 | 1 - usr.bin/Makefile.powerpc | 1 - usr.bin/Makefile.sparc64 | 1 - usr.bin/kdump/kdump.c | 2 -- usr.sbin/Makefile.amd64 | 1 - usr.sbin/Makefile.i386 | 1 - usr.sbin/Makefile.ia64 | 1 - usr.sbin/Makefile.powerpc | 1 - usr.sbin/Makefile.sparc64 | 1 - 40 files changed, 14 insertions(+), 146 deletions(-) (limited to 'usr.sbin') diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 98c89e49e9790..0efaaa15da646 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -92,7 +92,7 @@ fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails. fsck_y_flags="" # Additional flags for fsck -y background_fsck="YES" # Attempt to run fsck in the background where possible. background_fsck_delay="60" # Time to wait (seconds) before starting the fsck. -netfs_types="nfs:NFS oldnfs:OLDNFS smbfs:SMB" # Net filesystems. +netfs_types="nfs:NFS oldnfs:OLDNFS" # Net filesystems. extra_netfs_types="NO" # List of network extra filesystem types for delayed # mount at startup (or NO). diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 5a2b6aed7bb42..81f0e5bf57dcb 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -170,8 +170,6 @@ .. procfs .. - smbfs - .. udf .. unionfs diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index bc3f4e758b237..83d20a890e8a1 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -307,10 +307,6 @@ srcs .. .. - smbfs - print - .. - .. sunrpc dir .. diff --git a/include/Makefile b/include/Makefile index 03a085f960a6a..161dcf05836c2 100644 --- a/include/Makefile +++ b/include/Makefile @@ -34,7 +34,7 @@ LHDRS= aio.h errno.h fcntl.h linker_set.h poll.h stdint.h syslog.h \ ucontext.h LDIRS= bsm cam geom net net80211 netatalk netgraph netinet netinet6 \ - netipsec ${_netipx} netnatm netsmb \ + netipsec ${_netipx} netnatm \ nfs nfsclient nfsserver \ sys vm @@ -44,7 +44,7 @@ LSUBDIRS= cam/ata cam/scsi \ dev/ofw dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \ dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \ fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/nullfs \ - fs/procfs fs/smbfs fs/udf fs/unionfs \ + fs/procfs fs/udf fs/unionfs \ geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \ geom/mirror geom/mountver geom/multipath geom/nop \ geom/raid geom/raid3 geom/shsec geom/stripe geom/virstor \ @@ -76,7 +76,6 @@ INCS+= iconv.h LSUBSUBDIRS+= netgraph/bluetooth/include .endif -# XXX unconditionally needed by #.if ${MK_IPX} != "no" _netipx= netipx #.endif diff --git a/lib/Makefile b/lib/Makefile index ae2592473e2d2..431abf4f287bd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -100,7 +100,6 @@ SUBDIR= ${SUBDIR_ORDERED} \ ${_librtld_db} \ ${_libsdp} \ ${_libsm} \ - ${_libsmb} \ ${_libsmdb} \ ${_libsmutil} \ libstand \ @@ -188,7 +187,6 @@ _libypclnt= libypclnt .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" -_libsmb= libsmb _libvgl= libvgl _libproc= libproc _librtld_db= librtld_db @@ -196,7 +194,6 @@ _librtld_db= librtld_db .if ${MACHINE_CPUARCH} == "ia64" _libefi= libefi -_libsmb= libsmb .endif .if ${MACHINE_CPUARCH} == "mips" @@ -204,14 +201,6 @@ _libproc= libproc _librtld_db= librtld_db .endif -.if ${MACHINE_CPUARCH} == "powerpc" -_libsmb= libsmb -.endif - -.if ${MACHINE_CPUARCH} == "sparc64" -_libsmb= libsmb -.endif - .if ${MK_OPENSSL} != "no" _libmp= libmp .endif diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile index fade43b97b053..a29afc7a7d8de 100644 --- a/lib/libprocstat/Makefile +++ b/lib/libprocstat/Makefile @@ -8,7 +8,6 @@ SRCS= cd9660.c \ common_kvm.c \ libprocstat.c \ msdosfs.c \ - smbfs.c \ udf.c VERSION_DEF= ${.CURDIR}/Versions.def diff --git a/lib/libprocstat/common_kvm.h b/lib/libprocstat/common_kvm.h index 06627bff448c9..d5e08e1cfc09c 100644 --- a/lib/libprocstat/common_kvm.h +++ b/lib/libprocstat/common_kvm.h @@ -41,7 +41,6 @@ int devfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int isofs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int msdosfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); -int smbfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int udf_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 7ff1d53c63a43..9d9c111f7d8cc 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -973,7 +973,6 @@ procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, FSTYPE(isofs), FSTYPE(msdosfs), FSTYPE(nfs), - FSTYPE(smbfs), FSTYPE(udf), FSTYPE(ufs), #ifdef LIBPROCSTAT_ZFS diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile index b617353391596..62db656ca75fb 100644 --- a/rescue/rescue/Makefile +++ b/rescue/rescue/Makefile @@ -130,8 +130,6 @@ CRUNCH_LIBS+= -lgeom -lbsdxml -lkiconv -lmd -lsbuf -lufs -lz .if ${MACHINE_CPUARCH} == "i386" CRUNCH_PROGS_sbin+= bsdlabel sconfig fdisk CRUNCH_ALIAS_bsdlabel= disklabel -#CRUNCH_PROGS+= mount_smbfs -#CRUNCH_LIBS+= -lsmb .endif .if ${MACHINE} == "pc98" diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 index cda2f4c06d572..6bc4b5cc565b5 100644 --- a/sbin/mount/mount.8 +++ b/sbin/mount/mount.8 @@ -448,7 +448,6 @@ However, for the following file system types: .Cm nfs , .Cm nullfs , .Cm oldnfs , -.Cm smbfs , .Cm udf , and .Cm unionfs . @@ -543,7 +542,6 @@ support for a particular file system might be provided either on a static .Xr mount_msdosfs 8 , .Xr mount_nfs 8 , .Xr mount_nullfs 8 , -.Xr mount_smbfs 8 , .Xr mount_udf 8 , .Xr mount_unionfs 8 , .Xr umount 8 , diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c index a69c673f07119..1984eaced0f7b 100644 --- a/sbin/mount/mount.c +++ b/sbin/mount/mount.c @@ -143,7 +143,7 @@ use_mountprog(const char *vfstype) unsigned int i; const char *fs[] = { "cd9660", "mfs", "msdosfs", "nfs", - "nullfs", "oldnfs", "smbfs", "udf", "unionfs", + "nullfs", "oldnfs", "udf", "unionfs", NULL }; diff --git a/share/examples/Makefile b/share/examples/Makefile index 9502e9e4fe1d8..d3fab14b276ce 100644 --- a/share/examples/Makefile +++ b/share/examples/Makefile @@ -242,7 +242,6 @@ etc-examples: .endif .if ${SHARED} != "symlinks" -SUBDIR= smbfs .if ${MK_IPFILTER} != "no" SUBDIR+=ipfilter .endif diff --git a/share/examples/etc/README.examples b/share/examples/etc/README.examples index f7bf4ceb8cfb3..d703edbf56816 100644 --- a/share/examples/etc/README.examples +++ b/share/examples/etc/README.examples @@ -40,7 +40,6 @@ netstart - network startup script run from /etc/rc network.subr - routines for network configuration scripts networks - see networks(5) newsyslog.conf - configuration for system log file rotator newsyslog(8) -nsmb.conf - smbfs lookups configuration file opieaccess - OPIE database of trusted networks pf.conf - pf(4) example configuration file pf.os - SYN fingerprint database diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index aa78dd8509a31..f74959f6a26aa 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -143,7 +143,6 @@ LIBRT?= ${DESTDIR}${LIBDIR}/librt.a LIBRTLD_DB?= ${DESTDIR}${LIBDIR}/librtld_db.a LIBSBUF?= ${DESTDIR}${LIBDIR}/libsbuf.a LIBSDP?= ${DESTDIR}${LIBDIR}/libsdp.a -LIBSMB?= ${DESTDIR}${LIBDIR}/libsmb.a LIBSSH?= ${DESTDIR}${LIBDIR}/libssh.a LIBSSL?= ${DESTDIR}${LIBDIR}/libssl.a LIBSTAND?= ${DESTDIR}${LIBDIR}/libstand.a diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 3d1d9e94fa6e4..f16bf70b9e82b 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -573,12 +573,6 @@ options IPX #IPX/SPX communications protocols options NETATALK #Appletalk communications protocols options NETATALKDEBUG #Appletalk debugging -# -# SMB/CIFS requester -# NETSMB enables support for SMB protocol, it requires LIBMCHAIN and LIBICONV -# options. -options NETSMB #SMB/CIFS requester - # mchain library. It can be either loaded as KLD or compiled into kernel options LIBMCHAIN @@ -1010,7 +1004,6 @@ options NULLFS #NULL filesystem options PROCFS #Process filesystem (requires PSEUDOFS) options PSEUDOFS #Pseudo-filesystem framework options PSEUDOFS_TRACE #Debugging support for PSEUDOFS -options SMBFS #SMB/CIFS filesystem options TMPFS #Efficient memory filesystem options UDF #Universal Disk Format options UNIONFS #Union filesystem diff --git a/sys/conf/files b/sys/conf/files index 4d18d7f9ed7bb..197956c5fc6da 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -517,8 +517,8 @@ crypto/blowfish/bf_ecb.c optional ipsec crypto/blowfish/bf_skey.c optional crypto | ipsec crypto/camellia/camellia.c optional crypto | ipsec crypto/camellia/camellia-api.c optional crypto | ipsec -crypto/des/des_ecb.c optional crypto | ipsec | netsmb -crypto/des/des_setkey.c optional crypto | ipsec | netsmb +crypto/des/des_ecb.c optional crypto | ipsec +crypto/des/des_setkey.c optional crypto | ipsec crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \ ipsec | random | wlan_ccmp @@ -2386,12 +2386,6 @@ fs/pseudofs/pseudofs.c optional pseudofs fs/pseudofs/pseudofs_fileno.c optional pseudofs fs/pseudofs/pseudofs_vncache.c optional pseudofs fs/pseudofs/pseudofs_vnops.c optional pseudofs -fs/smbfs/smbfs_io.c optional smbfs -fs/smbfs/smbfs_node.c optional smbfs -fs/smbfs/smbfs_smb.c optional smbfs -fs/smbfs/smbfs_subr.c optional smbfs -fs/smbfs/smbfs_vfsops.c optional smbfs -fs/smbfs/smbfs_vnops.c optional smbfs fs/udf/osta.c optional udf fs/udf/udf_iconv.c optional udf_iconv fs/udf/udf_vfsops.c optional udf @@ -2626,7 +2620,6 @@ kern/kern_uuid.c standard kern/kern_xxx.c standard kern/link_elf.c standard kern/linker_if.m standard -kern/md4c.c optional netsmb kern/md5c.c standard kern/p1003_1b.c standard kern/posix4_mib.c standard @@ -3155,15 +3148,6 @@ netpfil/pf/pf_osfp.c optional pf inet netpfil/pf/pf_ruleset.c optional pf inet netpfil/pf/pf_table.c optional pf inet netpfil/pf/in4_cksum.c optional pf inet -netsmb/smb_conn.c optional netsmb -netsmb/smb_crypt.c optional netsmb -netsmb/smb_dev.c optional netsmb -netsmb/smb_iod.c optional netsmb -netsmb/smb_rq.c optional netsmb -netsmb/smb_smb.c optional netsmb -netsmb/smb_subr.c optional netsmb -netsmb/smb_trantcp.c optional netsmb -netsmb/smb_usr.c optional netsmb nfs/bootp_subr.c optional bootp nfsclient | bootp nfscl nfs/krpc_subr.c optional bootp nfsclient | bootp nfscl nfs/nfs_common.c optional nfsclient | nfsserver diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index 66f7df646797d..fec31f7799063 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -136,7 +136,7 @@ crypto/aesni/aeskeys_amd64.S optional aesni crypto/aesni/aesni.c optional aesni crypto/aesni/aesni_wrap.c optional aesni crypto/blowfish/bf_enc.c optional crypto | ipsec -crypto/des/des_enc.c optional crypto | ipsec | netsmb +crypto/des/des_enc.c optional crypto | ipsec crypto/via/padlock.c optional padlock crypto/via/padlock_cipher.c optional padlock crypto/via/padlock_hash.c optional padlock diff --git a/sys/conf/files.arm b/sys/conf/files.arm index 84ca8faca7980..775dfcb0e5dc6 100644 --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -5,7 +5,7 @@ font.h optional sc \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" crypto/blowfish/bf_enc.c optional crypto | ipsec -crypto/des/des_enc.c optional crypto | ipsec | netsmb +crypto/des/des_enc.c optional crypto | ipsec arm/arm/autoconf.c standard arm/arm/bcopy_page.S standard arm/arm/bcopyinout.S standard diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index aaddfde605b95..d4df01d554c65 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -122,7 +122,7 @@ crypto/aesni/aesencdec_i386.S optional aesni crypto/aesni/aeskeys_i386.S optional aesni crypto/aesni/aesni.c optional aesni crypto/aesni/aesni_wrap.c optional aesni -crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb +crypto/des/arch/i386/des_enc.S optional crypto | ipsec crypto/via/padlock.c optional padlock crypto/via/padlock_cipher.c optional padlock crypto/via/padlock_hash.c optional padlock diff --git a/sys/conf/files.ia64 b/sys/conf/files.ia64 index 7bedb0817d140..a039a4278b273 100644 --- a/sys/conf/files.ia64 +++ b/sys/conf/files.ia64 @@ -45,7 +45,7 @@ contrib/ia64/libuwx/src/uwx_trace.c standard contrib/ia64/libuwx/src/uwx_uinfo.c standard contrib/ia64/libuwx/src/uwx_utable.c standard crypto/blowfish/bf_enc.c optional crypto | ipsec -crypto/des/des_enc.c optional crypto | ipsec | netsmb +crypto/des/des_enc.c optional crypto | ipsec dev/atkbdc/atkbd.c optional atkbd atkbdc dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc dev/atkbdc/atkbdc.c optional atkbdc diff --git a/sys/conf/files.mips b/sys/conf/files.mips index bc628d585791a..ff44986c93bcc 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -65,7 +65,7 @@ mips/mips/swtch.S standard mips/mips/uio_machdep.c standard mips/mips/uma_machdep.c standard crypto/blowfish/bf_enc.c optional crypto | ipsec -crypto/des/des_enc.c optional crypto | ipsec | netsmb +crypto/des/des_enc.c optional crypto | ipsec geom/geom_bsd.c optional geom_bsd geom/geom_bsd_enc.c optional geom_bsd geom/geom_mbr.c optional geom_mbr diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index a8e60b6f72903..252ecdddeac9e 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -77,7 +77,7 @@ bf_enc.o optional crypto | ipsec \ dependency "$S/crypto/blowfish/arch/i386/bf_enc.S $S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S" \ compile-with "${CC} -c -I$S/crypto/blowfish/arch/i386 ${ASM_CFLAGS} ${WERROR} ${.IMPSRC}" \ no-implicit-rule -crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb +crypto/des/arch/i386/des_enc.S optional crypto | ipsec dev/agp/agp_ali.c optional agp dev/agp/agp_amd.c optional agp dev/agp/agp_i810.c optional agp diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index e1ccf094c6d46..cbf181341bfb9 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -18,7 +18,7 @@ font.h optional sc \ cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs powerpc compile-with "${ZFS_C}" cddl/contrib/opensolaris/common/atomic/powerpc64/opensolaris_atomic.S optional zfs powerpc64 compile-with "${ZFS_S}" crypto/blowfish/bf_enc.c optional crypto | ipsec -crypto/des/des_enc.c optional crypto | ipsec | netsmb +crypto/des/des_enc.c optional crypto | ipsec dev/bm/if_bm.c optional bm powermac dev/adb/adb_bus.c optional adb dev/adb/adb_kbd.c optional adb diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64 index 2907dd0408c2d..9232ea2310691 100644 --- a/sys/conf/files.sparc64 +++ b/sys/conf/files.sparc64 @@ -24,7 +24,7 @@ ukbdmap.h optional ukbd_dflt_keymap \ # cddl/contrib/opensolaris/common/atomic/sparc64/opensolaris_atomic.S optional zfs compile-with "${ZFS_S}" crypto/blowfish/bf_enc.c optional crypto | ipsec -crypto/des/des_enc.c optional crypto | ipsec | netsmb +crypto/des/des_enc.c optional crypto | ipsec dev/atkbdc/atkbd.c optional atkbd atkbdc dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc dev/atkbdc/atkbdc.c optional atkbdc diff --git a/sys/conf/options b/sys/conf/options index 894d282327489..84e0c0ef1a679 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -216,7 +216,6 @@ NULLFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h REISERFS opt_dontuse.h -SMBFS opt_dontuse.h TMPFS opt_dontuse.h UDF opt_dontuse.h UNIONFS opt_dontuse.h @@ -282,9 +281,6 @@ UFS_GJOURNAL opt_ufs.h # they won't make any difference yet). NFS_ROOT opt_nfsroot.h -# SMB/CIFS requester -NETSMB opt_netsmb.h - # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h diff --git a/sys/kern/Make.tags.inc b/sys/kern/Make.tags.inc index cb8a3ff993e7f..81205a6b429bb 100644 --- a/sys/kern/Make.tags.inc +++ b/sys/kern/Make.tags.inc @@ -30,7 +30,6 @@ COMM= ${SYS}/dev/advansys/*.[ch] \ ${SYS}/fs/msdosfs/*.[ch] \ ${SYS}/fs/nullfs/*.[ch] \ ${SYS}/fs/procfs/*.[ch] \ - ${SYS}/fs/smbfs/*.[ch] \ ${SYS}/fs/udf/*.[ch] \ ${SYS}/fs/unionfs/*.[ch] \ ${SYS}/geom/*.[ch] \ diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index cfaf9fc61a26c..f47cb0332e728 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -1473,62 +1473,6 @@ sys_openbsd_poll(td, uap) return (sys_poll(td, (struct poll_args *)uap)); } -/* - * XXX This was created specifically to support netncp and netsmb. This - * allows the caller to specify a socket to wait for events on. It returns - * 0 if any events matched and an error otherwise. There is no way to - * determine which events fired. - */ -int -selsocket(struct socket *so, int events, struct timeval *tvp, struct thread *td) -{ - struct timeval atv, rtv, ttv; - int error, timo; - - if (tvp != NULL) { - atv = *tvp; - if (itimerfix(&atv)) - return (EINVAL); - getmicrouptime(&rtv); - timevaladd(&atv, &rtv); - } else { - atv.tv_sec = 0; - atv.tv_usec = 0; - } - - timo = 0; - seltdinit(td); - /* - * Iterate until the timeout expires or the socket becomes ready. - */ - for (;;) { - selfdalloc(td, NULL); - error = sopoll(so, events, NULL, td); - /* error here is actually the ready events. */ - if (error) - return (0); - if (atv.tv_sec || atv.tv_usec) { - getmicrouptime(&rtv); - if (timevalcmp(&rtv, &atv, >=)) { - seltdclear(td); - return (EWOULDBLOCK); - } - ttv = atv; - timevalsub(&ttv, &rtv); - timo = ttv.tv_sec > 24 * 60 * 60 ? - 24 * 60 * 60 * hz : tvtohz(&ttv); - } - error = seltdwait(td, timo); - seltdclear(td); - if (error) - break; - } - /* XXX Duplicates ncp/smb behavior. */ - if (error == ERESTART) - error = 0; - return (error); -} - /* * Preallocate two selfds associated with 'cookie'. Some fo_poll routines * have two select sets, one for read and another for write. diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 6ec6ba982655c..aa546c153f9c8 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -295,7 +295,6 @@ SUBDIR= \ siis \ sis \ sk \ - ${_smbfs} \ ${_sn} \ ${_snc} \ snp \ @@ -514,7 +513,6 @@ _rdma= rdma _safe= safe _sbni= sbni _scsi_low= scsi_low -_smbfs= smbfs _sound= sound _speaker= speaker _splash= splash @@ -712,7 +710,6 @@ _s3= s3 _safe= safe _scsi_low= scsi_low _sfxge= sfxge -_smbfs= smbfs _sound= sound _speaker= speaker _splash= splash @@ -768,7 +765,6 @@ _ips= ips _mly= mly _pccard= pccard _scsi_low= scsi_low -_smbfs= smbfs _sound= sound _splash= splash _sppp= sppp @@ -790,7 +786,6 @@ _cpufreq= cpufreq _exca= exca _nvram= powermac_nvram _pccard= pccard -_smbfs= smbfs _sound= sound .endif @@ -812,7 +807,6 @@ _igb= igb .if ${MK_CDDL} != "no" || defined(ALL_MODULES) _opensolaris= opensolaris .endif -_smbfs= smbfs _sound= sound .if ${MK_ZFS} != "no" || defined(ALL_MODULES) _zfs= zfs diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 94c3b24e5c3e2..b4d01dc46b4fc 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -368,8 +368,6 @@ void soupcall_clear(struct socket *so, int which); void soupcall_set(struct socket *so, int which, int (*func)(struct socket *, void *, int), void *arg); void sowakeup(struct socket *so, struct sockbuf *sb); -int selsocket(struct socket *so, int events, struct timeval *tv, - struct thread *td); /* * Accept filter functions (duh). diff --git a/usr.bin/Makefile.amd64 b/usr.bin/Makefile.amd64 index fee2fb0768118..b720e3de68bf0 100644 --- a/usr.bin/Makefile.amd64 +++ b/usr.bin/Makefile.amd64 @@ -1,3 +1,2 @@ # $FreeBSD$ -SUBDIR+= smbutil diff --git a/usr.bin/Makefile.i386 b/usr.bin/Makefile.i386 index fee2fb0768118..b720e3de68bf0 100644 --- a/usr.bin/Makefile.i386 +++ b/usr.bin/Makefile.i386 @@ -1,3 +1,2 @@ # $FreeBSD$ -SUBDIR+= smbutil diff --git a/usr.bin/Makefile.ia64 b/usr.bin/Makefile.ia64 index fee2fb0768118..b720e3de68bf0 100644 --- a/usr.bin/Makefile.ia64 +++ b/usr.bin/Makefile.ia64 @@ -1,3 +1,2 @@ # $FreeBSD$ -SUBDIR+= smbutil diff --git a/usr.bin/Makefile.powerpc b/usr.bin/Makefile.powerpc index fee2fb0768118..b720e3de68bf0 100644 --- a/usr.bin/Makefile.powerpc +++ b/usr.bin/Makefile.powerpc @@ -1,3 +1,2 @@ # $FreeBSD$ -SUBDIR+= smbutil diff --git a/usr.bin/Makefile.sparc64 b/usr.bin/Makefile.sparc64 index fee2fb0768118..b720e3de68bf0 100644 --- a/usr.bin/Makefile.sparc64 +++ b/usr.bin/Makefile.sparc64 @@ -1,3 +1,2 @@ # $FreeBSD$ -SUBDIR+= smbutil diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c index 73fef94f8c80a..bd3ceab6717ba 100644 --- a/usr.bin/kdump/kdump.c +++ b/usr.bin/kdump/kdump.c @@ -1420,8 +1420,6 @@ ktrsockaddr(struct sockaddr *sa) TODO: Support additional address families #include struct sockaddr_natm *natm; - #include - struct sockaddr_nb *nb; */ char addr[64]; diff --git a/usr.sbin/Makefile.amd64 b/usr.sbin/Makefile.amd64 index 2856da23ce9c5..26eb2b03c482a 100644 --- a/usr.sbin/Makefile.amd64 +++ b/usr.sbin/Makefile.amd64 @@ -17,7 +17,6 @@ SUBDIR+= btxld SUBDIR+= cpucontrol SUBDIR+= kgmon SUBDIR+= lptcontrol -SUBDIR+= mount_smbfs SUBDIR+= mptable .if ${MK_NDIS} != "no" SUBDIR+= ndiscvt diff --git a/usr.sbin/Makefile.i386 b/usr.sbin/Makefile.i386 index 800626c06a4b5..9588eb815f031 100644 --- a/usr.sbin/Makefile.i386 +++ b/usr.sbin/Makefile.i386 @@ -12,7 +12,6 @@ SUBDIR+= cpucontrol SUBDIR+= kgmon SUBDIR+= kgzip SUBDIR+= lptcontrol -SUBDIR+= mount_smbfs SUBDIR+= mptable .if ${MK_NDIS} != "no" SUBDIR+= ndiscvt diff --git a/usr.sbin/Makefile.ia64 b/usr.sbin/Makefile.ia64 index 0a897b40b316e..d047b3375156b 100644 --- a/usr.sbin/Makefile.ia64 +++ b/usr.sbin/Makefile.ia64 @@ -4,6 +4,5 @@ SUBDIR+= acpi .endif SUBDIR+= kgmon -SUBDIR+= mount_smbfs SUBDIR:= ${SUBDIR:Nuathload} SUBDIR+= zzz diff --git a/usr.sbin/Makefile.powerpc b/usr.sbin/Makefile.powerpc index 8833a2741e194..131eb571cdc4e 100644 --- a/usr.sbin/Makefile.powerpc +++ b/usr.sbin/Makefile.powerpc @@ -1,5 +1,4 @@ # $FreeBSD$ -SUBDIR+= mount_smbfs SUBDIR+= nvram SUBDIR+= ofwdump diff --git a/usr.sbin/Makefile.sparc64 b/usr.sbin/Makefile.sparc64 index 320226d0599e9..479dafc6cfec2 100644 --- a/usr.sbin/Makefile.sparc64 +++ b/usr.sbin/Makefile.sparc64 @@ -1,7 +1,6 @@ # $FreeBSD$ SUBDIR+= eeprom -SUBDIR+= mount_smbfs SUBDIR+= ofwdump .if ${MK_SYSINSTALL} != "no" SUBDIR+= sade -- cgit v1.3 From 2dcf7e97da1eb6ef8c733be9f6069e9eda1b773d Mon Sep 17 00:00:00 2001 From: Maksim Yevmenkin Date: Thu, 18 Oct 2012 16:34:00 +0000 Subject: make sure that socket's send and receive buffers are properly sized Submitted by: Iain Hibbert plunky at rya-online dot net MFC after: 3 weeks --- usr.sbin/bluetooth/btpand/client.c | 34 +++++++++++++++++++++++++++++++++- usr.sbin/bluetooth/btpand/server.c | 12 ++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bluetooth/btpand/client.c b/usr.sbin/bluetooth/btpand/client.c index 97064dbbbddc6..2cc908996584e 100644 --- a/usr.sbin/bluetooth/btpand/client.c +++ b/usr.sbin/bluetooth/btpand/client.c @@ -47,7 +47,7 @@ client_init(void) struct sockaddr_l2cap sa; channel_t *chan; socklen_t len; - int fd; + int fd, n; uint16_t mru, mtu; if (bdaddr_any(&remote_bdaddr)) @@ -97,6 +97,17 @@ client_init(void) exit(EXIT_FAILURE); } + len = sizeof(n); + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) { + log_err("Could not read SO_RCVBUF"); + exit(EXIT_FAILURE); + } + if (n < (mru * 10)) { + n = mru * 10; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) + log_info("Could not increase SO_RCVBUF (from %d)", n); + } + len = sizeof(mtu); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { log_err("Could not get L2CAP OMTU: %m"); @@ -107,6 +118,27 @@ client_init(void) exit(EXIT_FAILURE); } + len = sizeof(n); + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) { + log_err("Could not get socket send buffer size: %m"); + close(fd); + return; + } + if (n < (mtu * 2)) { + n = mtu * 2; + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) { + log_err("Could not set socket send buffer size (%d): %m", n); + close(fd); + return; + } + } + n = mtu; + if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) { + log_err("Could not set socket low water mark (%d): %m", n); + close(fd); + return; + } + chan = channel_alloc(); if (chan == NULL) exit(EXIT_FAILURE); diff --git a/usr.sbin/bluetooth/btpand/server.c b/usr.sbin/bluetooth/btpand/server.c index 0843d0c709260..b24d416ff1969 100644 --- a/usr.sbin/bluetooth/btpand/server.c +++ b/usr.sbin/bluetooth/btpand/server.c @@ -177,6 +177,18 @@ server_read(int s, short ev, void *arg) return; } + len = sizeof(n); + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) { + log_err("Could not read SO_RCVBUF"); + close(fd); + return; + } + if (n < (mru * 10)) { + n = mru * 10; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) + log_info("Could not increase SO_RCVBUF (from %d)", n); + } + len = sizeof(mtu); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { log_err("Could not get L2CAP OMTU: %m"); -- cgit v1.3 From 7fba6f489e4d9bf0b66e8cf30b771df5266a2469 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Thu, 18 Oct 2012 17:09:45 +0000 Subject: Fix a bug where blank lines were not being counted (because awk's split() function returns zero when $0 is the NULL string). Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/share/strings.subr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/strings.subr b/usr.sbin/bsdconfig/share/strings.subr index 1a330ad6b4722..d11a9a2e3950e 100644 --- a/usr.sbin/bsdconfig/share/strings.subr +++ b/usr.sbin/bsdconfig/share/strings.subr @@ -75,7 +75,7 @@ f_longest_line_length() f_number_of_lines_awk=' BEGIN { num_lines = 0 } { - num_lines += split($0, unused, /\\n/) + num_lines += split(" "$0, unused, /\\n/) } END { print num_lines } ' -- cgit v1.3 From 6e0adcda1f2daec74d8854fc76b422206c9dbcd5 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Thu, 18 Oct 2012 18:43:54 +0000 Subject: More-accurately replicate the help system from sysinstall(8). However, also improve upon the system by giving the user a "Help" button instead of requiring the user to press F1. NOTE: In FreeBSD-9 and higher, dialog(1) does not support the F1 hook, so the mechanism for providing help to the user had to be changed to a button. This now means we can resurrect *.hlp files from usr.sbin/sysinstall/help/ as- is and reusing them as-needed in bsdconfig (holding to the goal of losing as little functionality from sysinstall as possible). Reviewed by: adrian (co-mentor) Approved by: adrian (co-mentor) --- usr.sbin/bsdconfig/bsdconfig | 13 ++++++++--- usr.sbin/bsdconfig/include/Makefile | 2 +- usr.sbin/bsdconfig/include/bsdconfig.hlp | 12 ++++++++++ usr.sbin/bsdconfig/include/messages.subr | 1 - usr.sbin/bsdconfig/share/common.subr | 38 ++++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 usr.sbin/bsdconfig/include/bsdconfig.hlp (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index ea72d3cf164e3..a9dc1fa4c3e15 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -37,6 +37,7 @@ f_include $BSDCFG_SHARE/strings.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" f_include_lang $BSDCFG_LIBE/include/messages.subr +f_include_help $BSDCFG_LIBE/include/bsdconfig.hlp ############################################################ FUNCTIONS @@ -185,6 +186,8 @@ dialog_menu_main() --item-help \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_exit_bsdconfig\" \ + --help-button \ + ${USE_XDIALOG:+--help \"\$( f_include_help )\"} \ --menu \"\$prompt\" $size $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) @@ -286,8 +289,6 @@ if [ "$1" ]; then exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1 fi -[ -f "$HOME/.bsdconfigrc" ] || f_dialog_msgbox "$msg_help_text" - # # Launch application main menu # @@ -297,7 +298,13 @@ while :; do mtag=$( f_dialog_menutag ) f_dprintf "retval=$retval mtag=[$mtag]" - [ $retval -eq 0 ] || f_die + if [ $retval -eq 2 ]; then + # The Help button was pressed + f_show_msg "%s" "$( f_include_help )" + continue + elif [ $retval -ne 0 ]; then + f_die + fi case "$mtag" in X) # Exit diff --git a/usr.sbin/bsdconfig/include/Makefile b/usr.sbin/bsdconfig/include/Makefile index 15d1725eb829c..27ce643b0d70d 100644 --- a/usr.sbin/bsdconfig/include/Makefile +++ b/usr.sbin/bsdconfig/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/include -FILES= messages.subr +FILES= bsdconfig.hlp messages.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/include/bsdconfig.hlp b/usr.sbin/bsdconfig/include/bsdconfig.hlp new file mode 100644 index 0000000000000..b6eeaf3c45a76 --- /dev/null +++ b/usr.sbin/bsdconfig/include/bsdconfig.hlp @@ -0,0 +1,12 @@ +This menu allows you to configure your system after the installation +process is complete. At the minimum, you should probably set the root +password and the system time zone. + +For extra goodies like bash, emacs, firefox, etc., you should look at +the Packages item in this menu. + +For setting the timezone after the system is installed, use the 'Time +Zone' item in this menu. + +For more information on the overall general system configuration, see +the /etc/rc.conf and /etc/defaults/rc.conf files. diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr index 26d41881123ba..a3b8ace7d6983 100644 --- a/usr.sbin/bsdconfig/include/messages.subr +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -38,7 +38,6 @@ msg_created_path="Created %s" msg_directory_not_found="%s: Directory not found." msg_exit="Exit" msg_exit_bsdconfig="Exit bsdconfig" -msg_help_text="This menu allows you to configure your system after the installation\nprocess is complete. At the minimum, you should probably set the root\npassword and the system time zone.\n\nFor extra goodies like bash, emacs, firefox, etc., you should look at the\n'Packages' item in this menu.\n\nFor setting the timezone after the system is installed, use the 'Time\nZone' item in this menu.\n\nFor more information on the overall general system configuration, see the\n/etc/rc.conf and /etc/defaults/rc.conf files. (Note: to inhibit displaying\nof this message, create an empty file: \$HOME/.bsdconfigrc or copy it from\n/usr/share/examples/bsdconfig/bsdconfigrc)" msg_main_menu="Main Menu" msg_menu_text="If you've already installed FreeBSD, you may use\nthis menu to customize it somewhat to suit your\nparticular configuration. Most importantly, you\ncan use the Packages utility to load extra '3rd\nparty' software not provided in the base\ndistributions." msg_must_be_root_to_execute="%s: must be root to execute" diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 667a8f60092c4..9d034eacee1a9 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -179,6 +179,44 @@ f_include_lang() fi } +# f_include_help [$file] +# +# When given an argument, cache the contents of a language help-file (to later +# be retrieved by executing again without arguments). +# +# Automatically takes $LANG and $LC_ALL into consideration when reading $file +# (suffix ".$LC_ALL" or ".$LANG" will automatically be added prior to loading +# the language help-file). +# +# No error is produced if (a) a language has been requested (by setting either +# $LANG or $LC_ALL in the environment) and (b) the language help-file does not +# exist -- in which case we will fall back to loading $file without-suffix. +# +# If the language help-file does not exist, an error is cached in place of the +# help-file contents. +# +# When called without arguments, the cached value (if any) is produced. Each +# time this function is called, the cache is overwritten with the newly loaded +# contents. +# +f_include_help() +{ + local file="$1" + + if [ "$file" ]; then + local lang="${LANG:-$LC_ALL}" + + f_dprintf "lang=[$lang]" + if [ -f "$file.$lang" ]; then + setvar HELP_$$ "$( cat "$file.$lang" 2>&1 )" + else + setvar HELP_$$ "$( cat "$file" 2>&1 )" + fi + else + eval echo \"\$HELP_$$\" + fi +} + # f_usage $file [ $key1 $value1 ... ] # # Display USAGE file with optional pre-processor macro definitions. The first -- cgit v1.3 From 39893d565a6c4b948fc0ba1b8c22ee1d57f2aaa4 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 19 Oct 2012 14:29:03 +0000 Subject: Add missing const keywords. --- sbin/natd/natd.c | 2 +- usr.bin/logger/logger.c | 6 +++--- usr.sbin/syslogd/syslogd.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'usr.sbin') diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c index 3dbfef0fe9585..631791b18697d 100644 --- a/sbin/natd/natd.c +++ b/sbin/natd/natd.c @@ -1330,7 +1330,7 @@ static void ParseOption (const char* option, const char* parms) struct in_addr addrValue; int max; char* end; - CODE* fac_record = NULL; + const CODE* fac_record = NULL; /* * Find option from table. */ diff --git a/usr.bin/logger/logger.c b/usr.bin/logger/logger.c index d3c4786d8cab2..49360f4a050f5 100644 --- a/usr.bin/logger/logger.c +++ b/usr.bin/logger/logger.c @@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$"); #define SYSLOG_NAMES #include -static int decode(char *, CODE *); +static int decode(char *, const CODE *); static int pencode(char *); static void logmessage(int, const char *, const char *, const char *, const char *); @@ -271,9 +271,9 @@ pencode(char *s) } static int -decode(char *name, CODE *codetab) +decode(char *name, const CODE *codetab) { - CODE *c; + const CODE *c; if (isdigit(*name)) return (atoi(name)); diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index 2f42975cbcd9b..fff7e4c57f628 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -312,7 +312,7 @@ static void cfline(const char *, struct filed *, static const char *cvthname(struct sockaddr *); static void deadq_enter(pid_t, const char *); static int deadq_remove(pid_t); -static int decode(const char *, CODE *); +static int decode(const char *, const CODE *); static void die(int); static void dodie(int); static void dofsync(void); @@ -1123,7 +1123,7 @@ fprintlog(struct filed *f, int flags, const char *msg) char p_n[5]; /* Hollow laugh */ if (LogFacPri > 1) { - CODE *c; + const CODE *c; for (c = facilitynames; c->c_name; c++) { if (c->c_val == fac) { @@ -2029,9 +2029,9 @@ cfline(const char *line, struct filed *f, const char *prog, const char *host) * Decode a symbolic name to a numeric value */ static int -decode(const char *name, CODE *codetab) +decode(const char *name, const CODE *codetab) { - CODE *c; + const CODE *c; char *p, buf[40]; if (isdigit(*name)) -- cgit v1.3 From bf70becee6ff4798acdcfd586a88ad38b7b17495 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 19 Oct 2012 14:49:42 +0000 Subject: More -Wmissing-variable-declarations fixes. In addition to adding `static' where possible: - bin/date: Move `retval' into extern.h to make it visible to date.c. - bin/ed: Move globally used variables into ed.h. - sbin/camcontrol: Move `verbose' into camcontrol.h and fix shadow warnings. - usr.bin/calendar: Remove unneeded variables. - usr.bin/chat: Make `line' local instead of global. - usr.bin/elfdump: Comment out unneeded function. - usr.bin/rlogin: Use _Noreturn instead of __dead2. - usr.bin/tset: Pull `Ospeed' into extern.h. - usr.sbin/mfiutil: Put global variables in mfiutil.h. - usr.sbin/pkg: Remove unused `os_corres'. - usr.sbin/quotaon, usr.sbin/repquota: Remove unused `qfname'. --- bin/date/extern.h | 2 + bin/date/netdate.c | 2 - bin/ed/buf.c | 3 - bin/ed/ed.h | 6 ++ bin/ed/io.c | 9 -- bin/ed/re.c | 3 - libexec/rtld-aout/shlib.c | 2 +- sbin/camcontrol/camcontrol.c | 7 +- sbin/camcontrol/camcontrol.h | 6 +- sbin/camcontrol/fwdownload.c | 14 +-- sbin/camcontrol/modeedit.c | 2 - sbin/geom/core/geom.c | 2 +- usr.bin/at/privs.h | 12 +-- usr.bin/calendar/calendar.c | 4 +- usr.bin/calendar/dates.c | 11 +-- usr.bin/calendar/day.c | 2 +- usr.bin/calendar/io.c | 6 +- usr.bin/chat/chat.c | 53 +++++----- usr.bin/ctlstat/ctlstat.c | 2 +- usr.bin/elfdump/elfdump.c | 111 +++++++++++---------- usr.bin/fetch/fetch.c | 82 ++++++++-------- usr.bin/finger/finger.c | 3 +- usr.bin/gencat/gencat.c | 2 +- usr.bin/grep/grep.c | 7 +- usr.bin/grep/regex/xmalloc.c | 4 +- usr.bin/gzip/zuncompress.c | 7 +- usr.bin/msgs/msgs.c | 80 +++++++-------- usr.bin/ncal/ncal.c | 172 +++++++++++++++++---------------- usr.bin/rlogin/rlogin.c | 98 +++++++++---------- usr.bin/rpcgen/rpc_main.c | 2 +- usr.bin/rpcgen/rpc_svcout.c | 2 +- usr.bin/rpcgen/rpc_util.c | 4 +- usr.bin/sort/sort.c | 2 +- usr.bin/stat/stat.c | 4 +- usr.bin/tftp/main.c | 4 +- usr.bin/tset/extern.h | 3 + usr.bin/tset/map.c | 9 +- usr.bin/tset/term.c | 2 +- usr.sbin/acpi/acpidump/acpi.c | 15 +-- usr.sbin/acpi/acpidump/acpi_user.c | 2 +- usr.sbin/cpucontrol/cpucontrol.c | 2 +- usr.sbin/ctladm/ctladm.c | 8 +- usr.sbin/ctladm/util.c | 2 +- usr.sbin/gpioctl/gpioctl.c | 2 +- usr.sbin/ip6addrctl/ip6addrctl.c | 2 +- usr.sbin/mfiutil/mfi_flash.c | 2 - usr.sbin/mfiutil/mfi_show.c | 2 - usr.sbin/mfiutil/mfiutil.c | 2 +- usr.sbin/mfiutil/mfiutil.h | 1 + usr.sbin/nfscbd/nfscbd.c | 6 +- usr.sbin/nfsd/nfsd.c | 18 ++-- usr.sbin/pkg/elf_tables.h | 11 +-- usr.sbin/pkg_install/version/perform.c | 6 +- usr.sbin/pmccontrol/pmccontrol.c | 13 +-- usr.sbin/pmcstat/pmcpl_calltree.c | 6 +- usr.sbin/pmcstat/pmcstat.c | 13 ++- usr.sbin/pmcstat/pmcstat_log.c | 8 +- usr.sbin/pmcstat/pmcstat_log.h | 4 +- usr.sbin/quotaon/quotaon.c | 19 ++-- usr.sbin/repquota/repquota.c | 19 ++-- usr.sbin/vidcontrol/vidcontrol.c | 2 +- 61 files changed, 445 insertions(+), 466 deletions(-) (limited to 'usr.sbin') diff --git a/bin/date/extern.h b/bin/date/extern.h index 76b8d5e16105a..91aeab2e07e16 100644 --- a/bin/date/extern.h +++ b/bin/date/extern.h @@ -30,4 +30,6 @@ * $FreeBSD$ */ +extern int retval; + int netsettime(time_t); diff --git a/bin/date/netdate.c b/bin/date/netdate.c index b8441068eadf6..b085be4db68fe 100644 --- a/bin/date/netdate.c +++ b/bin/date/netdate.c @@ -55,8 +55,6 @@ __FBSDID("$FreeBSD$"); #define WAITACK 2 /* seconds */ #define WAITDATEACK 5 /* seconds */ -extern int retval; - /* * Set the date in the machines controlled by timedaemons by communicating the * new date to the local timedaemon. If the timedaemon is in the master state, diff --git a/bin/ed/buf.c b/bin/ed/buf.c index 2f3559a7fc819..6d8a6ed4f5591 100644 --- a/bin/ed/buf.c +++ b/bin/ed/buf.c @@ -185,9 +185,6 @@ get_addressed_line_node(long n) return lp; } - -extern int newline_added; - static char sfn[15] = ""; /* scratch file name */ /* open_sbuf: open scratch file */ diff --git a/bin/ed/ed.h b/bin/ed/ed.h index 2021232c04909..8cb8cec731ef2 100644 --- a/bin/ed/ed.h +++ b/bin/ed/ed.h @@ -277,3 +277,9 @@ extern int lineno; extern long second_addr; extern long u_addr_last; extern long u_current_addr; +extern long rows; +extern int cols; +extern int newline_added; +extern int des; +extern int scripted; +extern int patlock; diff --git a/bin/ed/io.c b/bin/ed/io.c index 29c5612d45301..cf2e95906c1fb 100644 --- a/bin/ed/io.c +++ b/bin/ed/io.c @@ -30,9 +30,6 @@ __FBSDID("$FreeBSD$"); #include "ed.h" - -extern int scripted; - /* read_file: read a named file/pipe into the buffer; return line count */ long read_file(char *fn, long n) @@ -58,9 +55,6 @@ read_file(char *fn, long n) return current_addr - n; } - -extern int des; - static char *sbuf; /* file i/o buffer */ static int sbufsz; /* file i/o buffer size */ int newline_added; /* if set, newline appended to input file */ @@ -298,9 +292,6 @@ get_tty_line(void) #define ESCAPES "\a\b\f\n\r\t\v\\" #define ESCCHARS "abfnrtv\\" -extern int rows; -extern int cols; - /* put_tty_line: print text to stdout */ int put_tty_line(const char *s, int l, long n, int gflag) diff --git a/bin/ed/re.c b/bin/ed/re.c index f89ecf94cd6b5..08a330de3f878 100644 --- a/bin/ed/re.c +++ b/bin/ed/re.c @@ -31,9 +31,6 @@ __FBSDID("$FreeBSD$"); #include "ed.h" - -extern int patlock; - const char *errmsg = ""; /* get_compiled_pattern: return pointer to compiled pattern from command diff --git a/libexec/rtld-aout/shlib.c b/libexec/rtld-aout/shlib.c index 5cfa3f3f128ab..70c5313224822 100644 --- a/libexec/rtld-aout/shlib.c +++ b/libexec/rtld-aout/shlib.c @@ -62,7 +62,7 @@ char **search_dirs; int n_search_dirs; -const char *standard_search_dirs[] = { +static const char *standard_search_dirs[] = { STANDARD_SEARCH_DIRS }; diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 362bedd3ba7a8..a3c7f0515c9db 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -5783,9 +5783,10 @@ bailout: #endif /* MINIMALISTIC */ void -usage(int verbose) +usage(int printlong) { - fprintf(verbose ? stdout : stderr, + + fprintf(printlong ? stdout : stderr, "usage: camcontrol [device id][generic args][command args]\n" " camcontrol devlist [-v]\n" #ifndef MINIMALISTIC @@ -5834,7 +5835,7 @@ usage(int verbose) " camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n" #endif /* MINIMALISTIC */ " camcontrol help\n"); - if (!verbose) + if (!printlong) return; #ifndef MINIMALISTIC fprintf(stdout, diff --git a/sbin/camcontrol/camcontrol.h b/sbin/camcontrol/camcontrol.h index adc3243d7d0ee..616236a8b762b 100644 --- a/sbin/camcontrol/camcontrol.h +++ b/sbin/camcontrol/camcontrol.h @@ -40,8 +40,10 @@ struct get_hook int got; }; +extern int verbose; + int fwdownload(struct cam_device *device, int argc, char **argv, - char *combinedopt, int verbose, int retry_count, int timeout, + char *combinedopt, int printerrors, int retry_count, int timeout, const char */*type*/); void mode_sense(struct cam_device *device, int mode_page, int page_control, int dbd, int retry_count, int timeout, u_int8_t *data, @@ -58,5 +60,5 @@ char *cget(void *hook, char *name); int iget(void *hook, char *name); void arg_put(void *hook, int letter, void *arg, int count, char *name); int get_confirmation(void); -void usage(int verbose); +void usage(int printlong); #endif /* _CAMCONTROL_H */ diff --git a/sbin/camcontrol/fwdownload.c b/sbin/camcontrol/fwdownload.c index 3ffa53083b698..daa1520eb4711 100644 --- a/sbin/camcontrol/fwdownload.c +++ b/sbin/camcontrol/fwdownload.c @@ -131,7 +131,7 @@ static char *fw_read_img(const char *fw_img_path, const struct fw_vendor *vp, int *num_bytes); static int fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, char *buf, int img_size, - int sim_mode, int verbose, int retry_count, int timeout, + int sim_mode, int printerrors, int retry_count, int timeout, const char */*name*/, const char */*type*/); /* @@ -238,7 +238,7 @@ bailout1: */ static int fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, - char *buf, int img_size, int sim_mode, int verbose, int retry_count, + char *buf, int img_size, int sim_mode, int printerrors, int retry_count, int timeout, const char *imgname, const char *type) { struct scsi_write_buffer cdb; @@ -290,7 +290,7 @@ fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error sending identify/test unit ready"); - if (verbose) + if (printerrors) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); @@ -298,7 +298,7 @@ fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("Device is not ready"); - if (verbose) + if (printerrors) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); @@ -372,7 +372,7 @@ fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, /* Execute the command. */ if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error writing image to device"); - if (verbose) + if (printerrors) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; @@ -398,7 +398,7 @@ bailout: int fwdownload(struct cam_device *device, int argc, char **argv, - char *combinedopt, int verbose, int retry_count, int timeout, + char *combinedopt, int printerrors, int retry_count, int timeout, const char *type) { const struct fw_vendor *vp; @@ -450,7 +450,7 @@ fwdownload(struct cam_device *device, int argc, char **argv, if (sim_mode) fprintf(stdout, "Running in simulation mode\n"); - if (fw_download_img(device, vp, buf, img_size, sim_mode, verbose, + if (fw_download_img(device, vp, buf, img_size, sim_mode, printerrors, retry_count, timeout, fw_img_path, type) != 0) { fprintf(stderr, "Firmware download failed\n"); goto fail; diff --git a/sbin/camcontrol/modeedit.c b/sbin/camcontrol/modeedit.c index 6a628e897911d..8504208cfcc41 100644 --- a/sbin/camcontrol/modeedit.c +++ b/sbin/camcontrol/modeedit.c @@ -48,8 +48,6 @@ __FBSDID("$FreeBSD$"); #include #include "camcontrol.h" -int verbose = 0; - #define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes" #define DEFAULT_EDITOR "vi" #define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */ diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c index 770e960f3c05d..8c15c2143e1eb 100644 --- a/sbin/geom/core/geom.c +++ b/sbin/geom/core/geom.c @@ -74,7 +74,7 @@ static void std_status(struct gctl_req *req, unsigned flags); static void std_load(struct gctl_req *req, unsigned flags); static void std_unload(struct gctl_req *req, unsigned flags); -struct g_command std_commands[] = { +static struct g_command std_commands[] = { { "help", 0, std_help, G_NULL_OPTS, NULL }, { "list", 0, std_list, { diff --git a/usr.bin/at/privs.h b/usr.bin/at/privs.h index 50dd6b108af47..3f70ce84e18d0 100644 --- a/usr.bin/at/privs.h +++ b/usr.bin/at/privs.h @@ -59,15 +59,13 @@ * to the real userid before calling any of them. */ -#ifndef MAIN -extern -#endif -uid_t real_uid, effective_uid; +extern uid_t real_uid, effective_uid; +extern gid_t real_gid, effective_gid; -#ifndef MAIN -extern -#endif +#ifdef MAIN +uid_t real_uid, effective_uid; gid_t real_gid, effective_gid; +#endif #define RELINQUISH_PRIVS { \ real_uid = getuid(); \ diff --git a/usr.bin/calendar/calendar.c b/usr.bin/calendar/calendar.c index bd03e0272284d..2a107c26afd51 100644 --- a/usr.bin/calendar/calendar.c +++ b/usr.bin/calendar/calendar.c @@ -60,8 +60,8 @@ __FBSDID("$FreeBSD$"); struct passwd *pw; int doall = 0; int debug = 0; -char *DEBUG = NULL; -time_t f_time = 0; +static char *DEBUG = NULL; +static time_t f_time = 0; double UTCOffset = UTCOFFSET_NOTSET; int EastLongitude = LONGITUDE_NOTSET; diff --git a/usr.bin/calendar/dates.c b/usr.bin/calendar/dates.c index 3f8b89f7ee9c2..989f29c970be6 100644 --- a/usr.bin/calendar/dates.c +++ b/usr.bin/calendar/dates.c @@ -43,7 +43,7 @@ struct cal_year { int firstdayofweek; /* 0 .. 6 */ struct cal_month *months; struct cal_year *nextyear; -} cal_year; +}; struct cal_month { int month; /* 01 .. 12 */ @@ -52,7 +52,7 @@ struct cal_month { struct cal_year *year; /* points back */ struct cal_day *days; struct cal_month *nextmonth; -} cal_month; +}; struct cal_day { int dayofmonth; /* 01 .. 31 */ @@ -62,19 +62,18 @@ struct cal_day { struct cal_month *month; /* points back */ struct cal_year *year; /* points back */ struct event *events; -} cal_day; +}; int debug_remember = 0; -struct cal_year *hyear = NULL; +static struct cal_year *hyear = NULL; /* 1-based month, 0-based days, cumulative */ -int *cumdays; int cumdaytab[][14] = { {0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364}, {0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, }; /* 1-based month, individual */ -int *mondays; +static int *mondays; int mondaytab[][14] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30}, diff --git a/usr.bin/calendar/day.c b/usr.bin/calendar/day.c index 237b6b5a7d927..dac6115be4b4d 100644 --- a/usr.bin/calendar/day.c +++ b/usr.bin/calendar/day.c @@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$"); #include "calendar.h" -time_t time1, time2; +static time_t time1, time2; const struct tm tm0; char dayname[100]; int year1, year2; diff --git a/usr.bin/calendar/io.c b/usr.bin/calendar/io.c index e1ed21d8aa84e..162571719e37a 100644 --- a/usr.bin/calendar/io.c +++ b/usr.bin/calendar/io.c @@ -60,10 +60,10 @@ __FBSDID("$FreeBSD$"); #include "calendar.h" const char *calendarFile = "calendar"; /* default calendar file */ -const char *calendarHomes[] = {".calendar", _PATH_INCLUDE}; /* HOME */ -const char *calendarNoMail = "nomail"; /* don't sent mail if this file exist */ +static const char *calendarHomes[] = {".calendar", _PATH_INCLUDE}; /* HOME */ +static const char *calendarNoMail = "nomail";/* don't sent mail if file exist */ -char path[MAXPATHLEN]; +static char path[MAXPATHLEN]; struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice; diff --git a/usr.bin/chat/chat.c b/usr.bin/chat/chat.c index 86e6cfb3237f0..522762d724efa 100644 --- a/usr.bin/chat/chat.c +++ b/usr.bin/chat/chat.c @@ -109,40 +109,39 @@ __FBSDID("$FreeBSD$"); #define MAX_REPORTS 50 #define DEFAULT_CHAT_TIMEOUT 45 -int echo = 0; -int verbose = 0; -int to_log = 1; -int to_stderr = 0; -int Verbose = 0; -int quiet = 0; -int exit_code = 0; -FILE* report_fp = (FILE *) 0; -char *report_file = (char *) 0; -char *chat_file = (char *) 0; -char *phone_num = (char *) 0; -char *phone_num2 = (char *) 0; -int timeout = DEFAULT_CHAT_TIMEOUT; +static int echo; +static int verbose; +static int to_log; +static int to_stderr; +static int Verbose; +static int quiet; +static int exit_code; +static FILE* report_fp; +static char *report_file; +static char *chat_file; +static char *phone_num; +static char *phone_num2; +static int timeout = DEFAULT_CHAT_TIMEOUT; static char blank[] = ""; -int have_tty_parameters = 0; +static int have_tty_parameters; #define term_parms struct termios #define get_term_param(param) tcgetattr(0, param) #define set_term_param(param) tcsetattr(0, TCSANOW, param) -struct termios saved_tty_parameters; +static struct termios saved_tty_parameters; -char *abort_string[MAX_ABORTS], *fail_reason = (char *)0, - fail_buffer[50]; -int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; -int clear_abort_next = 0; +static char *abort_string[MAX_ABORTS], *fail_reason, fail_buffer[50]; +static int n_aborts, abort_next, timeout_next, echo_next; +static int clear_abort_next; -char *report_string[MAX_REPORTS] ; -char report_buffer[50] ; -int n_reports = 0, report_next = 0, report_gathering = 0 ; -int clear_report_next = 0; +static char *report_string[MAX_REPORTS]; +static char report_buffer[50]; +static int n_reports, report_next, report_gathering; +static int clear_report_next; -int say_next = 0, hup_next = 0; +static int say_next, hup_next; void *dup_mem(void *b, size_t c); void *copy_of(char *s); @@ -396,14 +395,13 @@ usage(void) exit(1); } -char line[1024]; - /* * Send a message to syslog and/or stderr. */ void chat_logf(const char *fmt, ...) { + char line[1024]; va_list args; va_start(args, fmt); @@ -422,6 +420,7 @@ chat_logf(const char *fmt, ...) void fatal(int code, const char *fmt, ...) { + char line[1024]; va_list args; va_start(args, fmt); @@ -434,7 +433,7 @@ fatal(int code, const char *fmt, ...) terminate(code); } -int alarmed = 0; +static int alarmed; SIGTYPE sigalrm(int signo __unused) { diff --git a/usr.bin/ctlstat/ctlstat.c b/usr.bin/ctlstat/ctlstat.c index 4d734018621ba..1462ab9679c2e 100644 --- a/usr.bin/ctlstat/ctlstat.c +++ b/usr.bin/ctlstat/ctlstat.c @@ -329,7 +329,7 @@ compute_stats(struct ctl_lun_io_stats *cur_stats, #define PRINT_BINTIME(prefix, bt) \ printf("%s %jd s %ju frac\n", prefix, (intmax_t)(bt).sec, \ (uintmax_t)(bt).frac) -const char *iotypes[] = {"NO IO", "READ", "WRITE"}; +static const char *iotypes[] = {"NO IO", "READ", "WRITE"}; static void ctlstat_dump(struct ctlstat_context *ctx) { diff --git a/usr.bin/elfdump/elfdump.c b/usr.bin/elfdump/elfdump.c index 99ae8bf07ab82..f966f84b458c2 100644 --- a/usr.bin/elfdump/elfdump.c +++ b/usr.bin/elfdump/elfdump.c @@ -83,7 +83,7 @@ enum elf_member { typedef enum elf_member elf_member_t; -int elf32_offsets[] = { +static int elf32_offsets[] = { 0, offsetof(Elf32_Dyn, d_tag), offsetof(Elf32_Dyn, d_un.d_ptr), @@ -124,7 +124,7 @@ int elf32_offsets[] = { offsetof(Elf32_Rela, r_addend) }; -int elf64_offsets[] = { +static int elf64_offsets[] = { 0, offsetof(Elf64_Dyn, d_tag), offsetof(Elf64_Dyn, d_un.d_ptr), @@ -266,23 +266,23 @@ e_machines(u_int mach) return (machdesc); } -const char *e_types[] = { +static const char *e_types[] = { "ET_NONE", "ET_REL", "ET_EXEC", "ET_DYN", "ET_CORE" }; -const char *ei_versions[] = { +static const char *ei_versions[] = { "EV_NONE", "EV_CURRENT" }; -const char *ei_classes[] = { +static const char *ei_classes[] = { "ELFCLASSNONE", "ELFCLASS32", "ELFCLASS64" }; -const char *ei_data[] = { +static const char *ei_data[] = { "ELFDATANONE", "ELFDATA2LSB", "ELFDATA2MSB" }; -const char *ei_abis[256] = { +static const char *ei_abis[256] = { "ELFOSABI_SYSV", "ELFOSABI_HPUX", "ELFOSABI_NETBSD", "ELFOSABI_LINUX", "ELFOSABI_HURD", "ELFOSABI_86OPEN", "ELFOSABI_SOLARIS", "ELFOSABI_AIX", "ELFOSABI_IRIX", "ELFOSABI_FREEBSD", "ELFOSABI_TRU64", @@ -290,12 +290,12 @@ const char *ei_abis[256] = { [255] = "ELFOSABI_STANDALONE" }; -const char *p_types[] = { +static const char *p_types[] = { "PT_NULL", "PT_LOAD", "PT_DYNAMIC", "PT_INTERP", "PT_NOTE", "PT_SHLIB", "PT_PHDR", "PT_TLS" }; -const char *p_flags[] = { +static const char *p_flags[] = { "", "PF_X", "PF_W", "PF_X|PF_W", "PF_R", "PF_X|PF_R", "PF_W|PF_R", "PF_X|PF_W|PF_R" }; @@ -339,44 +339,47 @@ sh_types(u_int64_t sht) { } } -const char *sh_flags[] = { +static const char *sh_flags[] = { "", "SHF_WRITE", "SHF_ALLOC", "SHF_WRITE|SHF_ALLOC", "SHF_EXECINSTR", "SHF_WRITE|SHF_EXECINSTR", "SHF_ALLOC|SHF_EXECINSTR", "SHF_WRITE|SHF_ALLOC|SHF_EXECINSTR" }; -const char *st_types[] = { +static const char *st_types[] = { "STT_NOTYPE", "STT_OBJECT", "STT_FUNC", "STT_SECTION", "STT_FILE" }; -const char *st_bindings[] = { +static const char *st_bindings[] = { "STB_LOCAL", "STB_GLOBAL", "STB_WEAK" }; -char *dynstr; -char *shstrtab; -char *strtab; -FILE *out; - -u_int64_t elf_get_byte(Elf32_Ehdr *e, void *base, elf_member_t member); -u_int64_t elf_get_quarter(Elf32_Ehdr *e, void *base, elf_member_t member); -u_int64_t elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member); -u_int64_t elf_get_word(Elf32_Ehdr *e, void *base, elf_member_t member); -u_int64_t elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member); - -void elf_print_ehdr(Elf32_Ehdr *e); -void elf_print_phdr(Elf32_Ehdr *e, void *p); -void elf_print_shdr(Elf32_Ehdr *e, void *sh); -void elf_print_symtab(Elf32_Ehdr *e, void *sh, char *str); -void elf_print_dynamic(Elf32_Ehdr *e, void *sh); -void elf_print_rel(Elf32_Ehdr *e, void *r); -void elf_print_rela(Elf32_Ehdr *e, void *ra); -void elf_print_interp(Elf32_Ehdr *e, void *p); -void elf_print_got(Elf32_Ehdr *e, void *sh); -void elf_print_hash(Elf32_Ehdr *e, void *sh); -void elf_print_note(Elf32_Ehdr *e, void *sh); - -void usage(void); +static char *dynstr; +static char *shstrtab; +static char *strtab; +static FILE *out; + +static u_int64_t elf_get_byte(Elf32_Ehdr *e, void *base, elf_member_t member); +static u_int64_t elf_get_quarter(Elf32_Ehdr *e, void *base, + elf_member_t member); +#if 0 +static u_int64_t elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member); +#endif +static u_int64_t elf_get_word(Elf32_Ehdr *e, void *base, elf_member_t member); +static u_int64_t elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member); + +static void elf_print_ehdr(Elf32_Ehdr *e); +static void elf_print_phdr(Elf32_Ehdr *e, void *p); +static void elf_print_shdr(Elf32_Ehdr *e, void *sh); +static void elf_print_symtab(Elf32_Ehdr *e, void *sh, char *str); +static void elf_print_dynamic(Elf32_Ehdr *e, void *sh); +static void elf_print_rel(Elf32_Ehdr *e, void *r); +static void elf_print_rela(Elf32_Ehdr *e, void *ra); +static void elf_print_interp(Elf32_Ehdr *e, void *p); +static void elf_print_got(Elf32_Ehdr *e, void *sh); +static void elf_print_hash(Elf32_Ehdr *e, void *sh); +static void elf_print_note(Elf32_Ehdr *e, void *sh); + +static void usage(void); int main(int ac, char **av) @@ -551,7 +554,7 @@ main(int ac, char **av) return 0; } -void +static void elf_print_ehdr(Elf32_Ehdr *e) { u_int64_t class; @@ -606,7 +609,7 @@ elf_print_ehdr(Elf32_Ehdr *e) fprintf(out, "\te_shstrndx: %jd\n", (intmax_t)shstrndx); } -void +static void elf_print_phdr(Elf32_Ehdr *e, void *p) { u_int64_t phentsize; @@ -648,7 +651,7 @@ elf_print_phdr(Elf32_Ehdr *e, void *p) } } -void +static void elf_print_shdr(Elf32_Ehdr *e, void *sh) { u_int64_t shentsize; @@ -696,7 +699,7 @@ elf_print_shdr(Elf32_Ehdr *e, void *sh) } } -void +static void elf_print_symtab(Elf32_Ehdr *e, void *sh, char *str) { u_int64_t offset; @@ -735,7 +738,7 @@ elf_print_symtab(Elf32_Ehdr *e, void *sh, char *str) } } -void +static void elf_print_dynamic(Elf32_Ehdr *e, void *sh) { u_int64_t offset; @@ -795,7 +798,7 @@ elf_print_dynamic(Elf32_Ehdr *e, void *sh) } } -void +static void elf_print_rela(Elf32_Ehdr *e, void *sh) { u_int64_t offset; @@ -827,7 +830,7 @@ elf_print_rela(Elf32_Ehdr *e, void *sh) } } -void +static void elf_print_rel(Elf32_Ehdr *e, void *sh) { u_int64_t offset; @@ -856,7 +859,7 @@ elf_print_rel(Elf32_Ehdr *e, void *sh) } } -void +static void elf_print_interp(Elf32_Ehdr *e, void *p) { u_int64_t offset; @@ -868,7 +871,7 @@ elf_print_interp(Elf32_Ehdr *e, void *p) fprintf(out, "\t%s\n", s); } -void +static void elf_print_got(Elf32_Ehdr *e, void *sh) { u_int64_t offset; @@ -891,12 +894,12 @@ elf_print_got(Elf32_Ehdr *e, void *sh) } } -void +static void elf_print_hash(Elf32_Ehdr *e __unused, void *sh __unused) { } -void +static void elf_print_note(Elf32_Ehdr *e, void *sh) { u_int64_t offset; @@ -922,7 +925,7 @@ elf_print_note(Elf32_Ehdr *e, void *sh) } } -u_int64_t +static u_int64_t elf_get_byte(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; @@ -942,7 +945,7 @@ elf_get_byte(Elf32_Ehdr *e, void *base, elf_member_t member) return val; } -u_int64_t +static u_int64_t elf_get_quarter(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; @@ -982,7 +985,8 @@ elf_get_quarter(Elf32_Ehdr *e, void *base, elf_member_t member) return val; } -u_int64_t +#if 0 +static u_int64_t elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; @@ -1021,8 +1025,9 @@ elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member) return val; } +#endif -u_int64_t +static u_int64_t elf_get_word(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; @@ -1062,7 +1067,7 @@ elf_get_word(Elf32_Ehdr *e, void *base, elf_member_t member) return val; } -u_int64_t +static u_int64_t elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; @@ -1102,7 +1107,7 @@ elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member) return val; } -void +static void usage(void) { fprintf(stderr, "usage: elfdump -a | -cdeGhinprs [-w file] file\n"); diff --git a/usr.bin/fetch/fetch.c b/usr.bin/fetch/fetch.c index e8b7318a7ffea..3e897d7e1a8d2 100644 --- a/usr.bin/fetch/fetch.c +++ b/usr.bin/fetch/fetch.c @@ -51,47 +51,47 @@ __FBSDID("$FreeBSD$"); #define TIMEOUT 120 /* Option flags */ -int A_flag; /* -A: do not follow 302 redirects */ -int a_flag; /* -a: auto retry */ -off_t B_size; /* -B: buffer size */ -int b_flag; /*! -b: workaround TCP bug */ -char *c_dirname; /* -c: remote directory */ -int d_flag; /* -d: direct connection */ -int F_flag; /* -F: restart without checking mtime */ -char *f_filename; /* -f: file to fetch */ -char *h_hostname; /* -h: host to fetch from */ -int i_flag; /* -i: specify input file for mtime comparison */ -char *i_filename; /* name of input file */ -int l_flag; /* -l: link rather than copy file: URLs */ -int m_flag; /* -[Mm]: mirror mode */ -char *N_filename; /* -N: netrc file name */ -int n_flag; /* -n: do not preserve modification time */ -int o_flag; /* -o: specify output file */ -int o_directory; /* output file is a directory */ -char *o_filename; /* name of output file */ -int o_stdout; /* output file is stdout */ -int once_flag; /* -1: stop at first successful file */ -int p_flag; /* -[Pp]: use passive FTP */ -int R_flag; /* -R: don't delete partially transferred files */ -int r_flag; /* -r: restart previously interrupted transfer */ -off_t S_size; /* -S: require size to match */ -int s_flag; /* -s: show size, don't fetch */ -long T_secs; /* -T: transfer timeout in seconds */ -int t_flag; /*! -t: workaround TCP bug */ -int U_flag; /* -U: do not use high ports */ -int v_level = 1; /* -v: verbosity level */ -int v_tty; /* stdout is a tty */ -pid_t pgrp; /* our process group */ -long w_secs; /* -w: retry delay */ -int family = PF_UNSPEC; /* -[46]: address family to use */ - -int sigalrm; /* SIGALRM received */ -int siginfo; /* SIGINFO received */ -int sigint; /* SIGINT received */ - -long ftp_timeout = TIMEOUT; /* default timeout for FTP transfers */ -long http_timeout = TIMEOUT; /* default timeout for HTTP transfers */ -char *buf; /* transfer buffer */ +static int A_flag; /* -A: do not follow 302 redirects */ +static int a_flag; /* -a: auto retry */ +static off_t B_size; /* -B: buffer size */ +static int b_flag; /*! -b: workaround TCP bug */ +static char *c_dirname; /* -c: remote directory */ +static int d_flag; /* -d: direct connection */ +static int F_flag; /* -F: restart without checking mtime */ +static char *f_filename; /* -f: file to fetch */ +static char *h_hostname; /* -h: host to fetch from */ +static int i_flag; /* -i: specify file for mtime comparison */ +static char *i_filename; /* name of input file */ +static int l_flag; /* -l: link rather than copy file: URLs */ +static int m_flag; /* -[Mm]: mirror mode */ +static char *N_filename; /* -N: netrc file name */ +static int n_flag; /* -n: do not preserve modification time */ +static int o_flag; /* -o: specify output file */ +static int o_directory; /* output file is a directory */ +static char *o_filename; /* name of output file */ +static int o_stdout; /* output file is stdout */ +static int once_flag; /* -1: stop at first successful file */ +static int p_flag; /* -[Pp]: use passive FTP */ +static int R_flag; /* -R: don't delete partial files */ +static int r_flag; /* -r: restart previous transfer */ +static off_t S_size; /* -S: require size to match */ +static int s_flag; /* -s: show size, don't fetch */ +static long T_secs; /* -T: transfer timeout in seconds */ +static int t_flag; /*! -t: workaround TCP bug */ +static int U_flag; /* -U: do not use high ports */ +static int v_level = 1; /* -v: verbosity level */ +static int v_tty; /* stdout is a tty */ +static pid_t pgrp; /* our process group */ +static long w_secs; /* -w: retry delay */ +static int family = PF_UNSPEC; /* -[46]: address family to use */ + +static int sigalrm; /* SIGALRM received */ +static int siginfo; /* SIGINFO received */ +static int sigint; /* SIGINT received */ + +static long ftp_timeout = TIMEOUT; /* default timeout for FTP transfers */ +static long http_timeout = TIMEOUT;/* default timeout for HTTP transfers */ +static char *buf; /* transfer buffer */ /* diff --git a/usr.bin/finger/finger.c b/usr.bin/finger/finger.c index 7519f15e2c942..0a4e952ae7483 100644 --- a/usr.bin/finger/finger.c +++ b/usr.bin/finger/finger.c @@ -87,7 +87,8 @@ __FBSDID("$FreeBSD$"); DB *db; time_t now; -int entries, gflag, kflag, lflag, mflag, pplan, sflag, oflag; +static int kflag, mflag, sflag; +int entries, gflag, lflag, pplan, oflag; sa_family_t family = PF_UNSPEC; int d_first = -1; char tbuf[1024]; diff --git a/usr.bin/gencat/gencat.c b/usr.bin/gencat/gencat.c index 40d68fc3dc769..031c27aaa2048 100644 --- a/usr.bin/gencat/gencat.c +++ b/usr.bin/gencat/gencat.c @@ -96,7 +96,7 @@ struct _setT { LIST_ENTRY(_setT) entries; }; -LIST_HEAD(sethead, _setT) sethead; +static LIST_HEAD(sethead, _setT) sethead; static struct _setT *curSet; static char *curline = NULL; diff --git a/usr.bin/grep/grep.c b/usr.bin/grep/grep.c index 8133f44b51b25..43d93347230cb 100644 --- a/usr.bin/grep/grep.c +++ b/usr.bin/grep/grep.c @@ -82,14 +82,15 @@ int eflags = REG_STARTEND; bool matchall; /* Searching patterns */ -unsigned int patterns, pattern_sz; +unsigned int patterns; +static unsigned int pattern_sz; struct pat *pattern; regex_t *r_pattern; fastmatch_t *fg_pattern; /* Filename exclusion/inclusion patterns */ -unsigned int fpatterns, fpattern_sz; -unsigned int dpatterns, dpattern_sz; +unsigned int fpatterns, dpatterns; +static unsigned int fpattern_sz, dpattern_sz; struct epat *dpattern, *fpattern; /* For regex errors */ diff --git a/usr.bin/grep/regex/xmalloc.c b/usr.bin/grep/regex/xmalloc.c index b1acc9c5434c8..0fc5cd2a13403 100644 --- a/usr.bin/grep/regex/xmalloc.c +++ b/usr.bin/grep/regex/xmalloc.c @@ -39,9 +39,9 @@ typedef struct { } hashTable; static int xmalloc_peak; -int xmalloc_current; +static int xmalloc_current; static int xmalloc_peak_blocks; -int xmalloc_current_blocks; +static int xmalloc_current_blocks; static int xmalloc_fail_after; #define TABLE_BITS 8 diff --git a/usr.bin/gzip/zuncompress.c b/usr.bin/gzip/zuncompress.c index f7f50af2b9d18..f1b05fd235723 100644 --- a/usr.bin/gzip/zuncompress.c +++ b/usr.bin/gzip/zuncompress.c @@ -77,10 +77,9 @@ static char_type magic_header[] = static char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; -/* XXX zuncompress global */ -off_t total_compressed_bytes; -size_t compressed_prelen; -char *compressed_pre; +static off_t total_compressed_bytes; +static size_t compressed_prelen; +static char *compressed_pre; struct s_zstate { FILE *zs_fp; /* File stream for I/O */ diff --git a/usr.bin/msgs/msgs.c b/usr.bin/msgs/msgs.c index 8139b3ffeddd5..e534fac83da89 100644 --- a/usr.bin/msgs/msgs.c +++ b/usr.bin/msgs/msgs.c @@ -105,47 +105,47 @@ __FBSDID("$FreeBSD$"); typedef char bool; -FILE *msgsrc; -FILE *newmsg; -const char *sep = "-"; -char inbuf[BUFSIZ]; -char fname[MAXPATHLEN]; -char cmdbuf[MAXPATHLEN + MAXPATHLEN]; -char subj[128]; -char from[128]; -char date[128]; -char *ptr; -char *in; -bool local; -bool ruptible; -bool totty; -bool seenfrom; -bool seensubj; -bool blankline; -bool printing = NO; -bool mailing = NO; -bool quitit = NO; -bool sending = NO; -bool intrpflg = NO; -uid_t uid; -int msg; -int prevmsg; -int lct; -int nlines; -int Lpp = 0; -time_t t; -time_t keep; +static FILE *msgsrc; +static FILE *newmsg; +static const char *sep = "-"; +static char inbuf[BUFSIZ]; +static char fname[MAXPATHLEN]; +static char cmdbuf[MAXPATHLEN + MAXPATHLEN]; +static char subj[128]; +static char from[128]; +static char date[128]; +static char *ptr; +static char *in; +static bool local; +static bool ruptible; +static bool totty; +static bool seenfrom; +static bool seensubj; +static bool blankline; +static bool printing = NO; +static bool mailing = NO; +static bool quitit = NO; +static bool sending = NO; +static bool intrpflg = NO; +static uid_t uid; +static int msg; +static int prevmsg; +static int lct; +static int nlines; +static int Lpp = 0; +static time_t t; +static time_t keep; /* option initialization */ -bool hdrs = NO; -bool qopt = NO; -bool hush = NO; -bool send_msg = NO; -bool locomode = NO; -bool use_pager = NO; -bool clean = NO; -bool lastcmd = NO; -jmp_buf tstpbuf; +static bool hdrs = NO; +static bool qopt = NO; +static bool hush = NO; +static bool send_msg = NO; +static bool locomode = NO; +static bool use_pager = NO; +static bool clean = NO; +static bool lastcmd = NO; +static jmp_buf tstpbuf; static void ask(const char *); static void gfrsub(FILE *); @@ -155,7 +155,7 @@ static char *nxtfld(char *); static void onsusp(int); static void onintr(int); static void prmesg(int); -static void usage(void); +static void usage(void); int main(int argc, char *argv[]) diff --git a/usr.bin/ncal/ncal.c b/usr.bin/ncal/ncal.c index 02eb58754ae26..48b8c1ae0f562 100644 --- a/usr.bin/ncal/ncal.c +++ b/usr.bin/ncal/ncal.c @@ -24,10 +24,8 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); #include #include @@ -111,77 +109,81 @@ static struct djswitch { {"YU", "Yugoslavia", {1919, 3, 4}} }; -struct djswitch *dftswitch = +static struct djswitch *dftswitch = switches + sizeof(switches) / sizeof(struct djswitch) - 2; /* default switch (should be "US") */ /* Table used to print day of month and week numbers */ -char daystr[] = " 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" - " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" - " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" - " 48 49 50 51 52 53"; +static char daystr[] = " 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53"; /* Table used to print day of year and week numbers */ -char jdaystr[] = " 1 2 3 4 5 6 7 8 9" - " 10 11 12 13 14 15 16 17 18 19" - " 20 21 22 23 24 25 26 27 28 29" - " 30 31 32 33 34 35 36 37 38 39" - " 40 41 42 43 44 45 46 47 48 49" - " 50 51 52 53 54 55 56 57 58 59" - " 60 61 62 63 64 65 66 67 68 69" - " 70 71 72 73 74 75 76 77 78 79" - " 80 81 82 83 84 85 86 87 88 89" - " 90 91 92 93 94 95 96 97 98 99" - " 100 101 102 103 104 105 106 107 108 109" - " 110 111 112 113 114 115 116 117 118 119" - " 120 121 122 123 124 125 126 127 128 129" - " 130 131 132 133 134 135 136 137 138 139" - " 140 141 142 143 144 145 146 147 148 149" - " 150 151 152 153 154 155 156 157 158 159" - " 160 161 162 163 164 165 166 167 168 169" - " 170 171 172 173 174 175 176 177 178 179" - " 180 181 182 183 184 185 186 187 188 189" - " 190 191 192 193 194 195 196 197 198 199" - " 200 201 202 203 204 205 206 207 208 209" - " 210 211 212 213 214 215 216 217 218 219" - " 220 221 222 223 224 225 226 227 228 229" - " 230 231 232 233 234 235 236 237 238 239" - " 240 241 242 243 244 245 246 247 248 249" - " 250 251 252 253 254 255 256 257 258 259" - " 260 261 262 263 264 265 266 267 268 269" - " 270 271 272 273 274 275 276 277 278 279" - " 280 281 282 283 284 285 286 287 288 289" - " 290 291 292 293 294 295 296 297 298 299" - " 300 301 302 303 304 305 306 307 308 309" - " 310 311 312 313 314 315 316 317 318 319" - " 320 321 322 323 324 325 326 327 328 329" - " 330 331 332 333 334 335 336 337 338 339" - " 340 341 342 343 344 345 346 347 348 349" - " 350 351 352 353 354 355 356 357 358 359" - " 360 361 362 363 364 365 366"; - -int flag_nohighlight; /* user doesn't want a highlighted today */ -int flag_weeks; /* user wants number of week */ -int nswitch; /* user defined switch date */ -int nswitchb; /* switch date for backward compatibility */ -int highlightdate; - -char *center(char *s, char *t, int w); -wchar_t *wcenter(wchar_t *s, wchar_t *t, int w); -int firstday(int y, int m); -void highlight(char *dst, char *src, int len, int *extraletters); -void mkmonthr(int year, int month, int jd_flag, struct monthlines * monthl); -void mkmonthb(int year, int month, int jd_flag, struct monthlines * monthl); -void mkweekdays(struct weekdays * wds); -void monthranger(int year, int m, int jd_flag, int before, int after); -void monthrangeb(int year, int m, int jd_flag, int before, int after); -int parsemonth(const char *s, int *m, int *y); -void printcc(void); -void printeaster(int year, int julian, int orthodox); -date *sdater(int ndays, struct date * d); -date *sdateb(int ndays, struct date * d); -int sndaysr(struct date * d); -int sndaysb(struct date * d); +static char jdaystr[] = " 1 2 3 4 5 6 7 8 9" + " 10 11 12 13 14 15 16 17 18 19" + " 20 21 22 23 24 25 26 27 28 29" + " 30 31 32 33 34 35 36 37 38 39" + " 40 41 42 43 44 45 46 47 48 49" + " 50 51 52 53 54 55 56 57 58 59" + " 60 61 62 63 64 65 66 67 68 69" + " 70 71 72 73 74 75 76 77 78 79" + " 80 81 82 83 84 85 86 87 88 89" + " 90 91 92 93 94 95 96 97 98 99" + " 100 101 102 103 104 105 106 107 108 109" + " 110 111 112 113 114 115 116 117 118 119" + " 120 121 122 123 124 125 126 127 128 129" + " 130 131 132 133 134 135 136 137 138 139" + " 140 141 142 143 144 145 146 147 148 149" + " 150 151 152 153 154 155 156 157 158 159" + " 160 161 162 163 164 165 166 167 168 169" + " 170 171 172 173 174 175 176 177 178 179" + " 180 181 182 183 184 185 186 187 188 189" + " 190 191 192 193 194 195 196 197 198 199" + " 200 201 202 203 204 205 206 207 208 209" + " 210 211 212 213 214 215 216 217 218 219" + " 220 221 222 223 224 225 226 227 228 229" + " 230 231 232 233 234 235 236 237 238 239" + " 240 241 242 243 244 245 246 247 248 249" + " 250 251 252 253 254 255 256 257 258 259" + " 260 261 262 263 264 265 266 267 268 269" + " 270 271 272 273 274 275 276 277 278 279" + " 280 281 282 283 284 285 286 287 288 289" + " 290 291 292 293 294 295 296 297 298 299" + " 300 301 302 303 304 305 306 307 308 309" + " 310 311 312 313 314 315 316 317 318 319" + " 320 321 322 323 324 325 326 327 328 329" + " 330 331 332 333 334 335 336 337 338 339" + " 340 341 342 343 344 345 346 347 348 349" + " 350 351 352 353 354 355 356 357 358 359" + " 360 361 362 363 364 365 366"; + +static int flag_nohighlight; /* user doesn't want a highlighted today */ +static int flag_weeks; /* user wants number of week */ +static int nswitch; /* user defined switch date */ +static int nswitchb; /* switch date for backward compatibility */ +static int highlightdate; + +static char *center(char *s, char *t, int w); +static wchar_t *wcenter(wchar_t *s, wchar_t *t, int w); +static int firstday(int y, int m); +static void highlight(char *dst, char *src, int len, int *extraletters); +static void mkmonthr(int year, int month, int jd_flag, + struct monthlines * monthl); +static void mkmonthb(int year, int month, int jd_flag, + struct monthlines * monthl); +static void mkweekdays(struct weekdays * wds); +static void monthranger(int year, int m, int jd_flag, + int before, int after); +static void monthrangeb(int year, int m, int jd_flag, + int before, int after); +static int parsemonth(const char *s, int *m, int *y); +static void printcc(void); +static void printeaster(int year, int julian, int orthodox); +static date *sdater(int ndays, struct date * d); +static date *sdateb(int ndays, struct date * d); +static int sndaysr(struct date * d); +static int sndaysb(struct date * d); static void usage(void); int @@ -513,7 +515,7 @@ usage(void) } /* Print the assumed switches for all countries. */ -void +static void printcc(void) { struct djswitch *p; @@ -534,7 +536,7 @@ printcc(void) } /* Print the date of easter sunday. */ -void +static void printeaster(int y, int julian, int orthodox) { date dt; @@ -579,7 +581,7 @@ printeaster(int y, int julian, int orthodox) #define M2M(m) (1 + (m) % 12) /* Print all months for the period in the range [ before .. y-m .. after ]. */ -void +static void monthrangeb(int y, int m, int jd_flag, int before, int after) { struct monthlines year[12]; @@ -669,7 +671,7 @@ monthrangeb(int y, int m, int jd_flag, int before, int after) } } -void +static void monthranger(int y, int m, int jd_flag, int before, int after) { struct monthlines year[12]; @@ -757,7 +759,7 @@ monthranger(int y, int m, int jd_flag, int before, int after) return; } -void +static void mkmonthr(int y, int m, int jd_flag, struct monthlines *mlines) { @@ -848,7 +850,7 @@ mkmonthr(int y, int m, int jd_flag, struct monthlines *mlines) } } -void +static void mkmonthb(int y, int m, int jd_flag, struct monthlines *mlines) { @@ -945,7 +947,7 @@ mkmonthb(int y, int m, int jd_flag, struct monthlines *mlines) } /* Put the local names of weekdays into the wds. */ -void +static void mkweekdays(struct weekdays *wds) { int i, len, width = 0; @@ -973,7 +975,7 @@ mkweekdays(struct weekdays *wds) * Compute the day number of the first existing date after the first day in * month. (the first day in month and even the month might not exist!) */ -int +static int firstday(int y, int m) { date dt; @@ -997,7 +999,7 @@ firstday(int y, int m) * Compute the number of days from date, obey the local switch from * Julian to Gregorian if specified by the user. */ -int +static int sndaysr(struct date *d) { @@ -1014,7 +1016,7 @@ sndaysr(struct date *d) * Compute the number of days from date, obey the switch from * Julian to Gregorian as used by UK and her colonies. */ -int +static int sndaysb(struct date *d) { @@ -1025,7 +1027,7 @@ sndaysb(struct date *d) } /* Inverse of sndays. */ -struct date * +static struct date * sdater(int nd, struct date *d) { @@ -1036,7 +1038,7 @@ sdater(int nd, struct date *d) } /* Inverse of sndaysb. */ -struct date * +static struct date * sdateb(int nd, struct date *d) { @@ -1047,7 +1049,7 @@ sdateb(int nd, struct date *d) } /* Center string t in string s of length w by putting enough leading blanks. */ -char * +static char * center(char *s, char *t, int w) { char blanks[MAX_WIDTH]; @@ -1058,7 +1060,7 @@ center(char *s, char *t, int w) } /* Center string t in string s of length w by putting enough leading blanks. */ -wchar_t * +static wchar_t * wcenter(wchar_t *s, wchar_t *t, int w) { char blanks[MAX_WIDTH]; @@ -1068,7 +1070,7 @@ wcenter(wchar_t *s, wchar_t *t, int w) return (s); } -int +static int parsemonth(const char *s, int *m, int *y) { int nm, ny; @@ -1101,7 +1103,7 @@ parsemonth(const char *s, int *m, int *y) return (1); } -void +static void highlight(char *dst, char *src, int len, int *extralen) { static int first = 1; diff --git a/usr.bin/rlogin/rlogin.c b/usr.bin/rlogin/rlogin.c index b134679159812..646630b366b88 100644 --- a/usr.bin/rlogin/rlogin.c +++ b/usr.bin/rlogin/rlogin.c @@ -92,35 +92,35 @@ __FBSDID("$FreeBSD$"); #define SIGUSR1 30 #endif -int eight, rem; -struct termios deftty; +static int eight, rem; +static struct termios deftty; -int family = PF_UNSPEC; +static int family = PF_UNSPEC; -int noescape; -u_char escapechar = '~'; +static int noescape; +static u_char escapechar = '~'; #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) -struct winsize winsize; - -void catch_child(int); -void copytochild(int); -void doit(long) __dead2; -void done(int) __dead2; -void echo(char); -u_int getescape(const char *); -void lostpeer(int); -void mode(int); -void msg(const char *); -void oob(int); -int reader(int); -void sendwindow(void); -void setsignal(int); -void sigwinch(int); -void stop(char); -void usage(void) __dead2; -void writer(void); -void writeroob(int); +static struct winsize winsize; + +static void catch_child(int); +static void copytochild(int); +static _Noreturn void doit(long); +static _Noreturn void done(int); +static void echo(char); +static u_int getescape(const char *); +static void lostpeer(int); +static void mode(int); +static void msg(const char *); +static void oob(int); +static int reader(int); +static void sendwindow(void); +static void setsignal(int); +static void sigwinch(int); +static void stop(char); +static _Noreturn void usage(void); +static void writer(void); +static void writeroob(int); int main(int argc, char *argv[]) @@ -269,9 +269,9 @@ main(int argc, char *argv[]) /*NOTREACHED*/ } -int child; +static int child; -void +static void doit(long omask) { @@ -309,7 +309,7 @@ doit(long omask) } /* trap a signal, unless it is being ignored. */ -void +static void setsignal(int sig) { int omask = sigblock(sigmask(sig)); @@ -319,7 +319,7 @@ setsignal(int sig) (void)sigsetmask(omask); } -void +static void done(int status) { int w, wstatus; @@ -334,14 +334,14 @@ done(int status) exit(status); } -int dosigwinch; +static int dosigwinch; /* * This is called when the reader process gets the out-of-band (urgent) * request to turn on the window-changing protocol. */ /* ARGSUSED */ -void +static void writeroob(int signo __unused) { if (dosigwinch == 0) { @@ -352,7 +352,7 @@ writeroob(int signo __unused) } /* ARGSUSED */ -void +static void catch_child(int signo __unused) { pid_t pid; @@ -375,7 +375,7 @@ catch_child(int signo __unused) * ~^Z suspend rlogin process. * ~ suspend rlogin process, but leave reader alone. */ -void +static void writer(void) { int bol, local, n; @@ -432,7 +432,7 @@ writer(void) } } -void +static void echo(char c) { char *p; @@ -454,7 +454,7 @@ echo(char c) (void)write(STDOUT_FILENO, buf, p - buf); } -void +static void stop(char cmdc) { mode(0); @@ -466,7 +466,7 @@ stop(char cmdc) } /* ARGSUSED */ -void +static void sigwinch(int signo __unused) { struct winsize ws; @@ -481,7 +481,7 @@ sigwinch(int signo __unused) /* * Send the window size to the server via the magic escape */ -void +static void sendwindow(void) { struct winsize ws; @@ -506,13 +506,13 @@ sendwindow(void) #define READING 1 #define WRITING 2 -jmp_buf rcvtop; -int rcvcnt, rcvstate; -pid_t ppid; -char rcvbuf[8 * 1024]; +static jmp_buf rcvtop; +static int rcvcnt, rcvstate; +static pid_t ppid; +static char rcvbuf[8 * 1024]; /* ARGSUSED */ -void +static void oob(int signo __unused) { struct termios tty; @@ -593,7 +593,7 @@ oob(int signo __unused) } /* reader: read from remote: line -> 1 */ -int +static int reader(int omask) { int n, remaining; @@ -636,7 +636,7 @@ reader(int omask) } } -void +static void mode(int f) { struct termios tty; @@ -667,7 +667,7 @@ mode(int f) } /* ARGSUSED */ -void +static void lostpeer(int signo __unused) { (void)signal(SIGPIPE, SIG_IGN); @@ -677,19 +677,19 @@ lostpeer(int signo __unused) /* copy SIGURGs to the child process via SIGUSR1. */ /* ARGSUSED */ -void +static void copytochild(int signo __unused) { (void)kill(child, SIGUSR1); } -void +static void msg(const char *str) { (void)fprintf(stderr, "rlogin: %s\r\n", str); } -void +static void usage(void) { (void)fprintf(stderr, @@ -698,7 +698,7 @@ usage(void) exit(1); } -u_int +static u_int getescape(const char *p) { long val; diff --git a/usr.bin/rpcgen/rpc_main.c b/usr.bin/rpcgen/rpc_main.c index a54c3e996c018..3755f4c993a4f 100644 --- a/usr.bin/rpcgen/rpc_main.c +++ b/usr.bin/rpcgen/rpc_main.c @@ -435,7 +435,7 @@ c_initialize(void) } -const char rpcgen_table_dcl[] = "struct rpcgen_table {\n\ +static const char rpcgen_table_dcl[] = "struct rpcgen_table {\n\ char *(*proc)(); \n\ xdrproc_t xdr_arg; \n\ unsigned len_arg; \n\ diff --git a/usr.bin/rpcgen/rpc_svcout.c b/usr.bin/rpcgen/rpc_svcout.c index 08cf94ce0e8bc..4b951a337df41 100644 --- a/usr.bin/rpcgen/rpc_svcout.c +++ b/usr.bin/rpcgen/rpc_svcout.c @@ -54,7 +54,7 @@ static char RESULT[] = "result"; static char ROUTINE[] = "local"; static char RETVAL[] = "retval"; -char _errbuf[256]; /* For all messages */ +static char _errbuf[256]; /* For all messages */ void internal_proctype( proc_list * ); static void write_real_program( definition * ); diff --git a/usr.bin/rpcgen/rpc_util.c b/usr.bin/rpcgen/rpc_util.c index 9d1c85be5f1cf..bf0ca5dee91f7 100644 --- a/usr.bin/rpcgen/rpc_util.c +++ b/usr.bin/rpcgen/rpc_util.c @@ -59,8 +59,8 @@ int linenum = 0; /* current line number */ const char *infilename; /* input filename */ #define NFILES 7 -const char *outfiles[NFILES]; /* output file names */ -int nfiles; +static const char *outfiles[NFILES]; /* output file names */ +static int nfiles; FILE *fout; /* file pointer of current output */ FILE *fin; /* file pointer of current input */ diff --git a/usr.bin/sort/sort.c b/usr.bin/sort/sort.c index 0668bdfc559b7..1f7f947e65df0 100644 --- a/usr.bin/sort/sort.c +++ b/usr.bin/sort/sort.c @@ -146,7 +146,7 @@ enum #define NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS 6 static const char mutually_exclusive_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] = { 'M', 'n', 'g', 'R', 'h', 'V' }; -struct option long_options[] = { +static struct option long_options[] = { { "batch-size", required_argument, NULL, BS_OPT }, { "buffer-size", required_argument, NULL, 'S' }, { "check", optional_argument, NULL, 'c' }, diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c index 31e2bd957bf27..984ec7b493b85 100644 --- a/usr.bin/stat/stat.c +++ b/usr.bin/stat/stat.c @@ -189,8 +189,8 @@ int format1(const struct stat *, /* stat info */ char *xfflagstostr(unsigned long); #endif -const char *timefmt; -int linkfail; +static const char *timefmt; +static int linkfail; #define addchar(s, c, nl) \ do { \ diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c index 669299bde06db..c0f508838d3d4 100644 --- a/usr.bin/tftp/main.c +++ b/usr.bin/tftp/main.c @@ -80,7 +80,7 @@ __FBSDID("$FreeBSD$"); typedef struct sockaddr_storage peeraddr; static int connected; static char mode[32]; -jmp_buf toplevel; +static jmp_buf toplevel; volatile int txrx_error; static int peer; @@ -89,7 +89,7 @@ static int margc; static char *margv[MAX_MARGV]; int verbose; -char *port = NULL; +static char *port = NULL; static void get(int, char **); static void help(int, char **); diff --git a/usr.bin/tset/extern.h b/usr.bin/tset/extern.h index 833870eee77c1..f4a6514fbd3ba 100644 --- a/usr.bin/tset/extern.h +++ b/usr.bin/tset/extern.h @@ -30,9 +30,12 @@ * $FreeBSD$ */ +#include + extern struct termios mode, oldmode; extern int Columns, isreset, Lines; extern int erasech, intrchar, killch; +extern speed_t Ospeed; void add_mapping(const char *, char *); void cat(char *); diff --git a/usr.bin/tset/map.c b/usr.bin/tset/map.c index 8de594fecdd65..715f51471705e 100644 --- a/usr.bin/tset/map.c +++ b/usr.bin/tset/map.c @@ -44,8 +44,7 @@ static const char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/9/93"; #include "extern.h" -extern speed_t Ospeed; -speed_t tset_baudrate(char *); +static speed_t tset_baudrate(char *); /* Baud rate conditionals for mapping. */ #define GT 0x01 @@ -63,7 +62,7 @@ typedef struct map { speed_t speed; /* Baud rate to compare against. */ } MAP; -MAP *cur, *maplist; +static MAP *cur, *maplist; /* * Syntax for -m: @@ -224,7 +223,7 @@ typedef struct speeds { speed_t speed; } SPEEDS; -SPEEDS speeds[] = { +static SPEEDS speeds[] = { { "0", B0 }, { "134.5", B134 }, { "exta", B19200 }, @@ -232,7 +231,7 @@ SPEEDS speeds[] = { { NULL, 0 } }; -speed_t +static speed_t tset_baudrate(char *rate) { SPEEDS *sp; diff --git a/usr.bin/tset/term.c b/usr.bin/tset/term.c index 544e9288bb289..106c98f22d35b 100644 --- a/usr.bin/tset/term.c +++ b/usr.bin/tset/term.c @@ -46,7 +46,7 @@ static const char sccsid[] = "@(#)term.c 8.1 (Berkeley) 6/9/93"; #include #include "extern.h" -char tbuf[1024]; /* Termcap entry. */ +static char tbuf[1024]; /* Termcap entry. */ const char *askuser(const char *); char *ttys(char *); diff --git a/usr.sbin/acpi/acpidump/acpi.c b/usr.sbin/acpi/acpidump/acpi.c index 85e5e0e88bbd0..b0a751c154f1d 100644 --- a/usr.sbin/acpi/acpidump/acpi.c +++ b/usr.sbin/acpi/acpidump/acpi.c @@ -340,12 +340,13 @@ acpi_print_local_nmi(u_int lint, uint16_t mps_flags) acpi_print_mps_flags(mps_flags); } -const char *apic_types[] = { "Local APIC", "IO APIC", "INT Override", "NMI", - "Local APIC NMI", "Local APIC Override", - "IO SAPIC", "Local SAPIC", "Platform Interrupt", - "Local X2APIC", "Local X2APIC NMI" }; -const char *platform_int_types[] = { "0 (unknown)", "PMI", "INIT", - "Corrected Platform Error" }; +static const char *apic_types[] = { "Local APIC", "IO APIC", "INT Override", + "NMI", "Local APIC NMI", + "Local APIC Override", "IO SAPIC", + "Local SAPIC", "Platform Interrupt", + "Local X2APIC", "Local X2APIC NMI" }; +static const char *platform_int_types[] = { "0 (unknown)", "PMI", "INIT", + "Corrected Platform Error" }; static void acpi_print_madt(ACPI_SUBTABLE_HEADER *mp) @@ -747,7 +748,7 @@ acpi_print_srat_memory(ACPI_SRAT_MEM_AFFINITY *mp) printf("\tProximity Domain=%d\n", mp->ProximityDomain); } -const char *srat_types[] = { "CPU", "Memory", "X2APIC" }; +static const char *srat_types[] = { "CPU", "Memory", "X2APIC" }; static void acpi_print_srat(ACPI_SUBTABLE_HEADER *srat) diff --git a/usr.sbin/acpi/acpidump/acpi_user.c b/usr.sbin/acpi/acpidump/acpi_user.c index 86e581f21f6d8..0b2beb2c4caf9 100644 --- a/usr.sbin/acpi/acpidump/acpi_user.c +++ b/usr.sbin/acpi/acpidump/acpi_user.c @@ -53,7 +53,7 @@ struct acpi_user_mapping { size_t size; }; -LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; +static LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; static void acpi_user_init(void) diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c index 8738612fe77da..c0d5a3132d8dc 100644 --- a/usr.sbin/cpucontrol/cpucontrol.c +++ b/usr.sbin/cpucontrol/cpucontrol.c @@ -86,7 +86,7 @@ struct datadir { }; static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs); -struct ucode_handler { +static struct ucode_handler { ucode_probe_t *probe; ucode_update_t *update; } handlers[] = { diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 9497101d03754..2a743da03d3b4 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -164,7 +164,7 @@ typedef enum { static const char rw_opts[] = "Nb:c:d:f:l:"; static const char startstop_opts[] = "io"; -struct ctladm_opts option_table[] = { +static struct ctladm_opts option_table[] = { {"adddev", CTLADM_CMD_ADDDEV, CTLADM_ARG_NONE, NULL}, {"bbrread", CTLADM_CMD_BBRREAD, CTLADM_ARG_NEED_TL, "d:l:"}, {"create", CTLADM_CMD_CREATE, CTLADM_ARG_NONE, "b:B:d:l:o:s:S:t:"}, @@ -572,7 +572,7 @@ typedef enum { CCTL_PORT_MODE_OFF } cctl_port_mode; -struct ctladm_opts cctl_fe_table[] = { +static struct ctladm_opts cctl_fe_table[] = { {"fc", CTL_PORT_FC, CTLADM_ARG_NONE, NULL}, {"scsi", CTL_PORT_SCSI, CTLADM_ARG_NONE, NULL}, {"internal", CTL_PORT_INTERNAL, CTLADM_ARG_NONE, NULL}, @@ -999,7 +999,7 @@ bailout: return (retval); } -struct ctladm_opts cctl_err_types[] = { +static struct ctladm_opts cctl_err_types[] = { {"aborted", CTL_LUN_INJ_ABORTED, CTLADM_ARG_NONE, NULL}, {"mediumerr", CTL_LUN_INJ_MEDIUM_ERR, CTLADM_ARG_NONE, NULL}, {"ua", CTL_LUN_INJ_UA, CTLADM_ARG_NONE, NULL}, @@ -1008,7 +1008,7 @@ struct ctladm_opts cctl_err_types[] = { }; -struct ctladm_opts cctl_err_patterns[] = { +static struct ctladm_opts cctl_err_patterns[] = { {"read", CTL_LUN_PAT_READ, CTLADM_ARG_NONE, NULL}, {"write", CTL_LUN_PAT_WRITE, CTLADM_ARG_NONE, NULL}, {"rw", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL}, diff --git a/usr.sbin/ctladm/util.c b/usr.sbin/ctladm/util.c index df13357d0360c..52d8d172332e7 100644 --- a/usr.sbin/ctladm/util.c +++ b/usr.sbin/ctladm/util.c @@ -56,7 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include "ctladm.h" -int verbose; +static int verbose; /* iget: Integer argument callback */ diff --git a/usr.sbin/gpioctl/gpioctl.c b/usr.sbin/gpioctl/gpioctl.c index 8956ef40d9bfc..9d76e1df63b21 100644 --- a/usr.sbin/gpioctl/gpioctl.c +++ b/usr.sbin/gpioctl/gpioctl.c @@ -43,7 +43,7 @@ struct flag_desc { uint32_t flag; }; -struct flag_desc gpio_flags[] = { +static struct flag_desc gpio_flags[] = { { "IN", GPIO_PIN_INPUT }, { "OUT", GPIO_PIN_OUTPUT }, { "OD", GPIO_PIN_OPENDRAIN }, diff --git a/usr.sbin/ip6addrctl/ip6addrctl.c b/usr.sbin/ip6addrctl/ip6addrctl.c index 7514522b852b1..6a730ff1fc30a 100644 --- a/usr.sbin/ip6addrctl/ip6addrctl.c +++ b/usr.sbin/ip6addrctl/ip6addrctl.c @@ -59,7 +59,7 @@ struct policyqueue { struct in6_addrpolicy pc_policy; }; TAILQ_HEAD(policyhead, policyqueue); -struct policyhead policyhead; +static struct policyhead policyhead; static void usage(void); static void get_policy(void); diff --git a/usr.sbin/mfiutil/mfi_flash.c b/usr.sbin/mfiutil/mfi_flash.c index 2601025a13f37..2b825291d3d67 100644 --- a/usr.sbin/mfiutil/mfi_flash.c +++ b/usr.sbin/mfiutil/mfi_flash.c @@ -42,8 +42,6 @@ #define FLASH_BUF_SIZE (64 * 1024) -int fw_name_width, fw_version_width, fw_date_width, fw_time_width; - static void scan_firmware(struct mfi_info_component *comp) { diff --git a/usr.sbin/mfiutil/mfi_show.c b/usr.sbin/mfiutil/mfi_show.c index 28a2604039183..c8b4377a1d3e3 100644 --- a/usr.sbin/mfiutil/mfi_show.c +++ b/usr.sbin/mfiutil/mfi_show.c @@ -570,8 +570,6 @@ error: } MFI_COMMAND(show, drives, show_drives); -int fw_name_width, fw_version_width, fw_date_width, fw_time_width; - static void scan_firmware(struct mfi_info_component *comp) { diff --git a/usr.sbin/mfiutil/mfiutil.c b/usr.sbin/mfiutil/mfiutil.c index 7a8833d3181df..b0b6353a779ff 100644 --- a/usr.sbin/mfiutil/mfiutil.c +++ b/usr.sbin/mfiutil/mfiutil.c @@ -44,8 +44,8 @@ MFI_TABLE(top, stop); MFI_TABLE(top, abort); int mfi_unit; - u_int mfi_opts; +int fw_name_width, fw_version_width, fw_date_width, fw_time_width; static void usage(void) diff --git a/usr.sbin/mfiutil/mfiutil.h b/usr.sbin/mfiutil/mfiutil.h index bb9366f8769a9..c696a05dfd192 100644 --- a/usr.sbin/mfiutil/mfiutil.h +++ b/usr.sbin/mfiutil/mfiutil.h @@ -122,6 +122,7 @@ struct mfiutil_command { extern int mfi_unit; extern u_int mfi_opts; +extern int fw_name_width, fw_version_width, fw_date_width, fw_time_width; void mbox_store_ldref(uint8_t *mbox, union mfi_ld_ref *ref); void mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref); diff --git a/usr.sbin/nfscbd/nfscbd.c b/usr.sbin/nfscbd/nfscbd.c index ba093118888cd..c9153b432f1ef 100644 --- a/usr.sbin/nfscbd/nfscbd.c +++ b/usr.sbin/nfscbd/nfscbd.c @@ -66,12 +66,12 @@ __FBSDID("$FreeBSD$"); /* Global defs */ #ifdef DEBUG #define syslog(e, s) fprintf(stderr,(s)) -int debug = 1; +static int debug = 1; #else -int debug = 0; +static int debug = 0; #endif -pid_t children; +static pid_t children; static void nonfs(int); static void reapchild(int); diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c index c5cec5ead4f41..7f6f659200c59 100644 --- a/usr.sbin/nfsd/nfsd.c +++ b/usr.sbin/nfsd/nfsd.c @@ -75,22 +75,22 @@ static const char rcsid[] = /* Global defs */ #ifdef DEBUG #define syslog(e, s...) fprintf(stderr,s) -int debug = 1; +static int debug = 1; #else -int debug = 0; +static int debug = 0; #endif #define NFSD_STABLERESTART "/var/db/nfs-stablerestart" #define NFSD_STABLEBACKUP "/var/db/nfs-stablerestart.bak" #define MAXNFSDCNT 256 #define DEFNFSDCNT 4 -pid_t children[MAXNFSDCNT]; /* PIDs of children */ -int nfsdcnt; /* number of children */ -int new_syscall; -int run_v4server = 1; /* Force running of nfsv4 server */ -int nfssvc_nfsd; /* Set to correct NFSSVC_xxx flag */ -int stablefd = -1; /* Fd for the stable restart file */ -int backupfd; /* Fd for the backup stable restart file */ +static pid_t children[MAXNFSDCNT]; /* PIDs of children */ +static int nfsdcnt; /* number of children */ +static int new_syscall; +static int run_v4server = 1; /* Force running of nfsv4 server */ +static int nfssvc_nfsd; /* Set to correct NFSSVC_xxx flag */ +static int stablefd = -1; /* Fd for the stable restart file */ +static int backupfd; /* Fd for the backup stable restart file */ void cleanup(int); void child_cleanup(int); diff --git a/usr.sbin/pkg/elf_tables.h b/usr.sbin/pkg/elf_tables.h index e02a2f765c003..d3d689a99dcd8 100644 --- a/usr.sbin/pkg/elf_tables.h +++ b/usr.sbin/pkg/elf_tables.h @@ -34,7 +34,7 @@ struct _elf_corres { const char *string; }; -struct _elf_corres mach_corres[] = { +static struct _elf_corres mach_corres[] = { { EM_386, "x86" }, { EM_AMD64, "x86" }, { EM_ARM, "arm" }, @@ -46,23 +46,18 @@ struct _elf_corres mach_corres[] = { { -1, NULL }, }; -struct _elf_corres wordsize_corres[] = { +static struct _elf_corres wordsize_corres[] = { { ELFCLASS32, "32" }, { ELFCLASS64, "64" }, { -1, NULL}, }; -struct _elf_corres endian_corres[] = { +static struct _elf_corres endian_corres[] = { { ELFDATA2MSB, "eb" }, { ELFDATA2LSB, "el" }, { -1, NULL} }; -struct _elf_corres os_corres[] = { - { ELFOSABI_FREEBSD, "freebsd" }, - { -1, NULL } -}; - #define EF_MIPS_ABI 0x0000F000 #define E_MIPS_ABI_O32 0x00001000 #define E_MIPS_ABI_N32 0x00000020 diff --git a/usr.sbin/pkg_install/version/perform.c b/usr.sbin/pkg_install/version/perform.c index a5ac36fb5cb4b..426d979bdbaf0 100644 --- a/usr.sbin/pkg_install/version/perform.c +++ b/usr.sbin/pkg_install/version/perform.c @@ -27,9 +27,9 @@ __FBSDID("$FreeBSD$"); #include #include -FILE *IndexFile; -char IndexPath[PATH_MAX] = ""; -struct index_head Index = SLIST_HEAD_INITIALIZER(Index); +static FILE *IndexFile; +static char IndexPath[PATH_MAX] = ""; +static struct index_head Index = SLIST_HEAD_INITIALIZER(Index); static int pkg_do(char *); static void show_version(Package, const char *, const char *); diff --git a/usr.sbin/pmccontrol/pmccontrol.c b/usr.sbin/pmccontrol/pmccontrol.c index bc0ebdccfd55a..b3585d042d133 100644 --- a/usr.sbin/pmccontrol/pmccontrol.c +++ b/usr.sbin/pmccontrol/pmccontrol.c @@ -66,7 +66,7 @@ __FBSDID("$FreeBSD$"); #define PMCC_PROGRAM_NAME "pmccontrol" -STAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head); +static STAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head); struct pmcc_op { char op_cpu; @@ -94,7 +94,7 @@ static char usage_message[] = " " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ..."; #if DEBUG -FILE *debug_stream = NULL; +static FILE *debug_stream = NULL; #endif #if DEBUG @@ -104,15 +104,6 @@ FILE *debug_stream = NULL; #define DEBUG_MSG(m) /* */ #endif /* !DEBUG */ -int pmc_syscall = -1; - -#define PMC_CALL(cmd, params) \ -if ((error = syscall(pmc_syscall, PMC_OP_##cmd, (params))) != 0) \ -{ \ - DEBUG_MSG("ERROR: syscall [" #cmd "]"); \ - exit(EX_OSERR); \ -} - #if DEBUG /* log debug messages to a separate file */ static void diff --git a/usr.sbin/pmcstat/pmcpl_calltree.c b/usr.sbin/pmcstat/pmcpl_calltree.c index 6b8bc61241e17..3d0127d5a20d1 100644 --- a/usr.sbin/pmcstat/pmcpl_calltree.c +++ b/usr.sbin/pmcstat/pmcpl_calltree.c @@ -125,7 +125,7 @@ struct pmcpl_ct_node_hash { STAILQ_ENTRY(pmcpl_ct_node_hash) pch_next; }; -struct pmcpl_ct_sample pmcpl_ct_callid; +static struct pmcpl_ct_sample pmcpl_ct_callid; #define PMCPL_CT_MAXCOL PMC_CALLCHAIN_DEPTH_MAX #define PMCPL_CT_MAXLINE 1024 /* TODO: dynamic. */ @@ -135,8 +135,8 @@ struct pmcpl_ct_line { unsigned ln_index; }; -struct pmcpl_ct_line pmcpl_ct_topmax[PMCPL_CT_MAXLINE+1]; -struct pmcpl_ct_node +static struct pmcpl_ct_line pmcpl_ct_topmax[PMCPL_CT_MAXLINE+1]; +static struct pmcpl_ct_node *pmcpl_ct_topscreen[PMCPL_CT_MAXCOL+1][PMCPL_CT_MAXLINE+1]; /* diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c index 6808eedab2024..cc43b820690d8 100644 --- a/usr.sbin/pmcstat/pmcstat.c +++ b/usr.sbin/pmcstat/pmcstat.c @@ -105,13 +105,12 @@ __FBSDID("$FreeBSD$"); /* Globals */ -int pmcstat_interrupt = 0; -int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; -int pmcstat_displaywidth = DEFAULT_DISPLAY_WIDTH; -int pmcstat_sockpair[NSOCKPAIRFD]; -int pmcstat_kq; -kvm_t *pmcstat_kvm; -struct kinfo_proc *pmcstat_plist; +int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; +int pmcstat_displaywidth = DEFAULT_DISPLAY_WIDTH; +static int pmcstat_sockpair[NSOCKPAIRFD]; +static int pmcstat_kq; +static kvm_t *pmcstat_kvm; +static struct kinfo_proc *pmcstat_plist; struct pmcstat_args args; static void diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c index e5fee35a1c4dd..fdcf9c4037027 100644 --- a/usr.sbin/pmcstat/pmcstat_log.c +++ b/usr.sbin/pmcstat/pmcstat_log.c @@ -142,7 +142,7 @@ struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH]; struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH]; struct pmcstat_stats pmcstat_stats; /* statistics */ -int ps_samples_period; /* samples count between top refresh. */ +static int ps_samples_period; /* samples count between top refresh. */ struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ @@ -151,7 +151,7 @@ struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ #include "pmcpl_annotate.h" #include "pmcpl_calltree.h" -struct pmc_plugins { +static struct pmc_plugins { const char *pl_name; /* name */ /* configure */ @@ -218,7 +218,7 @@ struct pmc_plugins { } }; -int pmcstat_mergepmc; +static int pmcstat_mergepmc; int pmcstat_pmcinfilter = 0; /* PMC filter for top mode. */ float pmcstat_threshold = 0.5; /* Cost filter for top mode. */ @@ -275,7 +275,7 @@ int pmcstat_npmcs; /* * PMC Top mode pause state. */ -int pmcstat_pause; +static int pmcstat_pause; static void pmcstat_stats_reset(int reset_global) diff --git a/usr.sbin/pmcstat/pmcstat_log.h b/usr.sbin/pmcstat/pmcstat_log.h index bfa925c52221b..b4ced4d9397bb 100644 --- a/usr.sbin/pmcstat/pmcstat_log.h +++ b/usr.sbin/pmcstat/pmcstat_log.h @@ -176,8 +176,8 @@ extern int pmcstat_npmcs; /* PMC count. */ /* * Top mode global options. */ -float pmcstat_threshold; /* Threshold to filter node. */ -int pmcstat_pmcinfilter; /* PMC index displayed. */ +extern float pmcstat_threshold; /* Threshold to filter node. */ +extern int pmcstat_pmcinfilter; /* PMC index displayed. */ /* Function prototypes */ const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid); diff --git a/usr.sbin/quotaon/quotaon.c b/usr.sbin/quotaon/quotaon.c index 8d52983b43d5c..740df39c0a037 100644 --- a/usr.sbin/quotaon/quotaon.c +++ b/usr.sbin/quotaon/quotaon.c @@ -59,16 +59,15 @@ __FBSDID("$FreeBSD$"); #include #include -const char *qfname = QUOTAFILENAME; -const char *qfextension[] = INITQFNAMES; +static const char *qfextension[] = INITQFNAMES; -int aflag; /* all filesystems */ -int gflag; /* operate on group quotas */ -int uflag; /* operate on user quotas */ -int vflag; /* verbose */ +static int aflag; /* all filesystems */ +static int gflag; /* operate on group quotas */ +static int uflag; /* operate on user quotas */ +static int vflag; /* verbose */ -int oneof(char *, char *[], int); -int quotaonoff(struct fstab *fs, int, int); +static int oneof(char *, char *[], int); +static int quotaonoff(struct fstab *fs, int, int); static void usage(void); int @@ -150,7 +149,7 @@ usage(void) exit(1); } -int +static int quotaonoff(struct fstab *fs, int offmode, int type) { struct quotafile *qf; @@ -181,7 +180,7 @@ quotaonoff(struct fstab *fs, int offmode, int type) /* * Check to see if target appears in list of size cnt. */ -int +static int oneof(char *target, char *list[], int cnt) { int i; diff --git a/usr.sbin/repquota/repquota.c b/usr.sbin/repquota/repquota.c index 8fdea201554ff..4ffc2e68dd95c 100644 --- a/usr.sbin/repquota/repquota.c +++ b/usr.sbin/repquota/repquota.c @@ -79,8 +79,7 @@ __FBSDID("$FreeBSD$"); #define max(a,b) ((a) >= (b) ? (a) : (b)) -const char *qfname = QUOTAFILENAME; -const char *qfextension[] = INITQFNAMES; +static const char *qfextension[] = INITQFNAMES; struct fileusage { struct fileusage *fu_next; @@ -89,15 +88,15 @@ struct fileusage { /* actually bigger */ }; #define FUHASH 1024 /* must be power of two */ -struct fileusage *fuhead[MAXQUOTAS][FUHASH]; -struct fileusage *lookup(u_long, int); -struct fileusage *addid(u_long, int, char *); -u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ +static struct fileusage *fuhead[MAXQUOTAS][FUHASH]; +static struct fileusage *lookup(u_long, int); +static struct fileusage *addid(u_long, int, char *); +static u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ -int vflag; /* verbose */ -int aflag; /* all filesystems */ -int nflag; /* display user/group by id */ -int hflag; /* display in human readable format */ +static int vflag; /* verbose */ +static int aflag; /* all filesystems */ +static int nflag; /* display user/group by id */ +static int hflag; /* display in human readable format */ int oneof(char *, char *[], int); int repquota(struct fstab *, int); diff --git a/usr.sbin/vidcontrol/vidcontrol.c b/usr.sbin/vidcontrol/vidcontrol.c index 630de7495e84a..d07f8927ac628 100644 --- a/usr.sbin/vidcontrol/vidcontrol.c +++ b/usr.sbin/vidcontrol/vidcontrol.c @@ -70,7 +70,7 @@ static const char *legal_colors[16] = { "lightred", "lightmagenta", "yellow", "lightwhite" }; -struct { +static struct { int active_vty; vid_info_t console_info; unsigned char screen_map[256]; -- cgit v1.3 From db85bb0492c332dd0f382db40847350676175a8c Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 19 Oct 2012 22:48:22 +0000 Subject: Indent ecaps the same way we indent caps. MFC after: 3 days --- usr.sbin/pciconf/cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index 0dff0b2342711..4d3fab9a708c0 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -660,7 +660,7 @@ list_ecaps(int fd, struct pci_conf *p) if (ecap == 0xffffffff || ecap == 0) return; for (;;) { - printf("ecap %04x[%03x] = ", PCI_EXTCAP_ID(ecap), ptr); + printf(" ecap %04x[%03x] = ", PCI_EXTCAP_ID(ecap), ptr); switch (PCI_EXTCAP_ID(ecap)) { case PCIZ_AER: ecap_aer(fd, p, ptr, PCI_EXTCAP_VER(ecap)); -- cgit v1.3 From 186475e2ddef1d1675250461ba544cfef31e587b Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sat, 20 Oct 2012 10:20:06 +0000 Subject: Remove redundant code. Both mfi_flash.c and mfi_show.c contain very similar functions to print a list of firmwares. Move these routines into mfiutil.c. Reported by: jhb --- usr.sbin/mfiutil/mfi_flash.c | 32 ++------------------------------ usr.sbin/mfiutil/mfi_show.c | 28 ---------------------------- usr.sbin/mfiutil/mfiutil.c | 30 +++++++++++++++++++++++++++++- usr.sbin/mfiutil/mfiutil.h | 5 ++++- 4 files changed, 35 insertions(+), 60 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/mfiutil/mfi_flash.c b/usr.sbin/mfiutil/mfi_flash.c index 2b825291d3d67..d640cf75c814f 100644 --- a/usr.sbin/mfiutil/mfi_flash.c +++ b/usr.sbin/mfiutil/mfi_flash.c @@ -42,34 +42,6 @@ #define FLASH_BUF_SIZE (64 * 1024) -static void -scan_firmware(struct mfi_info_component *comp) -{ - int len; - - len = strlen(comp->name); - if (fw_name_width < len) - fw_name_width = len; - len = strlen(comp->version); - if (fw_version_width < len) - fw_version_width = len; - len = strlen(comp->build_date); - if (fw_date_width < len) - fw_date_width = len; - len = strlen(comp->build_time); - if (fw_time_width < len) - fw_time_width = len; -} - -static void -display_firmware(struct mfi_info_component *comp) -{ - - printf("%-*s %-*s %-*s %-*s\n", fw_name_width, comp->name, - fw_version_width, comp->version, fw_date_width, comp->build_date, - fw_time_width, comp->build_time); -} - static int display_pending_firmware(int fd) { @@ -94,9 +66,9 @@ display_pending_firmware(int fd) info.pending_image_component_count = 8; for (i = 0; i < info.pending_image_component_count; i++) scan_firmware(&info.pending_image_component[i]); - display_firmware(&header); + display_firmware(&header, ""); for (i = 0; i < info.pending_image_component_count; i++) - display_firmware(&info.pending_image_component[i]); + display_firmware(&info.pending_image_component[i], ""); return (0); } diff --git a/usr.sbin/mfiutil/mfi_show.c b/usr.sbin/mfiutil/mfi_show.c index c8b4377a1d3e3..be395bb118028 100644 --- a/usr.sbin/mfiutil/mfi_show.c +++ b/usr.sbin/mfiutil/mfi_show.c @@ -570,34 +570,6 @@ error: } MFI_COMMAND(show, drives, show_drives); -static void -scan_firmware(struct mfi_info_component *comp) -{ - int len; - - len = strlen(comp->name); - if (fw_name_width < len) - fw_name_width = len; - len = strlen(comp->version); - if (fw_version_width < len) - fw_version_width = len; - len = strlen(comp->build_date); - if (fw_date_width < len) - fw_date_width = len; - len = strlen(comp->build_time); - if (fw_time_width < len) - fw_time_width = len; -} - -static void -display_firmware(struct mfi_info_component *comp, const char *tag) -{ - - printf("%-*s %-*s %-*s %-*s %s\n", fw_name_width, comp->name, - fw_version_width, comp->version, fw_date_width, comp->build_date, - fw_time_width, comp->build_time, tag); -} - static int show_firmware(int ac, char **av __unused) { diff --git a/usr.sbin/mfiutil/mfiutil.c b/usr.sbin/mfiutil/mfiutil.c index b0b6353a779ff..79c6a4b3ec9be 100644 --- a/usr.sbin/mfiutil/mfiutil.c +++ b/usr.sbin/mfiutil/mfiutil.c @@ -45,7 +45,7 @@ MFI_TABLE(top, abort); int mfi_unit; u_int mfi_opts; -int fw_name_width, fw_version_width, fw_date_width, fw_time_width; +static int fw_name_width, fw_version_width, fw_date_width, fw_time_width; static void usage(void) @@ -144,3 +144,31 @@ main(int ac, char **av) warnx("Unknown command %s.", av[0]); return (1); } + +void +scan_firmware(struct mfi_info_component *comp) +{ + int len; + + len = strlen(comp->name); + if (fw_name_width < len) + fw_name_width = len; + len = strlen(comp->version); + if (fw_version_width < len) + fw_version_width = len; + len = strlen(comp->build_date); + if (fw_date_width < len) + fw_date_width = len; + len = strlen(comp->build_time); + if (fw_time_width < len) + fw_time_width = len; +} + +void +display_firmware(struct mfi_info_component *comp, const char *tag) +{ + + printf("%-*s %-*s %-*s %-*s %s\n", fw_name_width, comp->name, + fw_version_width, comp->version, fw_date_width, comp->build_date, + fw_time_width, comp->build_time, tag); +} diff --git a/usr.sbin/mfiutil/mfiutil.h b/usr.sbin/mfiutil/mfiutil.h index c696a05dfd192..687bdd9fb8b92 100644 --- a/usr.sbin/mfiutil/mfiutil.h +++ b/usr.sbin/mfiutil/mfiutil.h @@ -121,8 +121,8 @@ struct mfiutil_command { #define MFI_DNAME_HONOR_OPTS 0x8000 /* Allow cmd line to override default */ extern int mfi_unit; + extern u_int mfi_opts; -extern int fw_name_width, fw_version_width, fw_date_width, fw_time_width; void mbox_store_ldref(uint8_t *mbox, union mfi_ld_ref *ref); void mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref); @@ -153,4 +153,7 @@ const char *mfi_status(u_int status_code); const char *mfi_drive_name(struct mfi_pd_info *pinfo, uint16_t device_id, uint32_t def); +void scan_firmware(struct mfi_info_component *comp); +void display_firmware(struct mfi_info_component *comp, const char *tag); + #endif /* !__MFIUTIL_H__ */ -- cgit v1.3 From eccad222770297bdd2787e31cd37b906edba62c6 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sat, 20 Oct 2012 10:33:15 +0000 Subject: More -Wmissing-variable-declarations fixes. In addition to adding missing `static' keywords: - bin/dd: Pull in `extern.h' to guarantee consistency with source file. - libexec/rpc.rusersd: Move shared globals into an extern.h. - libexec/talkd: Move `debug' and `hostname' into extern.h. - usr.bin/cksum: Put counters in extern.h, as they are used by ckdist/mtree. - usr.bin/m4: Move `end_result' into extern.h. - usr.sbin/services_mkdb: Move shared globals into an extern.h. --- bin/dd/conv_tab.c | 5 ++++ libexec/rpc.rusersd/Makefile | 2 +- libexec/rpc.rusersd/extern.h | 38 +++++++++++++++++++++++++++++ libexec/rpc.rusersd/rusers_proc.c | 6 ++--- libexec/rpc.rusersd/rusersd.c | 2 +- libexec/talkd/announce.c | 2 -- libexec/talkd/extern.h | 3 +++ libexec/talkd/process.c | 2 -- libexec/talkd/table.c | 5 ++-- libexec/talkd/talkd.c | 10 ++++---- usr.bin/cksum/extern.h | 3 +++ usr.bin/m4/expr.c | 2 +- usr.bin/m4/extern.h | 1 + usr.bin/m4/gnum4.c | 2 +- usr.bin/m4/main.c | 2 +- usr.bin/m4/parser.y | 9 ++++++- usr.sbin/mtree/mtree.c | 3 ++- usr.sbin/newsyslog/newsyslog.c | 44 ++++++++++++++++++---------------- usr.sbin/services_mkdb/Makefile | 2 +- usr.sbin/services_mkdb/extern.h | 32 +++++++++++++++++++++++++ usr.sbin/services_mkdb/services_mkdb.c | 2 ++ usr.sbin/services_mkdb/uniq.c | 2 +- 22 files changed, 133 insertions(+), 46 deletions(-) create mode 100644 libexec/rpc.rusersd/extern.h create mode 100644 usr.sbin/services_mkdb/extern.h (limited to 'usr.sbin') diff --git a/bin/dd/conv_tab.c b/bin/dd/conv_tab.c index 9bfd10921823e..07449fd0f572b 100644 --- a/bin/dd/conv_tab.c +++ b/bin/dd/conv_tab.c @@ -41,6 +41,11 @@ __FBSDID("$FreeBSD$"); #include +#include + +#include "dd.h" +#include "extern.h" + /* * There are currently six tables: * diff --git a/libexec/rpc.rusersd/Makefile b/libexec/rpc.rusersd/Makefile index 6305343ff9f9b..df2be379d0865 100644 --- a/libexec/rpc.rusersd/Makefile +++ b/libexec/rpc.rusersd/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ PROG = rpc.rusersd -SRCS = rusersd.c rusers_proc.c +SRCS = rusersd.c rusers_proc.c extern.h MAN = rpc.rusersd.8 DPADD= ${LIBRPCSVC} ${LIBUTIL} diff --git a/libexec/rpc.rusersd/extern.h b/libexec/rpc.rusersd/extern.h new file mode 100644 index 0000000000000..b1a8309bf85f9 --- /dev/null +++ b/libexec/rpc.rusersd/extern.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +extern int from_inetd; + +void rusers_service(struct svc_req *, SVCXPRT *); diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c index 6b34fa72cd286..ff2ebc4c63ed1 100644 --- a/libexec/rpc.rusersd/rusers_proc.c +++ b/libexec/rpc.rusersd/rusers_proc.c @@ -53,6 +53,8 @@ static const char rcsid[] = #endif #include +#include "extern.h" + #ifndef _PATH_DEV #define _PATH_DEV "/dev" #endif @@ -61,10 +63,6 @@ static utmpidle utmp_idle[MAXUSERS]; static utmp old_utmp[MAXUSERS]; static struct utmpx utmp_list[MAXUSERS]; -extern int from_inetd; - -void rusers_service(struct svc_req *, SVCXPRT *); - #ifdef XIDLE static Display *dpy; diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c index 009bbfde85e4b..0ad468650a9f5 100644 --- a/libexec/rpc.rusersd/rusersd.c +++ b/libexec/rpc.rusersd/rusersd.c @@ -43,7 +43,7 @@ static const char rcsid[] = #include #include -extern void rusers_service(struct svc_req *, SVCXPRT *); +#include "extern.h" int from_inetd = 1; diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c index 869197e5c52c7..83c96b724c01b 100644 --- a/libexec/talkd/announce.c +++ b/libexec/talkd/announce.c @@ -60,8 +60,6 @@ static const char rcsid[] = #include "ttymsg.h" #include "extern.h" -extern char hostname[]; - /* * Announce an invitation to talk. */ diff --git a/libexec/talkd/extern.h b/libexec/talkd/extern.h index cb231a95d1348..a91cb25a71e19 100644 --- a/libexec/talkd/extern.h +++ b/libexec/talkd/extern.h @@ -25,6 +25,9 @@ * $FreeBSD$ */ +extern int debug; +extern char hostname[]; + int announce(CTL_MSG *, const char *); int delete_invite(u_int32_t); void do_announce(CTL_MSG *, CTL_RESPONSE *); diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c index 18ddc42aff1f2..1c5108fc7d59a 100644 --- a/libexec/talkd/process.c +++ b/libexec/talkd/process.c @@ -63,8 +63,6 @@ static const char rcsid[] = #include "extern.h" -extern int debug; - void process_request(CTL_MSG *mp, CTL_RESPONSE *rp) { diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c index dcaed9c2a3e37..5ad7b61bdf7a1 100644 --- a/libexec/talkd/table.c +++ b/libexec/talkd/table.c @@ -64,8 +64,7 @@ static const char rcsid[] = #define NIL ((TABLE_ENTRY *)0) -extern int debug; -struct timeval tp; +static struct timeval tp; typedef struct table_entry TABLE_ENTRY; @@ -78,7 +77,7 @@ struct table_entry { static void delete(TABLE_ENTRY *); -TABLE_ENTRY *table = NIL; +static TABLE_ENTRY *table = NIL; /* * Look in the table for an invitation that matches the current diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c index e81cec6eb74d9..56ce4414c1062 100644 --- a/libexec/talkd/talkd.c +++ b/libexec/talkd/talkd.c @@ -69,13 +69,13 @@ static const char rcsid[] = #include "extern.h" -CTL_MSG request; -CTL_RESPONSE response; +static CTL_MSG request; +static CTL_RESPONSE response; -int debug = 0; -long lastmsgtime; +int debug = 0; +static long lastmsgtime; -char hostname[MAXHOSTNAMELEN]; +char hostname[MAXHOSTNAMELEN]; #define TIMEOUT 30 #define MAXIDLE 120 diff --git a/usr.bin/cksum/extern.h b/usr.bin/cksum/extern.h index 7cfe5079d0926..84ec8d2cd1539 100644 --- a/usr.bin/cksum/extern.h +++ b/usr.bin/cksum/extern.h @@ -32,6 +32,9 @@ #include +extern uint32_t crc_total; +extern uint32_t crc32_total; + __BEGIN_DECLS int crc(int, uint32_t *, off_t *); void pcrc(char *, uint32_t, off_t); diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c index 85bd8f1e912bc..8a64217b5768d 100644 --- a/usr.bin/m4/expr.c +++ b/usr.bin/m4/expr.c @@ -24,7 +24,7 @@ __FBSDID("$FreeBSD$"); #include "extern.h" int32_t end_result; -const char *copy_toeval; +static const char *copy_toeval; int yyerror(const char *msg); extern void yy_scan_string(const char *); diff --git a/usr.bin/m4/extern.h b/usr.bin/m4/extern.h index 9054d22860b74..ccb2e77045e77 100644 --- a/usr.bin/m4/extern.h +++ b/usr.bin/m4/extern.h @@ -43,6 +43,7 @@ extern unsigned long expansion_id; /* expr.c */ extern int expr(const char *); +extern int32_t end_result; /* gnum4.c */ extern void addtoincludepath(const char *); diff --git a/usr.bin/m4/gnum4.c b/usr.bin/m4/gnum4.c index 861279c06f314..342751a152151 100644 --- a/usr.bin/m4/gnum4.c +++ b/usr.bin/m4/gnum4.c @@ -59,7 +59,7 @@ int mimic_gnu = 0; * Then M4PATH env variable */ -struct path_entry { +static struct path_entry { char *name; struct path_entry *next; } *first, *last; diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c index 6b3cad43855d9..c8de97c8bbf40 100644 --- a/usr.bin/m4/main.c +++ b/usr.bin/m4/main.c @@ -85,7 +85,7 @@ struct keyblk { int ktyp; /* keyword type */ }; -struct keyblk keywrds[] = { /* m4 keywords to be installed */ +static struct keyblk keywrds[] = { /* m4 keywords to be installed */ { "include", INCLTYPE }, { "sinclude", SINCTYPE }, { "define", DEFITYPE }, diff --git a/usr.bin/m4/parser.y b/usr.bin/m4/parser.y index 14959738bdff4..2239cf39c18d5 100644 --- a/usr.bin/m4/parser.y +++ b/usr.bin/m4/parser.y @@ -17,10 +17,17 @@ * * $FreeBSD$ */ + #include +#include +#include #include + +#include "mdef.h" +#include "extern.h" + #define YYSTYPE int32_t -extern int32_t end_result; + extern int yylex(void); extern int yyerror(const char *); %} diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c index 49e3e6b56405a..e90a5bbca1d33 100644 --- a/usr.sbin/mtree/mtree.c +++ b/usr.sbin/mtree/mtree.c @@ -52,7 +52,8 @@ __FBSDID("$FreeBSD$"); #include "extern.h" int ftsoptions = FTS_PHYSICAL; -int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag; +int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag; +static int cflag, Uflag; u_int keys; char fullpath[MAXPATHLEN]; diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c index 67a363e55114f..875f9113abdc8 100644 --- a/usr.sbin/newsyslog/newsyslog.c +++ b/usr.sbin/newsyslog/newsyslog.c @@ -145,7 +145,7 @@ struct compress_types { const char *path; /* Path to compression program */ }; -const struct compress_types compress_type[COMPRESS_TYPES] = { +static const struct compress_types compress_type[COMPRESS_TYPES] = { { "", "", "" }, /* no compression */ { "Z", COMPRESS_SUFFIX_GZ, _PATH_GZIP }, /* gzip compression */ { "J", COMPRESS_SUFFIX_BZ2, _PATH_BZIP2 }, /* bzip2 compression */ @@ -206,42 +206,44 @@ typedef enum { } fk_entry; STAILQ_HEAD(cflist, conf_entry); -SLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead); -SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead); +static SLIST_HEAD(swlisthead, sigwork_entry) swhead = + SLIST_HEAD_INITIALIZER(swhead); +static SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = + SLIST_HEAD_INITIALIZER(zwhead); STAILQ_HEAD(ilist, include_entry); int dbg_at_times; /* -D Show details of 'trim_at' code */ -int archtodir = 0; /* Archive old logfiles to other directory */ -int createlogs; /* Create (non-GLOB) logfiles which do not */ +static int archtodir = 0; /* Archive old logfiles to other directory */ +static int createlogs; /* Create (non-GLOB) logfiles which do not */ /* already exist. 1=='for entries with */ /* C flag', 2=='for all entries'. */ int verbose = 0; /* Print out what's going on */ -int needroot = 1; /* Root privs are necessary */ +static int needroot = 1; /* Root privs are necessary */ int noaction = 0; /* Don't do anything, just show it */ -int norotate = 0; /* Don't rotate */ -int nosignal; /* Do not send any signals */ -int enforcepid = 0; /* If PID file does not exist or empty, do nothing */ -int force = 0; /* Force the trim no matter what */ -int rotatereq = 0; /* -R = Always rotate the file(s) as given */ +static int norotate = 0; /* Don't rotate */ +static int nosignal; /* Do not send any signals */ +static int enforcepid = 0; /* If PID file does not exist or empty, do nothing */ +static int force = 0; /* Force the trim no matter what */ +static int rotatereq = 0; /* -R = Always rotate the file(s) as given */ /* on the command (this also requires */ /* that a list of files *are* given on */ /* the run command). */ -char *requestor; /* The name given on a -R request */ -char *timefnamefmt = NULL; /* Use time based filenames instead of .0 etc */ -char *archdirname; /* Directory path to old logfiles archive */ -char *destdir = NULL; /* Directory to treat at root for logs */ -const char *conf; /* Configuration file to use */ +static char *requestor; /* The name given on a -R request */ +static char *timefnamefmt = NULL;/* Use time based filenames instead of .0 */ +static char *archdirname; /* Directory path to old logfiles archive */ +static char *destdir = NULL; /* Directory to treat at root for logs */ +static const char *conf; /* Configuration file to use */ struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */ -struct ptime_data *timenow; /* The time to use for checking at-fields */ +static struct ptime_data *timenow; /* The time to use for checking at-fields */ #define DAYTIME_LEN 16 -char daytime[DAYTIME_LEN]; /* The current time in human readable form, - * used for rotation-tracking messages. */ -char hostname[MAXHOSTNAMELEN]; /* hostname */ +static char daytime[DAYTIME_LEN];/* The current time in human readable form, + * used for rotation-tracking messages. */ +static char hostname[MAXHOSTNAMELEN]; /* hostname */ -const char *path_syslogpid = _PATH_SYSLOGPID; +static const char *path_syslogpid = _PATH_SYSLOGPID; static struct cflist *get_worklist(char **files); static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, diff --git a/usr.sbin/services_mkdb/Makefile b/usr.sbin/services_mkdb/Makefile index 659cdb8163e55..e61c6dfa7d16a 100644 --- a/usr.sbin/services_mkdb/Makefile +++ b/usr.sbin/services_mkdb/Makefile @@ -2,7 +2,7 @@ PROG= services_mkdb MAN= services_mkdb.8 -SRCS= services_mkdb.c uniq.c +SRCS= services_mkdb.c uniq.c extern.h DPADD+= ${LIBUTIL} LDADD+= -lutil diff --git a/usr.sbin/services_mkdb/extern.h b/usr.sbin/services_mkdb/extern.h new file mode 100644 index 0000000000000..f0976ef87a061 --- /dev/null +++ b/usr.sbin/services_mkdb/extern.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +extern const HASHINFO hinfo; diff --git a/usr.sbin/services_mkdb/services_mkdb.c b/usr.sbin/services_mkdb/services_mkdb.c index f4cf62ac414f8..d81488cf15ebe 100644 --- a/usr.sbin/services_mkdb/services_mkdb.c +++ b/usr.sbin/services_mkdb/services_mkdb.c @@ -49,6 +49,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include "extern.h" + static char tname[MAXPATHLEN]; #define PMASK 0xffff diff --git a/usr.sbin/services_mkdb/uniq.c b/usr.sbin/services_mkdb/uniq.c index 0674b4bbc407e..b6d06d44fd9c8 100644 --- a/usr.sbin/services_mkdb/uniq.c +++ b/usr.sbin/services_mkdb/uniq.c @@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include -extern const HASHINFO hinfo; +#include "extern.h" void uniq(const char *); static int comp(const char *, char **, size_t *); -- cgit v1.3 From 59f3e1891c862b2097c376a2eb2fe3e0ebec9835 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sat, 20 Oct 2012 10:34:55 +0000 Subject: Move uniq() prototype into extern.h. --- usr.sbin/services_mkdb/extern.h | 2 ++ usr.sbin/services_mkdb/services_mkdb.c | 2 -- usr.sbin/services_mkdb/uniq.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/services_mkdb/extern.h b/usr.sbin/services_mkdb/extern.h index f0976ef87a061..94e31d30de44d 100644 --- a/usr.sbin/services_mkdb/extern.h +++ b/usr.sbin/services_mkdb/extern.h @@ -30,3 +30,5 @@ */ extern const HASHINFO hinfo; + +void uniq(const char *); diff --git a/usr.sbin/services_mkdb/services_mkdb.c b/usr.sbin/services_mkdb/services_mkdb.c index d81488cf15ebe..7f5316c7c4198 100644 --- a/usr.sbin/services_mkdb/services_mkdb.c +++ b/usr.sbin/services_mkdb/services_mkdb.c @@ -56,8 +56,6 @@ static char tname[MAXPATHLEN]; #define PMASK 0xffff #define PROTOMAX 5 -extern void uniq(const char *); - static void add(DB *, StringList *, size_t, const char *, size_t *, int); static StringList ***parseservices(const char *, StringList *); static void cleanup(void); diff --git a/usr.sbin/services_mkdb/uniq.c b/usr.sbin/services_mkdb/uniq.c index b6d06d44fd9c8..e1bac38549345 100644 --- a/usr.sbin/services_mkdb/uniq.c +++ b/usr.sbin/services_mkdb/uniq.c @@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$"); #include "extern.h" -void uniq(const char *); static int comp(const char *, char **, size_t *); /* -- cgit v1.3 From d3522c026f32cd760a7cc747ad6b8d459edc701a Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Mon, 22 Oct 2012 02:12:15 +0000 Subject: Fix conditional expression: previously a bit vector was being operated on by a logical not instead of the entire expression. Approved by: cperciva MFC after: 3 days --- usr.sbin/memcontrol/memcontrol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/memcontrol/memcontrol.c b/usr.sbin/memcontrol/memcontrol.c index 542bc3a1a647e..36329af79c544 100644 --- a/usr.sbin/memcontrol/memcontrol.c +++ b/usr.sbin/memcontrol/memcontrol.c @@ -233,7 +233,7 @@ setfunc(int memfd, int argc, char *argv[]) while(argc--) { for (i = 0; attrnames[i].name != NULL; i++) { if (!strcmp(attrnames[i].name, argv[0])) { - if (!attrnames[i].kind & MDF_SETTABLE) + if (!(attrnames[i].kind & MDF_SETTABLE)) help("flags"); mrd.mr_flags |= attrnames[i].val; break; -- cgit v1.3 From 529d9f352adbd76589a2e0d7c491ffd1afb29b06 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Mon, 22 Oct 2012 02:12:20 +0000 Subject: Warn users when using pkg tools if it looks like they be be pkgng users. Reviewed by: bapt (earlier version) Reviewed by: kwm Approved by: cperciva MFC after: 3 days --- usr.sbin/pkg_install/add/main.c | 1 + usr.sbin/pkg_install/create/main.c | 1 + usr.sbin/pkg_install/delete/main.c | 1 + usr.sbin/pkg_install/info/main.c | 1 + usr.sbin/pkg_install/lib/Makefile | 2 +- usr.sbin/pkg_install/lib/lib.h | 1 + usr.sbin/pkg_install/lib/pkgng.c | 38 ++++++++++++++++++++++++++++++++++++ usr.sbin/pkg_install/updating/main.c | 1 + usr.sbin/pkg_install/version/main.c | 1 + 9 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 usr.sbin/pkg_install/lib/pkgng.c (limited to 'usr.sbin') diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c index 5932e1acb23ce..94ef1278ced42 100644 --- a/usr.sbin/pkg_install/add/main.c +++ b/usr.sbin/pkg_install/add/main.c @@ -135,6 +135,7 @@ main(int argc, char **argv) static char temppackageroot[MAXPATHLEN]; static char pkgaddpath[MAXPATHLEN]; + warnpkgng(); if (*argv[0] != '/' && strchr(argv[0], '/') != NULL) PkgAddCmd = realpath(argv[0], pkgaddpath); else diff --git a/usr.sbin/pkg_install/create/main.c b/usr.sbin/pkg_install/create/main.c index 5b5ecb2e10c7e..4f68697b85ed6 100644 --- a/usr.sbin/pkg_install/create/main.c +++ b/usr.sbin/pkg_install/create/main.c @@ -72,6 +72,7 @@ main(int argc, char **argv) int ch; char **pkgs, **start, *tmp; + warnpkgng(); pkgs = start = argv; while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) switch(ch) { diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c index 7cd4677d11260..0b97ddde273ee 100644 --- a/usr.sbin/pkg_install/delete/main.c +++ b/usr.sbin/pkg_install/delete/main.c @@ -67,6 +67,7 @@ main(int argc, char **argv) const char *tmp; struct stat stat_s; + warnpkgng(); pkgs = start = argv; while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) switch(ch) { diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c index 6692148d9f6fb..ff2ea79d3e853 100644 --- a/usr.sbin/pkg_install/info/main.c +++ b/usr.sbin/pkg_install/info/main.c @@ -68,6 +68,7 @@ main(int argc, char **argv) char **pkgs, **start; char *pkgs_split; + warnpkgng(); whead = malloc(sizeof(struct which_head)); if (whead == NULL) err(2, NULL); diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile index 84a41b8d7f156..12cc3073eb768 100644 --- a/usr.sbin/pkg_install/lib/Makefile +++ b/usr.sbin/pkg_install/lib/Makefile @@ -3,7 +3,7 @@ LIB= install INTERNALLIB= SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c match.c \ - deps.c version.c pkgwrap.c url.c + deps.c version.c pkgwrap.c url.c pkgng.c WARNS?= 3 WFORMAT?= 1 diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index a6935ade78bd5..88fcf34d72d92 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -157,6 +157,7 @@ const char *make_playpen(char *, off_t); char *where_playpen(void); int leave_playpen(void); off_t min_free(const char *); +void warnpkgng(void); /* String */ char *get_dash_string(char **); diff --git a/usr.sbin/pkg_install/lib/pkgng.c b/usr.sbin/pkg_install/lib/pkgng.c new file mode 100644 index 0000000000000..76c8ad85934de --- /dev/null +++ b/usr.sbin/pkg_install/lib/pkgng.c @@ -0,0 +1,38 @@ +/* + * FreeBSD install - a package for the installation and maintenance + * of non-core utilities. + * + * 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. + * + * Eitan Adler + * + * detect pkgng's existence and warn + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "lib.h" +#include + +void warnpkgng(void) { + char pkgngpath[MAXPATHLEN]; + char *pkgngdir; + + pkgngdir = getenv("PKG_DBDIR"); + if (pkgngdir == NULL) + pkgngdir = "/var/db/pkg"; + strcpy(pkgngpath, pkgngdir); + strcat(pkgngpath, "/local.sqlite"); + + if (access(pkgngpath, F_OK) == 0) + warnx("Don't use the pkg_ tools if you are using pkgng"); +} diff --git a/usr.sbin/pkg_install/updating/main.c b/usr.sbin/pkg_install/updating/main.c index e7a840f344df8..4a36e22b842a5 100644 --- a/usr.sbin/pkg_install/updating/main.c +++ b/usr.sbin/pkg_install/updating/main.c @@ -87,6 +87,7 @@ main(int argc, char *argv[]) DIR *dir; FILE *fd; + warnpkgng(); while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { switch (ch) { case 'd': diff --git a/usr.sbin/pkg_install/version/main.c b/usr.sbin/pkg_install/version/main.c index 39c603e61c16a..dd67e635f310e 100644 --- a/usr.sbin/pkg_install/version/main.c +++ b/usr.sbin/pkg_install/version/main.c @@ -58,6 +58,7 @@ main(int argc, char **argv) { int ch, cmp = 0; + warnpkgng(); if (argc == 4 && !strcmp(argv[1], "-t")) { cmp = version_cmp(argv[2], argv[3]); printf(cmp > 0 ? ">\n" : (cmp < 0 ? "<\n" : "=\n")); -- cgit v1.3 From db702c59cf2577cd10e82ed61282fef3e840f877 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Mon, 22 Oct 2012 03:00:37 +0000 Subject: remove duplicate semicolons where possible. Approved by: cperciva MFC after: 1 week --- lib/libc/net/ip6opt.c | 2 +- lib/libdwarf/dwarf_init.c | 2 +- lib/libpam/modules/pam_krb5/pam_krb5.c | 2 +- sbin/ddb/ddb.c | 2 +- sbin/natd/natd.c | 2 +- sys/arm/broadcom/bcm2835/bcm2835_systimer.c | 4 ++-- sys/arm/ti/am335x/am335x_dmtimer.c | 2 +- sys/cam/ctl/ctl.c | 4 ++-- sys/cam/scsi/scsi_enc_ses.c | 2 +- sys/dev/ahci/ahciem.c | 2 +- sys/dev/cxgb/cxgb_main.c | 2 +- sys/dev/e1000/if_lem.c | 2 +- sys/dev/hpt27xx/os_bsd.c | 2 +- sys/dev/isf/isf.c | 2 +- sys/dev/mps/mps_sas.c | 2 +- sys/dev/mps/mps_user.c | 2 +- sys/dev/oce/oce_if.c | 2 +- sys/dev/sound/pci/hda/hdaa.c | 2 +- sys/dev/usb/net/if_smsc.c | 2 +- sys/fs/nandfs/nandfs_subr.c | 2 +- sys/kern/sched_ule.c | 2 +- sys/mips/rmi/xlr_i2c.c | 2 +- sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 2 +- sys/ofed/drivers/net/mlx4/en_netdev.c | 2 +- sys/ofed/drivers/net/mlx4/en_rx.c | 2 +- tools/regression/lib/libc/nss/test-gethostby.c | 2 +- tools/regression/sockets/unix_cmsg/unix_cmsg.c | 2 +- usr.sbin/diskinfo/diskinfo.c | 2 +- usr.sbin/ppp/radius.c | 2 +- 29 files changed, 31 insertions(+), 31 deletions(-) (limited to 'usr.sbin') diff --git a/lib/libc/net/ip6opt.c b/lib/libc/net/ip6opt.c index d999fd48fba93..003c625279afb 100644 --- a/lib/libc/net/ip6opt.c +++ b/lib/libc/net/ip6opt.c @@ -461,7 +461,7 @@ inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, int inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) { - int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;; + int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0; if (extbuf) { u_int8_t *padp; diff --git a/lib/libdwarf/dwarf_init.c b/lib/libdwarf/dwarf_init.c index aca6cef646863..6ebc7d90d6883 100644 --- a/lib/libdwarf/dwarf_init.c +++ b/lib/libdwarf/dwarf_init.c @@ -551,7 +551,7 @@ dwarf_init_info(Dwarf_Debug dbg, Dwarf_Error *error) Dwarf_Attribute at; Dwarf_Die die; uint64_t abnum; - uint64_t die_offset = offset;; + uint64_t die_offset = offset; abnum = dwarf_read_uleb128(&d, &offset); diff --git a/lib/libpam/modules/pam_krb5/pam_krb5.c b/lib/libpam/modules/pam_krb5/pam_krb5.c index e547a3ad31c76..19592e3fcdfc1 100644 --- a/lib/libpam/modules/pam_krb5/pam_krb5.c +++ b/lib/libpam/modules/pam_krb5/pam_krb5.c @@ -650,7 +650,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, if (krbret != 0) { PAM_LOG_KRB5_ERR(pam_context, krbret, "Error krb5_cc_get_principal()"); - retval = PAM_PERM_DENIED;; + retval = PAM_PERM_DENIED; goto cleanup; } diff --git a/sbin/ddb/ddb.c b/sbin/ddb/ddb.c index 901d494aa28ab..edba7f8509a57 100644 --- a/sbin/ddb/ddb.c +++ b/sbin/ddb/ddb.c @@ -80,7 +80,7 @@ ddb_readfile(char *filename) argc++; spn = strcspn(argv[0], WHITESP); - argv[1] = argv[0] + spn + strspn(argv[0] + spn, WHITESP);; + argv[1] = argv[0] + spn + strspn(argv[0] + spn, WHITESP); argv[0][spn] = '\0'; if (*argv[1] != '\0') argc++; diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c index 631791b18697d..959e1eeaf52c9 100644 --- a/sbin/natd/natd.c +++ b/sbin/natd/natd.c @@ -1509,7 +1509,7 @@ static void ParseOption (const char* option, const char* parms) break; case LogIpfwDenied: - logIpfwDenied = yesNoValue;; + logIpfwDenied = yesNoValue; break; case PidFile: diff --git a/sys/arm/broadcom/bcm2835/bcm2835_systimer.c b/sys/arm/broadcom/bcm2835/bcm2835_systimer.c index e027d5287ca1f..1b35389550815 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_systimer.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_systimer.c @@ -1,4 +1,4 @@ -/*- +/* * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Damjan Marion * All rights reserved. @@ -280,7 +280,7 @@ DELAY(int usec) } /* Get the number of times to count */ - counts = usec * ((bcm_systimer_tc.tc_frequency / 1000000) + 1);; + counts = usec * ((bcm_systimer_tc.tc_frequency / 1000000) + 1); first = bcm_systimer_tc_read_4(SYSTIMER_CLO); diff --git a/sys/arm/ti/am335x/am335x_dmtimer.c b/sys/arm/ti/am335x/am335x_dmtimer.c index c2d01393787b3..d3473503262f4 100644 --- a/sys/arm/ti/am335x/am335x_dmtimer.c +++ b/sys/arm/ti/am335x/am335x_dmtimer.c @@ -368,7 +368,7 @@ DELAY(int usec) } /* Get the number of times to count */ - counts = usec * ((am335x_dmtimer_tc.tc_frequency / 1000000) + 1);; + counts = usec * ((am335x_dmtimer_tc.tc_frequency / 1000000) + 1); first = am335x_dmtimer_tc_read_4(DMTIMER_TCRR); diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c index 8bc1c16aaa33d..a1241c2d33f5c 100644 --- a/sys/cam/ctl/ctl.c +++ b/sys/cam/ctl/ctl.c @@ -480,7 +480,7 @@ ctl_isc_handler_finish_xfer(struct ctl_softc *ctl_softc, memcpy(&ctsio->sense_data, &msg_info->scsi.sense_data, sizeof(ctsio->sense_data)); memcpy(&ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, - &msg_info->scsi.lbalen, sizeof(msg_info->scsi.lbalen));; + &msg_info->scsi.lbalen, sizeof(msg_info->scsi.lbalen)); STAILQ_INSERT_TAIL(&ctl_softc->isc_queue, &ctsio->io_hdr, links); ctl_wakeup_thread(); } @@ -12593,7 +12593,7 @@ ctl_process_done(union ctl_io *io, int have_lock) */ memcpy(&msg.scsi.lbalen, &io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, - sizeof(msg.scsi.lbalen));; + sizeof(msg.scsi.lbalen)); if (ctl_ha_msg_send(CTL_HA_CHAN_CTL, &msg, sizeof(msg), 0) > CTL_HA_STATUS_SUCCESS) { diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 6a942653aa987..1e632cae46155 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1323,7 +1323,7 @@ ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; buf = *bufp; - err = -1;; + err = -1; if (error != 0) { err = error; diff --git a/sys/dev/ahci/ahciem.c b/sys/dev/ahci/ahciem.c index 4e73d02bf3a2a..a0978d17b732f 100644 --- a/sys/dev/ahci/ahciem.c +++ b/sys/dev/ahci/ahciem.c @@ -87,7 +87,7 @@ ahci_em_attach(device_t dev) if (!(enc->r_memc = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) return (ENXIO); - enc->capsem = ATA_INL(enc->r_memc, 0);; + enc->capsem = ATA_INL(enc->r_memc, 0); rid = 1; if (!(enc->r_memt = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) { diff --git a/sys/dev/cxgb/cxgb_main.c b/sys/dev/cxgb/cxgb_main.c index b0987167f8c65..455c7235747de 100644 --- a/sys/dev/cxgb/cxgb_main.c +++ b/sys/dev/cxgb/cxgb_main.c @@ -2984,7 +2984,7 @@ cxgb_extension_ioctl(struct cdev *dev, unsigned long cmd, caddr_t data, break; } case CHELSIO_SET_FILTER: { - struct ch_filter *f = (struct ch_filter *)data;; + struct ch_filter *f = (struct ch_filter *)data; struct filter_info *p; unsigned int nfilters = sc->params.mc5.nfilters; diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c index 8c467f1b56978..cce734695acec 100644 --- a/sys/dev/e1000/if_lem.c +++ b/sys/dev/e1000/if_lem.c @@ -3425,7 +3425,7 @@ lem_free_receive_structures(struct adapter *adapter) static bool lem_rxeof(struct adapter *adapter, int count, int *done) { - struct ifnet *ifp = adapter->ifp;; + struct ifnet *ifp = adapter->ifp; struct mbuf *mp; u8 status = 0, accept_frame = 0, eop = 0; u16 len, desc_len, prev_len_adj; diff --git a/sys/dev/hpt27xx/os_bsd.c b/sys/dev/hpt27xx/os_bsd.c index 43f8116ed40d3..12f832bae5ded 100644 --- a/sys/dev/hpt27xx/os_bsd.c +++ b/sys/dev/hpt27xx/os_bsd.c @@ -133,7 +133,7 @@ HPT_U8 pcicfg_read_byte (HPT_U8 bus, HPT_U8 dev, HPT_U8 func, HPT_U8 reg) } HPT_U32 pcicfg_read_dword(HPT_U8 bus, HPT_U8 dev, HPT_U8 func, HPT_U8 reg) { - return (HPT_U32)pci_cfgregread(bus, dev, func, reg, 4);; + return (HPT_U32)pci_cfgregread(bus, dev, func, reg, 4); } void pcicfg_write_byte (HPT_U8 bus, HPT_U8 dev, HPT_U8 func, HPT_U8 reg, HPT_U8 v) { diff --git a/sys/dev/isf/isf.c b/sys/dev/isf/isf.c index b3ac080a381f3..e537799125259 100644 --- a/sys/dev/isf/isf.c +++ b/sys/dev/isf/isf.c @@ -498,7 +498,7 @@ isf_disk_ioctl(struct disk *disk, u_long cmd, void *data, int fflag, static void isf_disk_strategy(struct bio *bp) { - struct isf_softc *sc = bp->bio_disk->d_drv1;; + struct isf_softc *sc = bp->bio_disk->d_drv1; /* * We advertise a block size and maximum I/O size up the stack; catch diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c index e02fe337128d3..a76a0aaf01fa9 100644 --- a/sys/dev/mps/mps_sas.c +++ b/sys/dev/mps/mps_sas.c @@ -2020,7 +2020,7 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm) if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) dir = BUS_DMASYNC_POSTREAD; else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) - dir = BUS_DMASYNC_POSTWRITE;; + dir = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); } diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c index ee0357359d16b..3183df857e44a 100644 --- a/sys/dev/mps/mps_user.c +++ b/sys/dev/mps/mps_user.c @@ -1000,7 +1000,7 @@ mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data) if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) dir = BUS_DMASYNC_POSTREAD; else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) - dir = BUS_DMASYNC_POSTWRITE;; + dir = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); diff --git a/sys/dev/oce/oce_if.c b/sys/dev/oce/oce_if.c index 61a6f7d136b01..79ba410451639 100644 --- a/sys/dev/oce/oce_if.c +++ b/sys/dev/oce/oce_if.c @@ -248,7 +248,7 @@ oce_attach(device_t dev) rc = oce_hw_start(sc); if (rc) - goto lro_free;; + goto lro_free; sc->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, oce_add_vlan, sc, EVENTHANDLER_PRI_FIRST); diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c index 3fc948b6741f8..0be77c3d47f24 100644 --- a/sys/dev/sound/pci/hda/hdaa.c +++ b/sys/dev/sound/pci/hda/hdaa.c @@ -1148,7 +1148,7 @@ hdaa_widget_parse(struct hdaa_widget *w) w->wclass.pin.config = hda_command(dev, HDA_CMD_GET_CONFIGURATION_DEFAULT(0, w->nid)); w->wclass.pin.cap = hda_command(dev, - HDA_CMD_GET_PARAMETER(0, w->nid, HDA_PARAM_PIN_CAP));; + HDA_CMD_GET_PARAMETER(0, w->nid, HDA_PARAM_PIN_CAP)); w->wclass.pin.ctrl = hda_command(dev, HDA_CMD_GET_PIN_WIDGET_CTRL(0, nid)); if (HDA_PARAM_PIN_CAP_EAPD_CAP(w->wclass.pin.cap)) { diff --git a/sys/dev/usb/net/if_smsc.c b/sys/dev/usb/net/if_smsc.c index 07874efb460fb..2e2b4efee5035 100644 --- a/sys/dev/usb/net/if_smsc.c +++ b/sys/dev/usb/net/if_smsc.c @@ -1179,7 +1179,7 @@ static void smsc_tick(struct usb_ether *ue) { struct smsc_softc *sc = uether_getsc(ue); - struct mii_data *mii = uether_getmii(&sc->sc_ue);; + struct mii_data *mii = uether_getmii(&sc->sc_ue); SMSC_LOCK_ASSERT(sc, MA_OWNED); diff --git a/sys/fs/nandfs/nandfs_subr.c b/sys/fs/nandfs/nandfs_subr.c index b485422caab9c..961a0a81412e1 100644 --- a/sys/fs/nandfs/nandfs_subr.c +++ b/sys/fs/nandfs/nandfs_subr.c @@ -58,7 +58,7 @@ __FBSDID("$FreeBSD$"); #include "nandfs.h" #include "nandfs_subr.h" -MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount");; +MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount"); MALLOC_DEFINE(M_NANDFSTEMP, "nandfs_tmt", "NANDFS tmp"); uma_zone_t nandfs_node_zone; diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c index 9428b39fcf485..82d1ccefa4b70 100644 --- a/sys/kern/sched_ule.c +++ b/sys/kern/sched_ule.c @@ -202,7 +202,7 @@ static struct td_sched td_sched0; */ static int sched_interact = SCHED_INTERACT_THRESH; static int realstathz = 127; -static int tickincr = 8 << SCHED_TICK_SHIFT;; +static int tickincr = 8 << SCHED_TICK_SHIFT; static int sched_slice = 12; #ifdef PREEMPTION #ifdef FULL_PREEMPTION diff --git a/sys/mips/rmi/xlr_i2c.c b/sys/mips/rmi/xlr_i2c.c index eca41226cd800..96f9820667028 100644 --- a/sys/mips/rmi/xlr_i2c.c +++ b/sys/mips/rmi/xlr_i2c.c @@ -332,7 +332,7 @@ retry: continue; if (pos >= len) - break;; + break; } *sent = len - 1; return 0; diff --git a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index a5746e426b27b..e5c40bf364b19 100644 --- a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -723,7 +723,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) void ipoib_mcast_restart(struct ipoib_dev_priv *priv) { struct ifnet *dev = priv->dev; - struct ifmultiaddr *ifma;; + struct ifmultiaddr *ifma; struct ipoib_mcast *mcast, *tmcast; LIST_HEAD(remove_list); struct ib_sa_mcmember_rec rec; diff --git a/sys/ofed/drivers/net/mlx4/en_netdev.c b/sys/ofed/drivers/net/mlx4/en_netdev.c index 537e3eb4d498c..3dca7563f66d8 100644 --- a/sys/ofed/drivers/net/mlx4/en_netdev.c +++ b/sys/ofed/drivers/net/mlx4/en_netdev.c @@ -101,7 +101,7 @@ u64 mlx4_en_mac_to_u64(u8 *addr) static int mlx4_en_cache_mclist(struct net_device *dev, u64 **mcaddrp) { - struct ifmultiaddr *ifma;; + struct ifmultiaddr *ifma; u64 *mcaddr; int cnt; int i; diff --git a/sys/ofed/drivers/net/mlx4/en_rx.c b/sys/ofed/drivers/net/mlx4/en_rx.c index cf85615d27ffe..d6843d8cd2f6f 100644 --- a/sys/ofed/drivers/net/mlx4/en_rx.c +++ b/sys/ofed/drivers/net/mlx4/en_rx.c @@ -405,7 +405,7 @@ static inline int invalid_cqe(struct mlx4_en_priv *priv, } if (unlikely(cqe->badfcs_enc & MLX4_CQE_BAD_FCS)) { en_dbg(RX_ERR, priv, "Accepted frame with bad FCS\n"); - return 1;; + return 1; } return 0; diff --git a/tools/regression/lib/libc/nss/test-gethostby.c b/tools/regression/lib/libc/nss/test-gethostby.c index 61e4ca2af4cee..5a0c925975542 100644 --- a/tools/regression/lib/libc/nss/test-gethostby.c +++ b/tools/regression/lib/libc/nss/test-gethostby.c @@ -565,7 +565,7 @@ hostent_read_snapshot_addr(char *addr, unsigned char *result, size_t len) static int hostent_read_snapshot_func(struct hostent *ht, char *line) { - StringList *sl1, *sl2;; + StringList *sl1, *sl2; char *s, *ps, *ts; int i, rv; diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.c b/tools/regression/sockets/unix_cmsg/unix_cmsg.c index 6a39ac36ade5a..c16b468ceb709 100644 --- a/tools/regression/sockets/unix_cmsg/unix_cmsg.c +++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.c @@ -1513,7 +1513,7 @@ t_timestamp_server(int fd1) msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = control_un.control; - msg.msg_controllen = sizeof control_un.control;; + msg.msg_controllen = sizeof control_un.control; msg.msg_flags = 0; if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0) diff --git a/usr.sbin/diskinfo/diskinfo.c b/usr.sbin/diskinfo/diskinfo.c index fa234433fc182..90beba2339d1b 100644 --- a/usr.sbin/diskinfo/diskinfo.c +++ b/usr.sbin/diskinfo/diskinfo.c @@ -339,7 +339,7 @@ speeddisk(int fd, off_t mediasize, u_int sectorsize) TR(bulk * 1024); printf("\tinside: "); - b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;; + b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1; rdsect(fd, b0, sectorsize); T0(); for (i = 0; i < bulk; i++) { diff --git a/usr.sbin/ppp/radius.c b/usr.sbin/ppp/radius.c index 6b1d685ee3e15..dc71ab3167119 100644 --- a/usr.sbin/ppp/radius.c +++ b/usr.sbin/ppp/radius.c @@ -784,7 +784,7 @@ radius_Init(struct radius *r) r->mppe.recvkeylen = 0; r->mppe.sendkey = NULL; r->mppe.sendkeylen = 0; - *r->cfg.file = '\0';; + *r->cfg.file = '\0'; log_Printf(LogDEBUG, "Radius: radius_Init\n"); } -- cgit v1.3 From 50e04779c499d5c329d1863b71ecd7c2b5824f76 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Mon, 22 Oct 2012 03:07:05 +0000 Subject: Check the return error of set[e][ug]id. While this can never fail in the current version of FreeBSD, this isn't guarenteed by the API. Custom security modules, or future implementations of the setuid and setgid may fail. Submitted by: Erik Cederstrand Approved by: cperciva MFC after: 3 days --- libexec/tftpd/tftpd.c | 5 ++++- sbin/ccdconfig/ccdconfig.c | 9 ++++++--- sbin/restore/tape.c | 6 +++++- usr.bin/lock/lock.c | 4 +++- usr.bin/msgs/msgs.c | 3 ++- usr.bin/wall/wall.c | 3 ++- usr.sbin/edquota/edquota.c | 6 ++++-- usr.sbin/kgmon/kgmon.c | 4 +++- 8 files changed, 29 insertions(+), 11 deletions(-) (limited to 'usr.sbin') diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index 7363abf3c0e95..a0010b3c50322 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -371,7 +371,10 @@ main(int argc, char *argv[]) } chdir("/"); setgroups(1, &nobody->pw_gid); - setuid(nobody->pw_uid); + if (setuid(nobody->pw_uid) != 0) { + tftp_log(LOG_ERR, "setuid failed"); + exit(1); + } } len = sizeof(me_sock); diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c index 6324150b0ff79..76867ba8ff078 100644 --- a/sbin/ccdconfig/ccdconfig.c +++ b/sbin/ccdconfig/ccdconfig.c @@ -288,13 +288,16 @@ do_all(int action) rval = 0; egid = getegid(); - setegid(getgid()); + if (setegid(getgid()) != 0) + err(1, "setegid failed"); if ((f = fopen(ccdconf, "r")) == NULL) { - setegid(egid); + if (setegid(egid) != 0) + err(1, "setegid failed"); warn("fopen: %s", ccdconf); return (1); } - setegid(egid); + if (setegid(egid) != 0) + err(1, "setegid failed"); while (fgets(line, sizeof(line), f) != NULL) { argc = 0; diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c index f63d573617b7c..4f34549011042 100644 --- a/sbin/restore/tape.c +++ b/sbin/restore/tape.c @@ -164,7 +164,11 @@ setinput(char *source, int ispipecommand) } pipein++; } - setuid(getuid()); /* no longer need or want root privileges */ + /* no longer need or want root privileges */ + if (setuid(getuid()) != 0) { + fprintf(stderr, "setuid failed\n"); + done(1); + } magtape = strdup(source); if (magtape == NULL) { fprintf(stderr, "Cannot allocate space for magtape buffer\n"); diff --git a/usr.bin/lock/lock.c b/usr.bin/lock/lock.c index 0382e831862c6..acc0e29d62d89 100644 --- a/usr.bin/lock/lock.c +++ b/usr.bin/lock/lock.c @@ -129,7 +129,9 @@ main(int argc, char **argv) } timeout.tv_sec = sectimeout * 60; - setuid(getuid()); /* discard privs */ + /* discard privs */ + if (setuid(getuid()) != 0) + errx(1, "setuid failed"); if (tcgetattr(0, &tty)) /* get information for header */ exit(1); diff --git a/usr.bin/msgs/msgs.c b/usr.bin/msgs/msgs.c index e534fac83da89..c4c589e168e1e 100644 --- a/usr.bin/msgs/msgs.c +++ b/usr.bin/msgs/msgs.c @@ -175,7 +175,8 @@ main(int argc, char *argv[]) setlocale(LC_ALL, ""); time(&t); - setuid(uid = getuid()); + if (setuid(uid = getuid()) != 0) + err(1, "setuid failed"); ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL); if (ruptible) signal(SIGINT, SIG_DFL); diff --git a/usr.bin/wall/wall.c b/usr.bin/wall/wall.c index dafd44800f91a..b3a63dcbf20ac 100644 --- a/usr.bin/wall/wall.c +++ b/usr.bin/wall/wall.c @@ -240,7 +240,8 @@ makemsg(char *fname) setegid(getgid()); if (freopen(fname, "r", stdin) == NULL) err(1, "can't read %s", fname); - setegid(egid); + if (setegid(egid) != 0) + err(1, "setegid failed"); } cnt = 0; while (fgetws(lbuf, sizeof(lbuf)/sizeof(wchar_t), stdin)) { diff --git a/usr.sbin/edquota/edquota.c b/usr.sbin/edquota/edquota.c index 0957011b5be46..930a460a3df79 100644 --- a/usr.sbin/edquota/edquota.c +++ b/usr.sbin/edquota/edquota.c @@ -453,8 +453,10 @@ editit(char *tmpf) const char *ed; sigsetmask(omask); - setgid(getgid()); - setuid(getuid()); + if (setgid(getgid()) != 0) + err(1, "setgid failed"); + if (setuid(getuid()) != 0) + err(1, "setuid failed"); if ((ed = getenv("EDITOR")) == (char *)0) ed = _PATH_VI; execlp(ed, ed, tmpf, (char *)0); diff --git a/usr.sbin/kgmon/kgmon.c b/usr.sbin/kgmon/kgmon.c index d5cdf961768b1..c691d6ecf414b 100644 --- a/usr.sbin/kgmon/kgmon.c +++ b/usr.sbin/kgmon/kgmon.c @@ -90,7 +90,9 @@ main(int argc, char **argv) struct kvmvars kvmvars; char *system, *kmemf; - seteuid(getuid()); + if (seteuid(getuid()) != 0) { + err(1, "seteuid failed\n"); + } kmemf = NULL; system = NULL; while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) { -- cgit v1.3 From 521aea7c3013acd0b2665c7f257bc229a3725701 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Mon, 22 Oct 2012 03:16:32 +0000 Subject: Fix typo in error when unable to create TCP socket PR: bin/172490 Submitted by: bdrewery Approved by: cperciva MFC after: 3 days --- usr.sbin/nfsd/nfsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c index 7f6f659200c59..c269e47f56795 100644 --- a/usr.sbin/nfsd/nfsd.c +++ b/usr.sbin/nfsd/nfsd.c @@ -590,7 +590,7 @@ main(int argc, char **argv) if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, - "can't create tpc socket"); + "can't create tcp socket"); nfsd_exit(1); } if (setsockopt(tcpsock, SOL_SOCKET, -- cgit v1.3 From 1d1d4a4727577afb8709b756a78e0ab9ee43e9e6 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Mon, 22 Oct 2012 03:31:22 +0000 Subject: Check the return error of set[ug]id. While this can never fail in the current version of FreeBSD, this isn't guarenteed by the API. Custom security modules, or future implementations of the setuid and setgid may fail. PR: bin/172289 PR: bin/172290 PR: bin/172291 Submittud by: Erik Cederstrand Discussed by: freebsd-security Approved by: cperciva MFC after: 1 week --- sbin/ping/ping.c | 3 +- sbin/ping6/ping6.c | 6 ++-- usr.bin/at/privs.h | 20 +++++------ usr.sbin/lpr/common_source/common.c | 25 +++++++------- usr.sbin/lpr/common_source/displayq.c | 41 +++++++++++----------- usr.sbin/lpr/common_source/lp.h | 11 ++++++ usr.sbin/lpr/common_source/net.c | 37 ++++++++++---------- usr.sbin/lpr/common_source/rmjob.c | 25 +++++++------- usr.sbin/lpr/common_source/startdaemon.c | 4 +-- usr.sbin/lpr/lpc/cmds.c | 59 ++++++++++++++++---------------- usr.sbin/lpr/lpc/lpc.c | 6 ++-- usr.sbin/lpr/lpc/movejobs.c | 5 +-- usr.sbin/lpr/lpd/printjob.c | 3 +- usr.sbin/lpr/lpq/lpq.c | 2 +- usr.sbin/lpr/lpr/lpr.c | 34 +++++++++--------- usr.sbin/lpr/lprm/lprm.c | 3 +- 16 files changed, 153 insertions(+), 131 deletions(-) (limited to 'usr.sbin') diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c index f8f62d1f86461..85d03483a5a3e 100644 --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -255,7 +255,8 @@ main(int argc, char *const *argv) s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); sockerrno = errno; - setuid(getuid()); + if (setuid(getuid()) != 0) + err(EX_NOPERM, "setuid() failed"); uid = getuid(); alarmtimeout = df = preload = tos = 0; diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c index 711561ebb9acb..8c3e16bbdaa02 100644 --- a/sbin/ping6/ping6.c +++ b/sbin/ping6/ping6.c @@ -702,8 +702,10 @@ main(int argc, char *argv[]) } /* revoke root privilege */ - seteuid(getuid()); - setuid(getuid()); + if (seteuid(getuid()) != 0) + err(1, "seteuid() failed"); + if (setuid(getuid()) != 0) + err(1, "setuid() failed"); if ((options & F_FLOOD) && (options & F_INTERVAL)) errx(1, "-f and -i incompatible options"); diff --git a/usr.bin/at/privs.h b/usr.bin/at/privs.h index 3f70ce84e18d0..297c2529ac477 100644 --- a/usr.bin/at/privs.h +++ b/usr.bin/at/privs.h @@ -72,8 +72,8 @@ gid_t real_gid, effective_gid; effective_uid = geteuid(); \ real_gid = getgid(); \ effective_gid = getegid(); \ - seteuid(real_uid); \ - setegid(real_gid); \ + if (seteuid(real_uid) != 0) err(1, "seteuid failed"); \ + if (setegid(real_gid) != 0) err(1, "setegid failed"); \ } #define RELINQUISH_PRIVS_ROOT(a, b) { \ @@ -81,26 +81,26 @@ gid_t real_gid, effective_gid; effective_uid = geteuid(); \ real_gid = (b); \ effective_gid = getegid(); \ - setegid(real_gid); \ - seteuid(real_uid); \ + if (setegid(real_gid) != 0) err(1, "setegid failed"); \ + if (seteuid(real_uid) != 0) err(1, "seteuid failed"); \ } #define PRIV_START { \ - seteuid(effective_uid); \ - setegid(effective_gid); \ + if (seteuid(effective_uid) != 0) err(1, "seteuid failed"); \ + if (setegid(effective_gid) != 0) err(1, "setegid failed"); \ } #define PRIV_END { \ - setegid(real_gid); \ - seteuid(real_uid); \ + if (setegid(real_gid) != 0) err(1, "setegid failed"); \ + if (seteuid(real_uid) != 0) err(1, "seteuid failed"); \ } #define REDUCE_PRIV(a, b) { \ PRIV_START \ effective_uid = (a); \ effective_gid = (b); \ - setreuid((uid_t)-1, effective_uid); \ - setregid((gid_t)-1, effective_gid); \ + if (setreuid((uid_t)-1, effective_uid) != 0) err(1, "setreuid failed"); \ + if (setregid((gid_t)-1, effective_gid) != 0) err(1, "setregid failed"); \ PRIV_END \ } #endif diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c index c9c276d8c5590..ce4a62dc70e36 100644 --- a/usr.sbin/lpr/common_source/common.c +++ b/usr.sbin/lpr/common_source/common.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -125,14 +126,14 @@ getq(const struct printer *pp, struct jobqueue *(*namelist[])) DIR *dirp; int statres; - seteuid(euid); + PRIV_START if ((dirp = opendir(pp->spool_dir)) == NULL) { - seteuid(uid); + PRIV_END return (-1); } if (fstat(dirfd(dirp), &stbuf) < 0) goto errdone; - seteuid(uid); + PRIV_END /* * Estimate the array size by taking the size of the directory file @@ -149,9 +150,9 @@ getq(const struct printer *pp, struct jobqueue *(*namelist[])) while ((d = readdir(dirp)) != NULL) { if (d->d_name[0] != 'c' || d->d_name[1] != 'f') continue; /* daemon control files only */ - seteuid(euid); + PRIV_START statres = stat(d->d_name, &stbuf); - seteuid(uid); + PRIV_END if (statres < 0) continue; /* Doesn't exist */ entrysz = sizeof(struct jobqueue) - sizeof(q->job_cfname) + @@ -184,7 +185,7 @@ getq(const struct printer *pp, struct jobqueue *(*namelist[])) errdone: closedir(dirp); - seteuid(uid); + PRIV_END return (-1); } @@ -340,10 +341,10 @@ set_qstate(int action, const char *lfname) * Find what the current access-bits are. */ memset(&stbuf, 0, sizeof(stbuf)); - seteuid(euid); + PRIV_START statres = stat(lfname, &stbuf); errsav = errno; - seteuid(uid); + PRIV_END if ((statres < 0) && (errsav != ENOENT)) { printf("\tcannot stat() lock file\n"); return (SQS_STATFAIL); @@ -402,10 +403,10 @@ set_qstate(int action, const char *lfname) res = 0; if (statres >= 0) { /* The file already exists, so change the access. */ - seteuid(euid); + PRIV_START chres = chmod(lfname, chgbits); errsav = errno; - seteuid(uid); + PRIV_END res = SQS_CHGOK; if (chres < 0) res = SQS_CHGFAIL; @@ -424,10 +425,10 @@ set_qstate(int action, const char *lfname) * all the read/write bits are set as desired. */ oldmask = umask(S_IWOTH); - seteuid(euid); + PRIV_START fd = open(lfname, O_WRONLY|O_CREAT, newbits); errsav = errno; - seteuid(uid); + PRIV_END umask(oldmask); res = SQS_CREFAIL; if (fd >= 0) { diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c index 061b3709d5bbb..b4bd7ce40e449 100644 --- a/usr.sbin/lpr/common_source/displayq.c +++ b/usr.sbin/lpr/common_source/displayq.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -90,7 +91,7 @@ static const char *head1 = "Total Size\n"; static void alarmhandler(int _signo); static void filtered_write(char *_obuffer, int _wlen, FILE *_wstream); -static void warn(const struct printer *_pp); +static void daemonwarn(const struct printer *_pp); /* * Display the current state of the queue. Format = 1 if long format. @@ -119,24 +120,24 @@ displayq(struct printer *pp, int format) * Print out local queue * Find all the control files in the spooling directory */ - seteuid(euid); + PRIV_START if (chdir(pp->spool_dir) < 0) fatal(pp, "cannot chdir to spooling directory: %s", strerror(errno)); - seteuid(uid); + PRIV_END if ((nitems = getq(pp, &queue)) < 0) fatal(pp, "cannot examine spooling area\n"); - seteuid(euid); + PRIV_START ret = stat(pp->lock_file, &statb); - seteuid(uid); + PRIV_END if (ret >= 0) { if (statb.st_mode & LFM_PRINT_DIS) { if (pp->remote) printf("%s: ", local_host); printf("Warning: %s is down: ", pp->printer); - seteuid(euid); + PRIV_START fd = open(pp->status_file, O_RDONLY|O_SHLOCK); - seteuid(uid); + PRIV_END if (fd >= 0) { while ((i = read(fd, line, sizeof(line))) > 0) (void) fwrite(line, 1, i, stdout); @@ -153,11 +154,11 @@ displayq(struct printer *pp, int format) } if (nitems) { - seteuid(euid); + PRIV_START fp = fopen(pp->lock_file, "r"); - seteuid(uid); + PRIV_END if (fp == NULL) - warn(pp); + daemonwarn(pp); else { /* get daemon pid */ cp = current; @@ -171,12 +172,12 @@ displayq(struct printer *pp, int format) if (i <= 0) { ret = -1; } else { - seteuid(euid); + PRIV_START ret = kill(i, 0); - seteuid(uid); + PRIV_END } if (ret < 0) { - warn(pp); + daemonwarn(pp); } else { /* read current file name */ cp = current; @@ -191,9 +192,9 @@ displayq(struct printer *pp, int format) */ if (pp->remote) printf("%s: ", local_host); - seteuid(euid); + PRIV_START fd = open(pp->status_file, O_RDONLY|O_SHLOCK); - seteuid(uid); + PRIV_END if (fd >= 0) { while ((i = read(fd, line, sizeof(line))) > 0) @@ -360,7 +361,7 @@ check_next: * Print a warning message if there is no daemon present. */ static void -warn(const struct printer *pp) +daemonwarn(const struct printer *pp) { if (pp->remote) printf("%s: ", local_host); @@ -391,10 +392,10 @@ inform(const struct printer *pp, char *cf) * There's a chance the control file has gone away * in the meantime; if this is the case just keep going */ - seteuid(euid); + PRIV_START if ((cfp = fopen(cf, "r")) == NULL) return; - seteuid(uid); + PRIV_END if (rank < 0) rank = 0; @@ -578,10 +579,10 @@ dump(const char *nfile, const char *datafile, int copies) } first = 0; - seteuid(euid); + PRIV_START if (*datafile && !stat(datafile, &lbuf)) totsize += copies * lbuf.st_size; - seteuid(uid); + PRIV_END } /* diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h index b7ebff6476faa..ab7ce2ecd6e05 100644 --- a/usr.sbin/lpr/common_source/lp.h +++ b/usr.sbin/lpr/common_source/lp.h @@ -249,6 +249,17 @@ typedef enum { TR_SENDING, TR_RECVING, TR_PRINTING } tr_sendrecv; #define CMD_SHOWQ_LONG '\4' #define CMD_RMJOB '\5' +/* + * seteuid() macros. +*/ +#define PRIV_START { \ + if (seteuid(uid) != 0) err(1, "seteuid failed"); \ +} +#define PRIV_END { \ + if (seteuid(euid) != 0) err(1, "seteuid failed"); \ +} + + #include "lp.cdefs.h" /* A cross-platform version of */ __BEGIN_DECLS diff --git a/usr.sbin/lpr/common_source/net.c b/usr.sbin/lpr/common_source/net.c index 2a1e0c80a5dfc..0ac816a2d6ddb 100644 --- a/usr.sbin/lpr/common_source/net.c +++ b/usr.sbin/lpr/common_source/net.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include #include /* required for lp.h, not used here */ +#include #include #include #include @@ -91,7 +92,7 @@ getport(const struct printer *pp, const char *rhost, int rport) { struct addrinfo hints, *res, *ai; int s, timo = 1, lport = IPPORT_RESERVED - 1; - int err, refused = 0; + int error, refused = 0; /* * Get the host address and port number to connect to. @@ -102,10 +103,10 @@ getport(const struct printer *pp, const char *rhost, int rport) hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; - err = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL), + error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL), &hints, &res); - if (err) - fatal(pp, "%s\n", gai_strerror(err)); + if (error) + fatal(pp, "%s\n", gai_strerror(error)); if (rport != 0) ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport); @@ -114,9 +115,9 @@ getport(const struct printer *pp, const char *rhost, int rport) */ ai = res; retry: - seteuid(euid); + PRIV_START s = rresvport_af(&lport, ai->ai_family); - seteuid(uid); + PRIV_END if (s < 0) { if (errno != EAGAIN) { if (ai->ai_next) { @@ -135,9 +136,9 @@ retry: return(-1); } if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { - err = errno; + error = errno; (void) close(s); - errno = err; + errno = error; /* * This used to decrement lport, but the current semantics * of rresvport do not provide such a function (in fact, @@ -184,8 +185,8 @@ checkremote(struct printer *pp) { char lclhost[MAXHOSTNAMELEN]; struct addrinfo hints, *local_res, *remote_res, *lr, *rr; - char *err; - int ncommonaddrs, error; + char *error; + int ncommonaddrs, errno; char h1[NI_MAXHOST], h2[NI_MAXHOST]; if (!pp->rp_matches_local) { /* Remote printer doesn't match local */ @@ -205,11 +206,11 @@ checkremote(struct printer *pp) hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; - if ((error = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) { - asprintf(&err, "unable to get official name " + if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) { + asprintf(&error, "unable to get official name " "for local machine %s: %s", - lclhost, gai_strerror(error)); - return err; + lclhost, gai_strerror(errno)); + return error; } /* get the official name of RM */ @@ -217,13 +218,13 @@ checkremote(struct printer *pp) hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; - if ((error = getaddrinfo(pp->remote_host, NULL, + if ((errno = getaddrinfo(pp->remote_host, NULL, &hints, &remote_res)) != 0) { - asprintf(&err, "unable to get address list for " + asprintf(&error, "unable to get address list for " "remote machine %s: %s", - pp->remote_host, gai_strerror(error)); + pp->remote_host, gai_strerror(errno)); freeaddrinfo(local_res); - return err; + return error; } ncommonaddrs = 0; diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c index 912a48ff70d3d..d0327a232251c 100644 --- a/usr.sbin/lpr/common_source/rmjob.c +++ b/usr.sbin/lpr/common_source/rmjob.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -110,12 +111,12 @@ rmjob(const char *printer) person = root; } - seteuid(euid); + PRIV_START if (chdir(pp->spool_dir) < 0) fatal(pp, "cannot chdir to spool directory"); if ((nitems = scandir(".", &files, iscf, NULL)) < 0) fatal(pp, "cannot access spool directory"); - seteuid(uid); + PRIV_END if (nitems) { /* @@ -124,9 +125,9 @@ rmjob(const char *printer) * (after which we have to restart the daemon). */ if (lockchk(pp, pp->lock_file) && chk(current)) { - seteuid(euid); + PRIV_START assassinated = kill(cur_daemon, SIGINT) == 0; - seteuid(uid); + PRIV_END if (!assassinated) fatal(pp, "cannot kill printer daemon"); } @@ -156,14 +157,14 @@ lockchk(struct printer *pp, char *slockf) register FILE *fp; register int i, n; - seteuid(euid); + PRIV_START if ((fp = fopen(slockf, "r")) == NULL) { if (errno == EACCES) fatal(pp, "%s: %s", slockf, strerror(errno)); else return(0); } - seteuid(uid); + PRIV_END if (!getline(fp)) { (void) fclose(fp); return(0); /* no daemon present */ @@ -195,10 +196,10 @@ process(const struct printer *pp, char *file) if (!chk(file)) return; - seteuid(euid); + PRIV_START if ((cfp = fopen(file, "r")) == NULL) fatal(pp, "cannot open %s", file); - seteuid(uid); + PRIV_END while (getline(cfp)) { switch (line[0]) { case 'U': /* unlink associated files */ @@ -218,9 +219,9 @@ do_unlink(char *file) if (from_host != local_host) printf("%s: ", local_host); - seteuid(euid); + PRIV_START ret = unlink(file); - seteuid(uid); + PRIV_END printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file); } @@ -248,10 +249,10 @@ chk(char *file) /* * get the owner's name from the control file. */ - seteuid(euid); + PRIV_START if ((cfp = fopen(file, "r")) == NULL) return(0); - seteuid(uid); + PRIV_END while (getline(cfp)) { if (line[0] == 'P') break; diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c index b175d79961894..a1398bb587f5d 100644 --- a/usr.sbin/lpr/common_source/startdaemon.c +++ b/usr.sbin/lpr/common_source/startdaemon.c @@ -74,9 +74,9 @@ startdaemon(const struct printer *pp) #ifndef SUN_LEN #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) #endif - seteuid(euid); + PRIV_START connectres = connect(s, (struct sockaddr *)&un, SUN_LEN(&un)); - seteuid(uid); + PRIV_END if (connectres < 0) { warn("Unable to connect to %s", _PATH_SOCKETNAME); warnx("Check to see if the master 'lpd' process is running."); diff --git a/usr.sbin/lpr/lpc/cmds.c b/usr.sbin/lpr/lpc/cmds.c index 44516d8305fcc..f960f7c315a63 100644 --- a/usr.sbin/lpr/lpc/cmds.c +++ b/usr.sbin/lpr/lpc/cmds.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -288,10 +289,10 @@ kill_qtask(const char *lf) pid_t pid; int errsav, killres, lockres, res; - seteuid(euid); + PRIV_START fp = fopen(lf, "r"); errsav = errno; - seteuid(uid); + PRIV_END res = KQT_NODAEMON; if (fp == NULL) { /* @@ -329,10 +330,10 @@ kill_qtask(const char *lf) goto killdone; } - seteuid(uid); + PRIV_END killres = kill(pid, SIGTERM); errsav = errno; - seteuid(uid); + PRIV_END if (killres == 0) { res = KQT_KILLOK; printf("\tdaemon (pid %d) killed\n", pid); @@ -376,9 +377,9 @@ upstat(struct printer *pp, const char *msg, int notifyuser) status_file_name(pp, statfile, sizeof statfile); umask(0); - seteuid(euid); + PRIV_START fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); - seteuid(uid); + PRIV_END if (fd < 0) { printf("\tcannot create status file: %s\n", strerror(errno)); return; @@ -683,9 +684,9 @@ clean_q(struct printer *pp) linerem = sizeof(line) - (lp - line); cln_foundcore = 0; - seteuid(euid); + PRIV_START nitems = scandir(pp->spool_dir, &queue, doselect, sortq); - seteuid(uid); + PRIV_END if (nitems < 0) { if (!didhead) { printf("%s:\n", pp->printer); @@ -795,9 +796,9 @@ unlinkf(char *name) * that case, we need to check the last-mod time of the symlink, and * not the file that the symlink is pointed at. */ - seteuid(euid); + PRIV_START res = lstat(name, &stbuf); - seteuid(uid); + PRIV_END if (res < 0) { printf("\terror return from stat(%s):\n", name); printf("\t %s\n", strerror(errno)); @@ -819,9 +820,9 @@ unlinkf(char *name) * symlink before unlink-ing the file itself */ if (S_ISLNK(stbuf.st_mode)) { - seteuid(euid); + PRIV_START res = readlink(name, linkbuf, sizeof(linkbuf)); - seteuid(uid); + PRIV_END if (res < 0) { printf("\terror return from readlink(%s):\n", name); printf("\t %s\n", strerror(errno)); @@ -841,9 +842,9 @@ unlinkf(char *name) printf("\t (which is a symlink to %s)\n", linkbuf); } } else { - seteuid(euid); + PRIV_START res = unlink(name); - seteuid(uid); + PRIV_END if (res < 0) printf("\tcannot remove %s (!)\n", name); else @@ -983,9 +984,9 @@ restart_q(struct printer *pp) /* make sure the queue is set to print jobs */ setres = set_qstate(SQS_STARTP, lf); - seteuid(euid); + PRIV_START startok = startdaemon(pp); - seteuid(uid); + PRIV_END if (!startok) printf("\tcouldn't restart daemon\n"); else @@ -1049,14 +1050,14 @@ start_q(struct printer *pp) setres = set_qstate(SQS_STARTP, lf); - seteuid(euid); + PRIV_START startok = startdaemon(pp); - seteuid(uid); + PRIV_END if (!startok) printf("\tcouldn't start daemon\n"); else printf("\tdaemon started\n"); - seteuid(uid); + PRIV_END } /* @@ -1178,12 +1179,12 @@ topq(int argc, char *argv[]) } printf("%s:\n", pp->printer); - seteuid(euid); + PRIV_START if (chdir(pp->spool_dir) < 0) { printf("\tcannot chdir to %s\n", pp->spool_dir); goto out; } - seteuid(uid); + PRIV_END nitems = getq(pp, &queue); if (nitems == 0) return; @@ -1207,12 +1208,12 @@ topq(int argc, char *argv[]) * Turn on the public execute bit of the lock file to * get lpd to rebuild the queue after the current job. */ - seteuid(euid); + PRIV_START if (changed && stat(pp->lock_file, &stbuf) >= 0) (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); out: - seteuid(uid); + PRIV_END } /* @@ -1227,9 +1228,9 @@ touch(struct jobqueue *jq) tvp[0].tv_sec = tvp[1].tv_sec = --mtime; tvp[0].tv_usec = tvp[1].tv_usec = 0; - seteuid(euid); + PRIV_START ret = utimes(jq->job_cfname, tvp); - seteuid(uid); + PRIV_END return (ret); } @@ -1286,9 +1287,9 @@ doarg(char *job) * Process item consisting of owner's name (example: henry). */ for (qq = queue + nitems; --qq >= queue; ) { - seteuid(euid); + PRIV_START fp = fopen((*qq)->job_cfname, "r"); - seteuid(uid); + PRIV_END if (fp == NULL) continue; while (getline(fp) > 0) @@ -1319,9 +1320,9 @@ up_q(struct printer *pp) setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); - seteuid(euid); + PRIV_START startok = startdaemon(pp); - seteuid(uid); + PRIV_END if (!startok) printf("\tcouldn't start daemon\n"); else diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c index f9fb920dd5407..cc58bd983841e 100644 --- a/usr.sbin/lpr/lpc/lpc.c +++ b/usr.sbin/lpr/lpc/lpc.c @@ -93,7 +93,7 @@ main(int argc, char *argv[]) euid = geteuid(); uid = getuid(); - seteuid(uid); + PRIV_END progname = argv[0]; openlog("lpd", 0, LOG_LPR); @@ -405,9 +405,9 @@ setup_myprinter(char *pwanted, struct printer *pp, int sump_opts) printf("%s:\n", pp->printer); if (sump_opts & SUMP_CHDIR_SD) { - seteuid(euid); + PRIV_START cdres = chdir(pp->spool_dir); - seteuid(uid); + PRIV_END if (cdres < 0) { printf("\tcannot chdir to %s\n", pp->spool_dir); free_printer(pp); diff --git a/usr.sbin/lpr/lpc/movejobs.c b/usr.sbin/lpr/lpc/movejobs.c index 60c9b38d57089..c3496011aef3c 100644 --- a/usr.sbin/lpr/lpc/movejobs.c +++ b/usr.sbin/lpr/lpc/movejobs.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include /* just for MAXNAMLEN, for job_cfname in lp.h! */ +#include #include #include #include @@ -147,9 +148,9 @@ touch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec) touch_info = myinfo; tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime; tvp[0].tv_usec = tvp[1].tv_usec = 0; - seteuid(euid); + PRIV_START ret = utimes(jq->job_cfname, tvp); - seteuid(uid); + PRIV_END if (ret == 0) { if (jspec->matcheduser) diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c index 61300547fd447..9771498acb3db 100644 --- a/usr.sbin/lpr/lpd/printjob.c +++ b/usr.sbin/lpr/lpd/printjob.c @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -173,7 +174,7 @@ printjob(struct printer *pp) pp->log_file); (void) open(_PATH_DEVNULL, O_WRONLY); } - setgid(getegid()); + if(setgid(getegid()) != 0) err(1, "setgid() failed"); printpid = getpid(); /* for use with lprm */ setpgrp(0, printpid); diff --git a/usr.sbin/lpr/lpq/lpq.c b/usr.sbin/lpr/lpq/lpq.c index 33082e575c52d..f39c81bc117c5 100644 --- a/usr.sbin/lpr/lpq/lpq.c +++ b/usr.sbin/lpr/lpq/lpq.c @@ -88,7 +88,7 @@ main(int argc, char **argv) printer = NULL; euid = geteuid(); uid = getuid(); - seteuid(uid); + PRIV_END progname = *argv; if (gethostname(local_host, sizeof(local_host))) err(1, "gethostname"); diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c index af7c807a1e084..394ccb063242b 100644 --- a/usr.sbin/lpr/lpr/lpr.c +++ b/usr.sbin/lpr/lpr/lpr.c @@ -145,7 +145,7 @@ main(int argc, char *argv[]) printer = NULL; euid = geteuid(); uid = getuid(); - seteuid(uid); + PRIV_END if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, cleanup); if (signal(SIGINT, SIG_IGN) != SIG_IGN) @@ -326,10 +326,10 @@ main(int argc, char *argv[]) */ mktemps(pp); tfd = nfile(tfname); - seteuid(euid); + PRIV_START (void) fchown(tfd, pp->daemon_user, -1); /* owned by daemon for protection */ - seteuid(uid); + PRIV_END card('H', local_host); card('P', lpr_username); card('C', class); @@ -415,7 +415,7 @@ main(int argc, char *argv[]) * can be very significant when running services like * samba, pcnfs, CAP, et al. */ - seteuid(euid); + PRIV_START didlink = 0; /* * There are several things to check to avoid any @@ -453,11 +453,11 @@ main(int argc, char *argv[]) * safe. Otherwise, abandon the move and fall back * to the (usual) copy method. */ - seteuid(uid); + PRIV_END ret = access(dfname, R_OK); if (ret == 0) ret = unlink(arg); - seteuid(euid); + PRIV_START if (ret != 0) goto nohardlink; /* @@ -467,7 +467,7 @@ main(int argc, char *argv[]) */ chown(dfname, pp->daemon_user, getegid()); chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - seteuid(uid); + PRIV_END if (format == 'p') card('T', title ? title : arg); for (i = 0; i < ncopies; i++) @@ -479,7 +479,7 @@ main(int argc, char *argv[]) nohardlink: if (didlink) unlink(dfname); - seteuid(uid); /* restore old uid */ + PRIV_END /* restore old uid */ } /* end: if (f) */ if ((i = open(arg, O_RDONLY)) < 0) { @@ -498,7 +498,7 @@ main(int argc, char *argv[]) /* * Touch the control file to fix position in the queue. */ - seteuid(euid); + PRIV_START if ((tfd = open(tfname, O_RDWR)) >= 0) { char touch_c; @@ -518,7 +518,7 @@ main(int argc, char *argv[]) cleanup(0); } unlink(tfname); - seteuid(uid); + PRIV_END if (qflag) /* just q things up */ exit(0); if (!startdaemon(pp)) @@ -604,9 +604,9 @@ linked(const char *file) strncat(buf, file, sizeof(buf) - strlen(buf) - 1); file = buf; } - seteuid(euid); + PRIV_START ret = symlink(file, dfname); - seteuid(uid); + PRIV_END return(ret ? NULL : file); } @@ -638,7 +638,7 @@ nfile(char *n) register int f; int oldumask = umask(0); /* should block signals */ - seteuid(euid); + PRIV_START f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD); (void) umask(oldumask); if (f < 0) { @@ -649,7 +649,7 @@ nfile(char *n) printf("%s: cannot chown %s\n", progname, n); cleanup(0); /* cleanup does exit */ } - seteuid(uid); + PRIV_END if (++n[inchar] > 'z') { if (++n[inchar-2] == 't') { printf("too many files - break up the job\n"); @@ -674,7 +674,7 @@ cleanup(int signo __unused) signal(SIGQUIT, SIG_IGN); signal(SIGTERM, SIG_IGN); i = inchar; - seteuid(euid); + PRIV_START if (tfname) do unlink(tfname); @@ -846,7 +846,7 @@ mktemps(const struct printer *pp) char buf[BUFSIZ]; (void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir); - seteuid(euid); + PRIV_START if ((fd = open(buf, O_RDWR|O_CREAT, 0664)) < 0) { printf("%s: cannot create %s\n", progname, buf); exit(1); @@ -855,7 +855,7 @@ mktemps(const struct printer *pp) printf("%s: cannot lock %s\n", progname, buf); exit(1); } - seteuid(uid); + PRIV_END n = 0; if ((len = read(fd, buf, sizeof(buf))) > 0) { for (cp = buf; len--; ) { diff --git a/usr.sbin/lpr/lprm/lprm.c b/usr.sbin/lpr/lprm/lprm.c index 741a8fae13618..35d88145adcc0 100644 --- a/usr.sbin/lpr/lprm/lprm.c +++ b/usr.sbin/lpr/lprm/lprm.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -93,7 +94,7 @@ main(int argc, char *argv[]) printer = NULL; uid = getuid(); euid = geteuid(); - seteuid(uid); /* be safe */ + PRIV_END /* be safe */ progname = argv[0]; gethostname(local_host, sizeof(local_host)); openlog("lpd", 0, LOG_LPR); -- cgit v1.3 From 145992504973bd16cf3518af9ba5ce185fefa82a Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Mon, 22 Oct 2012 18:01:38 +0000 Subject: Resurrect usage.hlp and securelevel.hlp from sysinstall(8) and integrate them into bsdconfig(8). Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/bsdconfig | 17 ++++-- usr.sbin/bsdconfig/include/Makefile | 2 +- usr.sbin/bsdconfig/include/messages.subr | 2 + usr.sbin/bsdconfig/include/usage.hlp | 64 ++++++++++++++++++++++ usr.sbin/bsdconfig/security/include/Makefile | 2 +- .../bsdconfig/security/include/securelevel.hlp | 40 ++++++++++++++ usr.sbin/bsdconfig/security/kern_securelevel | 23 ++++++-- usr.sbin/bsdconfig/share/common.subr | 26 ++++----- 8 files changed, 149 insertions(+), 27 deletions(-) create mode 100644 usr.sbin/bsdconfig/include/usage.hlp create mode 100644 usr.sbin/bsdconfig/security/include/securelevel.hlp (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index a9dc1fa4c3e15..860afd46eaf75 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -37,7 +37,8 @@ f_include $BSDCFG_SHARE/strings.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" f_include_lang $BSDCFG_LIBE/include/messages.subr -f_include_help $BSDCFG_LIBE/include/bsdconfig.hlp +f_include_help BSDCONFIG $BSDCFG_LIBE/include/bsdconfig.hlp +f_include_help USAGE $BSDCFG_LIBE/include/usage.hlp ############################################################ FUNCTIONS @@ -141,12 +142,13 @@ dialog_menu_main() local menu_list size menu_list=" - 'X' '$msg_exit' '$msg_exit_bsdconfig' + 'X' '$msg_exit' '$msg_exit_bsdconfig' + '1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system' " # END-QUOTE local sanitize_awk="{ gsub(/'/, \"'\\\\''\"); print }" - local menuitem menu_title menu_help menu_selection index=1 + local menuitem menu_title menu_help menu_selection index=2 for menuitem in $( ls -d [0-9][0-9][0-9].* ); do [ $index -lt ${#DIALOG_MENU_TAGS} ] || break tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) @@ -187,7 +189,7 @@ dialog_menu_main() --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_exit_bsdconfig\" \ --help-button \ - ${USE_XDIALOG:+--help \"\$( f_include_help )\"} \ + ${USE_XDIALOG:+--help \"\$( f_include_help BSDCONFIG )\"} \ --menu \"\$prompt\" $size $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) @@ -300,7 +302,7 @@ while :; do if [ $retval -eq 2 ]; then # The Help button was pressed - f_show_msg "%s" "$( f_include_help )" + f_show_msg "%s" "$( f_include_help BSDCONFIG )" continue elif [ $retval -ne 0 ]; then f_die @@ -311,6 +313,11 @@ while :; do break ;; + 1) # Usage + f_show_msg "%s" "$( f_include_help USAGE )" + continue + ;; + *) # Dynamically loaded menuitem cmd=$( eval echo \"\$menu_program$mtag\" ) f_dprintf "cmd=[$cmd]" diff --git a/usr.sbin/bsdconfig/include/Makefile b/usr.sbin/bsdconfig/include/Makefile index 27ce643b0d70d..cb4b1b6ccc438 100644 --- a/usr.sbin/bsdconfig/include/Makefile +++ b/usr.sbin/bsdconfig/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/include -FILES= bsdconfig.hlp messages.subr +FILES= bsdconfig.hlp messages.subr usage.hlp beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr index a3b8ace7d6983..ec988adb0f774 100644 --- a/usr.sbin/bsdconfig/include/messages.subr +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -51,11 +51,13 @@ msg_permission_denied="%s: %s: Permission denied" msg_please_enter_password="Please enter your password for sudo(8):" msg_please_enter_username_password="Please enter a username and password for sudo(8):" msg_previous_syntax_errors="%s: Not overwriting \`%s' due to previous syntax errors" +msg_quick_start_how_to_use_this_menu_system="Quick start - How to use this menu system" msg_secure_mode_requires_x11="Secure-mode requires X11 (use \`-X')!" msg_secure_mode_requires_root="Secure-mode requires root-access!" msg_sorry_try_again="Sorry, try again." msg_try_sudo_only_this_once="Try sudo(8) only this once" msg_unknown_user="Unknown user: %s" +msg_usage="Usage" msg_user_disallowed="User disallowed: %s" msg_yes="Yes" msg_you_are_not_root_but="You are not root but %s can use sudo(8).\nWhat would you like to do?" diff --git a/usr.sbin/bsdconfig/include/usage.hlp b/usr.sbin/bsdconfig/include/usage.hlp new file mode 100644 index 0000000000000..4412da7ddcb8b --- /dev/null +++ b/usr.sbin/bsdconfig/include/usage.hlp @@ -0,0 +1,64 @@ +HOW TO USE THIS SYSTEM +====================== + +[press the PageDown key to go to the next screen when you finish + reading this one] + +The following keys are recognized in most of the dialogs you'll +encounter during this installation: + +KEY ACTION +--- ------ +SPACE Select or toggle the current item. +ENTER Finish with a menu or item. +UP ARROW Move to previous item (or up, in a text display box). +DOWN ARROW Move to next item (or down, in a text display box). +TAB Move to next item or group. +RIGHT ARROW Move to next item or group (same as TAB). +SHIFT-TAB Move to previous item or group. +LEFT ARROW Move to previous item or group (same as SHIFT-TAB). +PAGE UP In text display boxes, scrolls up one page. +PAGE DOWN In text display boxes, scrolls down one page. +F1 Display associated help text. + +If you see small "^(-)" or "v(+)" symbols at the edges of a menu, it +means that there are more items above or below the current one that +aren't being shown (due to insufficient screen space). In text +display boxes, the amount of text above the current point will be +displayed as a percentage in the lower right corner. Using the +Up/Down arrow keys will cause the object to scroll by line. The +PageUp and PageDown keys will scroll by entire screens. + +Selecting OK in a menu will confirm whatever action it's controlling. +Selecting Cancel will cancel the operation and generally return you to +the previous menu. Use TAB to move the cursor around and select the +buttons. + +Most screens offer a Help button - USE IT! It generally offers useful +context-specific hints on what to do and if you're at all unsure about +what to do at a given configuration menu, choose Help! + + +SPECIAL FEATURES: +================= + +It is possible to select a menu item by typing the first character of +its name, if unique. This will generally be an item number. + +The console driver contains a scroll-back buffer for reviewing things +that may have scrolled off the screen. To use scroll-back, press the +"Scroll Lock" key on your keyboard and use the arrow or Page Up/Page +Down keys to move through the saved text. To leave scroll-back mode, +press the Scroll Lock key again. This feature is most useful for +reading back through your boot messages (go ahead, try it now!) though +it's also useful when dealing with sub-shells or other "expert modes" +that don't use menus and tend to scroll their output off the top of +the screen. + +FreeBSD also supports multiple "virtual consoles" which you can use +in order to have several active sessions at once. Use ALT-F to +switch between screens, where `F' is the function key corresponding +to the screen you wish to see. By default, the system comes with 8 +virtual consoles enabled - you can enable more by editing the +/etc/ttys file and turning the "off" field to "on" in the relevant vty +entries (up to 12). diff --git a/usr.sbin/bsdconfig/security/include/Makefile b/usr.sbin/bsdconfig/security/include/Makefile index 7cdfcc6921440..a94c127d81d02 100644 --- a/usr.sbin/bsdconfig/security/include/Makefile +++ b/usr.sbin/bsdconfig/security/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/130.security/include -FILES= messages.subr +FILES= messages.subr securelevel.hlp beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/security/include/securelevel.hlp b/usr.sbin/bsdconfig/security/include/securelevel.hlp new file mode 100644 index 0000000000000..27eb1ec231b91 --- /dev/null +++ b/usr.sbin/bsdconfig/security/include/securelevel.hlp @@ -0,0 +1,40 @@ +This menu allows you to configure the Securelevel mechanism in FreeBSD. + +Securelevels may be used to limit the privileges assigned to the +root user in multi-user mode, which in turn may limit the effects of +a root compromise, at the cost of reducing administrative functions. +Refer to the security(7) and init(8) manual pages for complete details. + + -1 Permanently insecure mode - always run the system in level 0 + mode. This is the default initial value. + + 0 Insecure mode - immutable and append-only flags may be turned + off. All devices may be read or written subject to their + permissions. + + 1 Secure mode - the system immutable and system append-only + flags may not be turned off; disks for mounted file systems, + /dev/mem, /dev/kmem and /dev/io (if your platform has it) + may not be opened for writing; kernel modules (see kld(4)) + may not be loaded or unloaded. + + 2 Highly secure mode - same as secure mode, plus disks may not + be opened for writing (except by mount(2)) whether mounted or + not. This level precludes tampering with file systems by + unmounting them, but also inhibits running newfs(8) while the + system is multi-user. + + In addition, kernel time changes are restricted to less than + or equal to one second. Attempts to change the time by more + than this will log the message ``Time adjustment clamped to +1 + second''. + + 3 Network secure mode - same as highly secure mode, plus IP + packet filter rules (see ipfw(8), ipfirewall(4) and pfctl(8)) + cannot be changed and dummynet(4) or pf(4) configuration + cannot be adjusted. + +Securelevels must be used in combination with careful system design and +application of protective mechanisms to prevent system configuration +files from being modified in a way that compromises the protections of +the securelevel variable upon reboot. diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index c48691ecb586e..dbabd00e3c2b6 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -36,6 +36,7 @@ f_include $BSDCFG_SHARE/sysrc.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="130.security" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr +f_include_help SECURELEVEL $BSDCFG_LIBE/$APP_DIR/include/securelevel.hlp ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" @@ -74,6 +75,8 @@ dialog_menu_main() --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ + --help-button \ + ${USE_XDIALOG:+--help \"\$( f_include_help SECURELEVEL )\"} \ --menu \"\$prompt\" $size \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD @@ -109,11 +112,21 @@ f_mustberoot_init # # Launch application main menu # -dialog_menu_main -retval=$? -mtag=$( f_dialog_menutag ) - -[ $retval -eq 0 ] || f_die +while :; do + dialog_menu_main + retval=$? + mtag=$( f_dialog_menutag ) + + if [ $retval -eq 2 ]; then + # The Help button was pressed + f_show_msg "%s" "$( f_include_help SECURELEVEL )" + continue + elif [ $retval -ne 0 ]; then + f_die + fi + + break +done case "$mtag" in "$msg_disabled") diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 9d034eacee1a9..11fc0380916f1 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -179,41 +179,37 @@ f_include_lang() fi } -# f_include_help [$file] +# f_include_help NAME [$file] # -# When given an argument, cache the contents of a language help-file (to later -# be retrieved by executing again without arguments). +# When given both arguments, cache the contents of a language help-file to +# later be retrieved by executing again with only the first argument. # # Automatically takes $LANG and $LC_ALL into consideration when reading $file # (suffix ".$LC_ALL" or ".$LANG" will automatically be added prior to loading # the language help-file). # -# No error is produced if (a) a language has been requested (by setting either -# $LANG or $LC_ALL in the environment) and (b) the language help-file does not -# exist -- in which case we will fall back to loading $file without-suffix. +# If a language has been requested by setting either $LANG or $LC_ALL in the +# environment and the language-specific help-file does not exist we will fall +# back to $file without-suffix. # # If the language help-file does not exist, an error is cached in place of the # help-file contents. # -# When called without arguments, the cached value (if any) is produced. Each -# time this function is called, the cache is overwritten with the newly loaded -# contents. -# f_include_help() { - local file="$1" + local name="$1" file="$2" if [ "$file" ]; then local lang="${LANG:-$LC_ALL}" - f_dprintf "lang=[$lang]" + f_dprintf "name=[$name] lang=[$lang]" if [ -f "$file.$lang" ]; then - setvar HELP_$$ "$( cat "$file.$lang" 2>&1 )" + setvar HELP_${name}_$$ "$( cat "$file.$lang" 2>&1 )" else - setvar HELP_$$ "$( cat "$file" 2>&1 )" + setvar HELP_${name}_$$ "$( cat "$file" 2>&1 )" fi else - eval echo \"\$HELP_$$\" + eval echo \"\$HELP_${name}_$$\" fi } -- cgit v1.3 From 4ca6fb657e8aa5445eb1f337bc4ab681abe9ec91 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Mon, 22 Oct 2012 18:14:27 +0000 Subject: Optimize syntax to use builtins and reduce unnecessary forking where possible. Reviewed by: nwhitehorn Approved by: adrian (co-mentor) --- usr.sbin/bsdinstall/bsdinstall | 10 +++------- usr.sbin/bsdinstall/scripts/auto | 14 +++++++------- 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdinstall/bsdinstall b/usr.sbin/bsdinstall/bsdinstall index 8225daffda77e..2b5550193449b 100755 --- a/usr.sbin/bsdinstall/bsdinstall +++ b/usr.sbin/bsdinstall/bsdinstall @@ -32,12 +32,8 @@ : ${BSDINSTALL_DISTDIR="/usr/freebsd-dist"}; export BSDINSTALL_DISTDIR : ${BSDINSTALL_CHROOT="/mnt"}; export BSDINSTALL_CHROOT -VERB=$1; shift +VERB=${1:-auto}; shift -if [ -z "$VERB" ]; then - VERB=auto -fi - -test -d "$BSDINSTALL_TMPETC" || mkdir "$BSDINSTALL_TMPETC" +[ -d "$BSDINSTALL_TMPETC" ] || mkdir -p "$BSDINSTALL_TMPETC" echo "Running installation step: $VERB $@" >> "$BSDINSTALL_LOG" -exec "/usr/libexec/bsdinstall/$VERB" "$@" 2>>"$BSDINSTALL_LOG" +exec "/usr/libexec/bsdinstall/$VERB" "$@" 2>> "$BSDINSTALL_LOG" diff --git a/usr.sbin/bsdinstall/scripts/auto b/usr.sbin/bsdinstall/scripts/auto index f6d1d37bece28..356a2152748c3 100755 --- a/usr.sbin/bsdinstall/scripts/auto +++ b/usr.sbin/bsdinstall/scripts/auto @@ -53,15 +53,15 @@ bsdinstall hostname || error export DISTRIBUTIONS="base.txz kernel.txz" if [ -f $BSDINSTALL_DISTDIR/MANIFEST ]; then - DISTMENU=`cut -f 4,5,6 $BSDINSTALL_DISTDIR/MANIFEST | grep -v -e ^kernel -e ^base` + DISTMENU=`awk '!/^(kernel|base)/{print $4,$5,$6}' $BSDINSTALL_DISTDIR/MANIFEST` exec 3>&1 - EXTRA_DISTS=$(echo $DISTMENU | xargs dialog \ - --backtitle "FreeBSD Installer" \ - --title "Distribution Select" --nocancel --separate-output \ - --checklist "Choose optional system components to install:" \ - 0 0 0 \ - 2>&1 1>&3) + EXTRA_DISTS=$( eval dialog \ + --backtitle \"FreeBSD Installer\" \ + --title \"Distribution Select\" --nocancel --separate-output \ + --checklist \"Choose optional system components to install:\" \ + 0 0 0 $DISTMENU \ + 2>&1 1>&3 ) for dist in $EXTRA_DISTS; do export DISTRIBUTIONS="$DISTRIBUTIONS $dist.txz" done -- cgit v1.3 From 16ddaa34674f2072aa60771314d6373ad64c91f1 Mon Sep 17 00:00:00 2001 From: Ken Smith Date: Wed, 24 Oct 2012 01:42:46 +0000 Subject: Add the release package directory for 9.1-RELEASE. --- usr.sbin/pkg_install/add/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'usr.sbin') diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c index 94ef1278ced42..563e04bd638df 100644 --- a/usr.sbin/pkg_install/add/main.c +++ b/usr.sbin/pkg_install/add/main.c @@ -89,6 +89,7 @@ struct { { 802000, 802499, "/packages-8.2-release" }, { 803000, 803499, "/packages-8.3-release" }, { 900000, 900499, "/packages-9.0-release" }, + { 901000, 901499, "/packages-9.1-release" }, { 300000, 399000, "/packages-3-stable" }, { 400000, 499000, "/packages-4-stable" }, { 502100, 502128, "/packages-5-current" }, -- cgit v1.3 From ba8999e989415a5a90a8fe6eb43765542cfd2e98 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Wed, 24 Oct 2012 12:49:03 +0000 Subject: Avoid changing IFS in a global context as this can sometimes cause errors later on. PR: bin/172715 Submitted by: Matthew D.Fuller (older version) Submitted by: dteske Approved by: cperciva MFC after: 1 week --- usr.sbin/portsnap/portsnap/portsnap.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/portsnap/portsnap/portsnap.sh b/usr.sbin/portsnap/portsnap/portsnap.sh index 80718203a8e15..ed9c28d5c6c04 100644 --- a/usr.sbin/portsnap/portsnap/portsnap.sh +++ b/usr.sbin/portsnap/portsnap/portsnap.sh @@ -587,7 +587,7 @@ fetch_metadata_sanity() { # Take a list of ${oldhash}|${newhash} and output a list of needed patches fetch_make_patchlist() { - IFS='|' + local IFS='|' echo "" 1>${QUIETREDIR} grep -vE "^([0-9a-f]{64})\|\1$" | while read X Y; do @@ -596,7 +596,6 @@ fetch_make_patchlist() { echo "${X}|${Y}" done echo "" 1>${QUIETREDIR} - IFS= } # Print user-friendly progress statistics @@ -711,7 +710,7 @@ fetch_update() { # Attempt to apply metadata patches echo -n "Applying metadata patches... " - IFS='|' + local oldifs="$IFS" IFS='|' while read X Y; do if [ ! -f "${X}-${Y}.gz" ]; then continue; fi gunzip -c < ${X}-${Y}.gz > diff @@ -725,7 +724,7 @@ fetch_update() { fi rm -f diff OLD NEW ${X}-${Y}.gz ptmp done < patchlist 2>${QUIETREDIR} - IFS= + IFS="$oldifs" echo "done." # Update metadata without patches @@ -792,7 +791,7 @@ fetch_update() { # Attempt to apply ports patches PATCHCNT=`wc -l patchlist` echo "Applying patches... " - IFS='|' + local oldifs="$IFS" IFS='|' I=0 while read X Y; do I=$(($I + 1)) @@ -810,7 +809,7 @@ fetch_update() { fi rm -f diff OLD NEW ${X}-${Y} done < patchlist 2>${QUIETREDIR} - IFS= + IFS="$oldifs" echo "done." # Update ports without patches @@ -912,7 +911,7 @@ extract_metadata() { # Do the actual work involved in "extract" extract_run() { - local IFS='|' + local oldifs="$IFS" IFS='|' mkdir -p ${PORTSDIR} || return 1 if ! @@ -948,6 +947,8 @@ extract_run() { return 0; fi + IFS="$oldifs" + extract_metadata extract_indices } -- cgit v1.3 From c47775a2f9946a04bbfddc55d75521b792915864 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Wed, 24 Oct 2012 16:03:05 +0000 Subject: PRIV_START and PRIV_END were reversed. Submitted by: KAHO Toshikazu Approved by: secteam (simon) MFC after: 3 days --- usr.sbin/lpr/common_source/lp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h index ab7ce2ecd6e05..9014ee050612f 100644 --- a/usr.sbin/lpr/common_source/lp.h +++ b/usr.sbin/lpr/common_source/lp.h @@ -253,10 +253,10 @@ typedef enum { TR_SENDING, TR_RECVING, TR_PRINTING } tr_sendrecv; * seteuid() macros. */ #define PRIV_START { \ - if (seteuid(uid) != 0) err(1, "seteuid failed"); \ + if (seteuid(euid) != 0) err(1, "seteuid failed"); \ } #define PRIV_END { \ - if (seteuid(euid) != 0) err(1, "seteuid failed"); \ + if (seteuid(uid) != 0) err(1, "seteuid failed"); \ } -- cgit v1.3 From ff473561daa14655c14122f81b758d5325c26c4b Mon Sep 17 00:00:00 2001 From: Jim Harris Date: Thu, 25 Oct 2012 17:22:37 +0000 Subject: For PCI Express capability, if max link width is greater than zero, print the current and max link speed. Sponsored by: Intel Discussed with: jhb MFC after: 1 week --- usr.sbin/pciconf/cap.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index 4d3fab9a708c0..ce184dd294535 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -363,6 +363,22 @@ cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr) #define MAX_PAYLOAD(field) (128 << (field)) +static const char * +link_speed_string(uint8_t speed) +{ + + switch (speed) { + case 1: + return ("2.5"); + case 2: + return ("5.0"); + case 3: + return ("8.0"); + default: + return ("undef"); + } +} + static void cap_express(int fd, struct pci_conf *p, uint8_t ptr) { @@ -418,6 +434,16 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) flags = read_config(fd, &p->pc_sel, ptr+ PCIER_LINK_STA, 2); printf(" link x%d(x%d)", (flags & PCIEM_LINK_STA_WIDTH) >> 4, (val & PCIEM_LINK_CAP_MAX_WIDTH) >> 4); + /* + * Only print link speed info if the link's max width is + * greater than 0. + */ + if ((val & PCIEM_LINK_CAP_MAX_WIDTH) != 0) { + printf("\n speed"); + printf(" %s(%s)", (flags & PCIEM_LINK_STA_WIDTH) == 0 ? + "0.0" : link_speed_string(flags & PCIEM_LINK_STA_SPEED), + link_speed_string(val & PCIEM_LINK_CAP_MAX_SPEED)); + } } static void -- cgit v1.3 From 6511bcb8c00ee6fc65d3a7bf120e803357bcfe41 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Thu, 25 Oct 2012 20:16:38 +0000 Subject: Let lpr build with -Wmissing-variable-declarations. Mark variables static where possible and place the uid/euid variables in lp.h, so that we can compile-time enforce that these variables have the same type. --- usr.sbin/lpr/common_source/common.c | 2 -- usr.sbin/lpr/common_source/displayq.c | 2 -- usr.sbin/lpr/common_source/lp.h | 3 +++ usr.sbin/lpr/common_source/net.c | 2 -- usr.sbin/lpr/common_source/rmjob.c | 2 -- usr.sbin/lpr/common_source/startdaemon.c | 2 -- usr.sbin/lpr/filters/lpf.c | 22 +++++++++++----------- 7 files changed, 14 insertions(+), 21 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c index ce4a62dc70e36..93657d9599b0b 100644 --- a/usr.sbin/lpr/common_source/common.c +++ b/usr.sbin/lpr/common_source/common.c @@ -70,8 +70,6 @@ __FBSDID("$FreeBSD$"); char line[BUFSIZ]; const char *progname; /* program name */ -extern uid_t uid, euid; - static int compar(const void *_p1, const void *_p2); /* diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c index b4bd7ce40e449..3c038d66430eb 100644 --- a/usr.sbin/lpr/common_source/displayq.c +++ b/usr.sbin/lpr/common_source/displayq.c @@ -75,8 +75,6 @@ __FBSDID("$FreeBSD$"); /* * Stuff for handling job specifications */ -extern uid_t uid, euid; - static int col; /* column on screen */ static char current[MAXNAMLEN+1]; /* current file being printed */ static char file[MAXNAMLEN+1]; /* print file name */ diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h index 9014ee050612f..fb05d2f92ad98 100644 --- a/usr.sbin/lpr/common_source/lp.h +++ b/usr.sbin/lpr/common_source/lp.h @@ -252,6 +252,9 @@ typedef enum { TR_SENDING, TR_RECVING, TR_PRINTING } tr_sendrecv; /* * seteuid() macros. */ + +extern uid_t uid, euid; + #define PRIV_START { \ if (seteuid(euid) != 0) err(1, "seteuid failed"); \ } diff --git a/usr.sbin/lpr/common_source/net.c b/usr.sbin/lpr/common_source/net.c index 0ac816a2d6ddb..ab6fa167bbbe0 100644 --- a/usr.sbin/lpr/common_source/net.c +++ b/usr.sbin/lpr/common_source/net.c @@ -80,8 +80,6 @@ u_char family = PF_UNSPEC; u_char family = PF_INET; #endif -extern uid_t uid, euid; - /* * Create a TCP connection to host "rhost" at port "rport". * If rport == 0, then use the printer service port. diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c index d0327a232251c..c89a6f0c0f94a 100644 --- a/usr.sbin/lpr/common_source/rmjob.c +++ b/usr.sbin/lpr/common_source/rmjob.c @@ -69,8 +69,6 @@ static int all = 0; /* eliminate all files (root only) */ static int cur_daemon; /* daemon's pid */ static char current[7+MAXHOSTNAMELEN]; /* active control file name */ -extern uid_t uid, euid; /* real and effective user id's */ - static void alarmhandler(int _signo); static void do_unlink(char *_file); static int isowner(char *_owner, char *_file, const char *_cfhost); diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c index a1398bb587f5d..8e4f60a6e9db1 100644 --- a/usr.sbin/lpr/common_source/startdaemon.c +++ b/usr.sbin/lpr/common_source/startdaemon.c @@ -49,8 +49,6 @@ __FBSDID("$FreeBSD$"); #include "lp.h" #include "pathnames.h" -extern uid_t uid, euid; - /* * Tell the printer daemon that there are new files in the spool directory. */ diff --git a/usr.sbin/lpr/filters/lpf.c b/usr.sbin/lpr/filters/lpf.c index b277d1768aa5b..f8ffe523a9375 100644 --- a/usr.sbin/lpr/filters/lpf.c +++ b/usr.sbin/lpr/filters/lpf.c @@ -60,17 +60,17 @@ __FBSDID("$FreeBSD$"); #define MAXWIDTH 132 #define MAXREP 10 -char buf[MAXREP][MAXWIDTH]; -int maxcol[MAXREP] = {-1}; -int lineno; -int width = 132; /* default line length */ -int length = 66; /* page length */ -int indent; /* indentation length */ -int npages = 1; -int literal; /* print control characters */ -char *name; /* user's login name */ -char *host; /* user's machine name */ -char *acctfile; /* accounting information file */ +static char buf[MAXREP][MAXWIDTH]; +static int maxcol[MAXREP] = {-1}; +static int lineno; +static int width = 132; /* default line length */ +static int length = 66; /* page length */ +static int indent; /* indentation length */ +static int npages = 1; +static int literal; /* print control characters */ +static char *name; /* user's login name */ +static char *host; /* user's machine name */ +static char *acctfile; /* accounting information file */ int main(int argc, char *argv[]) -- cgit v1.3 From 9c6cbb8aad68c651a89bbb57bee790a268e7b692 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Thu, 25 Oct 2012 21:04:11 +0000 Subject: When Xdialog(1) is passed a NULL argument to its `--help' option, Xdialog(1) acts like dialog(1) in that it returns exit status 2 when the help button is chosen. Approved by: adri (co-mentor) (implicit) --- usr.sbin/bsdconfig/bsdconfig | 2 +- usr.sbin/bsdconfig/security/kern_securelevel | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index 860afd46eaf75..5684c49d2b907 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -189,7 +189,7 @@ dialog_menu_main() --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_exit_bsdconfig\" \ --help-button \ - ${USE_XDIALOG:+--help \"\$( f_include_help BSDCONFIG )\"} \ + ${USE_XDIALOG:+--help \"\"} \ --menu \"\$prompt\" $size $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index dbabd00e3c2b6..a0a7a956fd42b 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -76,7 +76,7 @@ dialog_menu_main() --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --help-button \ - ${USE_XDIALOG:+--help \"\$( f_include_help SECURELEVEL )\"} \ + ${USE_XDIALOG:+--help \"\"} \ --menu \"\$prompt\" $size \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD -- cgit v1.3 From 7a5c30c5b67555c9f76a053812ba80ae258b8874 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Thu, 25 Oct 2012 22:54:29 +0000 Subject: Second attempt to add @every_second keyword support. Due to multiple requests, default to the previous 60-seconds scheduling method unless there is any @every_second entries to conserve CPU cycles and power. This change also improves scheduling in the default mode by running as close to the beginning of the minnute as possible by replacing sleep(3) with nanosleep(2). Previously, the tasks would run anywhere within the first second of the minute and that offset drifted back and forth each time cron(8) was engaged. MFC after: 1 month --- usr.sbin/cron/cron/cron.c | 148 ++++++++++++++++++++++++++++++++-------- usr.sbin/cron/cron/cron.h | 6 ++ usr.sbin/cron/crontab/crontab.5 | 2 + usr.sbin/cron/lib/entry.c | 23 +++++++ 4 files changed, 149 insertions(+), 30 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 8524ff9dfcb83..d374319f7483b 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -35,9 +35,9 @@ static const char rcsid[] = static void usage(void), run_reboot_jobs(cron_db *), - cron_tick(cron_db *), - cron_sync(void), - cron_sleep(cron_db *), + cron_tick(cron_db *, int), + cron_sync(int), + cron_sleep(cron_db *, int), cron_clean(cron_db *), #ifdef USE_SIGCHLD sigchld_handler(int), @@ -45,6 +45,8 @@ static void usage(void), sighup_handler(int), parse_args(int c, char *v[]); +static int run_at_secres(cron_db *); + static time_t last_time = 0; static int dst_enabled = 0; struct pidfh *pfh; @@ -98,6 +100,9 @@ main(argc, argv) char *argv[]; { cron_db database; + int runnum; + int secres1, secres2; + struct tm *tm; ProgramName = argv[0]; @@ -147,23 +152,47 @@ main(argc, argv) database.tail = NULL; database.mtime = (time_t) 0; load_database(&database); + secres1 = secres2 = run_at_secres(&database); run_reboot_jobs(&database); - cron_sync(); + cron_sync(secres1); + runnum = 0; while (TRUE) { # if DEBUGGING /* if (!(DebugFlags & DTEST)) */ # endif /*DEBUGGING*/ - cron_sleep(&database); - - load_database(&database); + cron_sleep(&database, secres1); + + if (secres1 == 0 || runnum % 60 == 0) { + load_database(&database); + secres2 = run_at_secres(&database); + if (secres2 != secres1) { + secres1 = secres2; + if (secres1 != 0) { + runnum = 0; + } else { + /* + * Going from 1 sec to 60 sec res. If we + * are already at minute's boundary, so + * let it run, otherwise schedule for the + * next minute. + */ + tm = localtime(&TargetTime); + if (tm->tm_sec > 0) { + cron_sync(secres2); + continue; + } + } + } + } /* do this iteration */ - cron_tick(&database); + cron_tick(&database, secres1); - /* sleep 1 minute + /* sleep 1 or 60 seconds */ - TargetTime += 60; + TargetTime += (secres1 != 0) ? 1 : 60; + runnum += 1; } } @@ -187,29 +216,29 @@ run_reboot_jobs(db) static void -cron_tick(db) - cron_db *db; +cron_tick(cron_db *db, int secres) { static struct tm lasttm; static time_t diff = 0, /* time difference in seconds from the last offset change */ difflimit = 0; /* end point for the time zone correction */ struct tm otztm; /* time in the old time zone */ - int otzminute, otzhour, otzdom, otzmonth, otzdow; + int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; register struct tm *tm = localtime(&TargetTime); - register int minute, hour, dom, month, dow; + register int second, minute, hour, dom, month, dow; register user *u; register entry *e; /* make 0-based values out of these so we can use them as indicies */ + second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND; minute = tm->tm_min -FIRST_MINUTE; hour = tm->tm_hour -FIRST_HOUR; dom = tm->tm_mday -FIRST_DOM; month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; dow = tm->tm_wday -FIRST_DOW; - Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", - getpid(), minute, hour, dom, month, dow)) + Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", + getpid(), second, minute, hour, dom, month, dow)) if (dst_enabled && last_time != 0 && TargetTime > last_time /* exclude stepping back */ @@ -262,6 +291,7 @@ cron_tick(db) /* make 0-based values out of these so we can use them as indicies */ + otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND; otzminute = otztm.tm_min -FIRST_MINUTE; otzhour = otztm.tm_hour -FIRST_HOUR; otzdom = otztm.tm_mday -FIRST_DOM; @@ -283,7 +313,8 @@ cron_tick(db) e->uid, e->gid, e->cmd)) if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { - if (bit_test(e->minute, otzminute) + if (bit_test(e->second, otzsecond) + && bit_test(e->minute, otzminute) && bit_test(e->hour, otzhour) && bit_test(e->month, otzmonth) && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) @@ -302,7 +333,8 @@ cron_tick(db) continue; } - if (bit_test(e->minute, minute) + if (bit_test(e->second, second) + && bit_test(e->minute, minute) && bit_test(e->hour, hour) && bit_test(e->month, month) && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) @@ -332,38 +364,74 @@ cron_tick(db) * that's something sysadmin's know to expect what with crashing computers.. */ static void -cron_sync() { - register struct tm *tm; +cron_sync(int secres) { + struct tm *tm; TargetTime = time((time_t*)0); - tm = localtime(&TargetTime); - TargetTime += (60 - tm->tm_sec); + if (secres != 0) { + TargetTime += 1; + } else { + tm = localtime(&TargetTime); + TargetTime += (60 - tm->tm_sec); + } } +static int +timespec_subtract(struct timespec *result, struct timespec *x, + struct timespec *y) +{ + time_t nsec; + + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + nsec = (y->tv_nsec - x->tv_nsec) / 10000000 + 1; + y->tv_nsec -= 1000000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_nsec - y->tv_nsec > 1000000000) { + nsec = (x->tv_nsec - y->tv_nsec) / 1000000000; + y->tv_nsec += 1000000000 * nsec; + y->tv_sec -= nsec; + } + + /* tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return True if result is negative. */ + return (x->tv_sec < y->tv_sec); +} static void -cron_sleep(db) - cron_db *db; +cron_sleep(cron_db *db, int secres) { - int seconds_to_wait = 0; + int seconds_to_wait; + int rval; + struct timespec ctime, ttime, stime, remtime; /* * Loop until we reach the top of the next minute, sleep when possible. */ for (;;) { - seconds_to_wait = (int) (TargetTime - time((time_t*)0)); + clock_gettime(CLOCK_REALTIME, &ctime); + ttime.tv_sec = TargetTime; + ttime.tv_nsec = 0; + timespec_subtract(&stime, &ttime, &ctime); /* * If the seconds_to_wait value is insane, jump the cron */ - if (seconds_to_wait < -600 || seconds_to_wait > 600) { + if (stime.tv_sec < -600 || stime.tv_sec > 600) { cron_clean(db); - cron_sync(); + cron_sync(secres); continue; } + seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : + stime.tv_sec; + Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", getpid(), (long)TargetTime, seconds_to_wait)) @@ -372,13 +440,19 @@ cron_sleep(db) * to run, break */ - if (seconds_to_wait <= 0) + if (stime.tv_sec < 0) break; if (job_runqueue() == 0) { Debug(DSCH, ("[%d] sleeping for %d seconds\n", getpid(), seconds_to_wait)) - sleep(seconds_to_wait); + for (;;) { + rval = nanosleep(&stime, &remtime); + if (rval == 0 || errno != EINTR) + break; + stime.tv_sec = remtime.tv_sec; + stime.tv_nsec = remtime.tv_nsec; + } } } } @@ -484,3 +558,17 @@ parse_args(argc, argv) } } +static int +run_at_secres(cron_db *db) +{ + user *u; + entry *e; + + for (u = db->head; u != NULL; u = u->next) { + for (e = u->crontab; e != NULL; e = e->next) { + if ((e->flags & SEC_RES) != 0) + return 1; + } + } + return 0; +} diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h index 38eb33b0c6f58..1a814dfed836d 100644 --- a/usr.sbin/cron/cron/cron.h +++ b/usr.sbin/cron/cron/cron.h @@ -124,6 +124,10 @@ LineNumber = ln; \ } +#define FIRST_SECOND 0 +#define LAST_SECOND 59 +#define SECOND_COUNT (LAST_SECOND - FIRST_SECOND + 1) + #define FIRST_MINUTE 0 #define LAST_MINUTE 59 #define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) @@ -165,6 +169,7 @@ typedef struct _entry { #endif char **envp; char *cmd; + bitstr_t bit_decl(second, SECOND_COUNT); bitstr_t bit_decl(minute, MINUTE_COUNT); bitstr_t bit_decl(hour, HOUR_COUNT); bitstr_t bit_decl(dom, DOM_COUNT); @@ -176,6 +181,7 @@ typedef struct _entry { #define WHEN_REBOOT 0x04 #define RUN_AT 0x08 #define NOT_UNTIL 0x10 +#define SEC_RES 0x20 time_t lastrun; } entry; diff --git a/usr.sbin/cron/crontab/crontab.5 b/usr.sbin/cron/crontab/crontab.5 index 458175e7a48d2..9be37df264e5f 100644 --- a/usr.sbin/cron/crontab/crontab.5 +++ b/usr.sbin/cron/crontab/crontab.5 @@ -232,6 +232,8 @@ string meaning @daily Run once a day, "0 0 * * *". @midnight (same as @daily) @hourly Run once an hour, "0 * * * *". +@every_minute Run once a minute, "*/1 * * * *". +@every_second Run once a second. .Ed .Sh EXAMPLE CRON FILE .Bd -literal diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index ea015da62704e..57f72554d3446 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -151,6 +151,7 @@ load_entry(file, error_func, pw, envp) e->flags |= WHEN_REBOOT; } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ Debug(DPARS, ("load_entry()...yearly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); @@ -159,6 +160,7 @@ load_entry(file, error_func, pw, envp) e->flags |= DOW_STAR; } else if (!strcmp("monthly", cmd)) { Debug(DPARS, ("load_entry()...monthly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); @@ -167,6 +169,7 @@ load_entry(file, error_func, pw, envp) e->flags |= DOW_STAR; } else if (!strcmp("weekly", cmd)) { Debug(DPARS, ("load_entry()...weekly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); @@ -175,6 +178,7 @@ load_entry(file, error_func, pw, envp) bit_set(e->dow, 0); } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { Debug(DPARS, ("load_entry()...daily shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); @@ -182,11 +186,29 @@ load_entry(file, error_func, pw, envp) bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("hourly", cmd)) { Debug(DPARS, ("load_entry()...hourly shortcut\n")) + bit_set(e->second, 0); bit_set(e->minute, 0); bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + } else if (!strcmp("every_minute", cmd)) { + Debug(DPARS, ("load_entry()...every_minute shortcut\n")) + bit_set(e->second, 0); + bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); + bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); + bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); + bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); + bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + } else if (!strcmp("every_second", cmd)) { + Debug(DPARS, ("load_entry()...every_second shortcut\n")) + e->flags |= SEC_RES; + bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1)); + bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); + bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); + bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); + bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); + bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else { ecode = e_timespec; goto eof; @@ -201,6 +223,7 @@ load_entry(file, error_func, pw, envp) } } else { Debug(DPARS, ("load_entry()...about to parse numerics\n")) + bit_set(e->second, 0); ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file); -- cgit v1.3 From 40dfc82d3519575734e8ceb6774f74ac74a84dd5 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 00:31:25 +0000 Subject: Switch from using a msgbox to display help files to a textbox instead. The problem with using a msgbox was one of truncation in the case of Xdialog(1) and lack of screen real-estate (since the msgbox is not scrollable in X11 while a textbox is). The textbox renders the text much better and is more appropriate for this type of data display. Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/bsdconfig | 9 ++-- usr.sbin/bsdconfig/security/kern_securelevel | 5 ++- usr.sbin/bsdconfig/share/common.subr | 65 +++++++++++++--------------- usr.sbin/bsdconfig/share/dialog.subr | 42 ++++++++++++++++++ 4 files changed, 81 insertions(+), 40 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index 5684c49d2b907..f80e5a90d2e87 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -37,8 +37,9 @@ f_include $BSDCFG_SHARE/strings.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" f_include_lang $BSDCFG_LIBE/include/messages.subr -f_include_help BSDCONFIG $BSDCFG_LIBE/include/bsdconfig.hlp -f_include_help USAGE $BSDCFG_LIBE/include/usage.hlp + +BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp +USAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp ############################################################ FUNCTIONS @@ -302,7 +303,7 @@ while :; do if [ $retval -eq 2 ]; then # The Help button was pressed - f_show_msg "%s" "$( f_include_help BSDCONFIG )" + f_show_help "$BSDCONFIG_HELPFILE" continue elif [ $retval -ne 0 ]; then f_die @@ -314,7 +315,7 @@ while :; do ;; 1) # Usage - f_show_msg "%s" "$( f_include_help USAGE )" + f_show_help "$USAGE_HELPFILE" continue ;; diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index a0a7a956fd42b..1bb89d8800624 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -36,7 +36,8 @@ f_include $BSDCFG_SHARE/sysrc.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="130.security" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr -f_include_help SECURELEVEL $BSDCFG_LIBE/$APP_DIR/include/securelevel.hlp + +SECURELEVEL_HELPFILE=$BSDCFG_LIBE/$APP_DIR/include/securelevel.hlp ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" @@ -119,7 +120,7 @@ while :; do if [ $retval -eq 2 ]; then # The Help button was pressed - f_show_msg "%s" "$( f_include_help SECURELEVEL )" + f_show_help "$SECURELEVEL_HELPFILE" continue elif [ $retval -ne 0 ]; then f_die diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 11fc0380916f1..46fdc6fb10006 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -140,6 +140,37 @@ f_show_msg() fi } +# f_show_help $file +# +# Display a language help-file. Automatically takes $LANG and $LC_ALL into +# consideration when displaying $file (suffix ".$LC_ALL" or ".$LANG" will +# automatically be added prior to loading the language help-file). +# +# If a language has been requested by setting either $LANG or $LC_ALL in the +# environment and the language-specific help-file does not exist we will fall +# back to $file without-suffix. +# +# If the language help-file does not exist, an error is displayed instead. +# +f_show_help() +{ + local file="$1" + local lang="${LANG:-$LC_ALL}" + + [ -f "$file.$lang" ] && file="$file.$lang" + + # + # Use f_dialog_textbox from dialog.subr if possible, otherwise fall + # back to dialog(1) (without options, making it obvious when using + # un-aided system dialog). + # + if f_have f_dialog_textbox; then + f_dialog_textbox "$file" + else + dialog --msgbox "$( cat "$file" 2>&1 )" 0 0 + fi +} + # f_include $file # # Include a shell subroutine file. @@ -179,40 +210,6 @@ f_include_lang() fi } -# f_include_help NAME [$file] -# -# When given both arguments, cache the contents of a language help-file to -# later be retrieved by executing again with only the first argument. -# -# Automatically takes $LANG and $LC_ALL into consideration when reading $file -# (suffix ".$LC_ALL" or ".$LANG" will automatically be added prior to loading -# the language help-file). -# -# If a language has been requested by setting either $LANG or $LC_ALL in the -# environment and the language-specific help-file does not exist we will fall -# back to $file without-suffix. -# -# If the language help-file does not exist, an error is cached in place of the -# help-file contents. -# -f_include_help() -{ - local name="$1" file="$2" - - if [ "$file" ]; then - local lang="${LANG:-$LC_ALL}" - - f_dprintf "name=[$name] lang=[$lang]" - if [ -f "$file.$lang" ]; then - setvar HELP_${name}_$$ "$( cat "$file.$lang" 2>&1 )" - else - setvar HELP_${name}_$$ "$( cat "$file" 2>&1 )" - fi - else - eval echo \"\$HELP_${name}_$$\" - fi -} - # f_usage $file [ $key1 $value1 ... ] # # Display USAGE file with optional pre-processor macro definitions. The first diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index d2ba529f323c6..bd0fe2da4f028 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -1015,6 +1015,48 @@ f_dialog_msgbox() --msgbox \"\$msg_text\" $size } +############################################################ TEXTBOX FUNCTIONS + +# f_dialog_textbox $file +# +# Display the contents of $file (or an error if $file does not exist, etc.) in +# a dialog(1) textbox (which has a scrollable region for the text). The textbox +# remains until the user presses ENTER or ESC, acknowledging the modal dialog. +# +# If the user presses ENTER, the exit status is zero (success), otherwise if +# the user presses ESC the exit status is 255. +# +f_dialog_textbox() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local file="$1" + local contents retval size + + contents=$( cat "$file" 2>&1 ) + retval=$? + + size=$( f_dialog_buttonbox_size \ + "$DIALOG_TITLE" \ + "$DIALOG_BACKTITLE" \ + "$contents" ) + + if [ $retval -eq $SUCCESS ]; then + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --exit-label \"\$msg_ok\" \ + --no-cancel \ + --textbox \"\$file\" $size + else + eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --ok-label \"\$msg_ok\" \ + --msgbox \"\$msg_text\" $size + fi +} + ############################################################ YESNO FUNCTIONS # f_dialog_yesno $msg_text ... -- cgit v1.3 From 254917d4fe502183e114aa143c9d0355d7deb58e Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 00:46:09 +0000 Subject: Fix copy/paste error. Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/share/dialog.subr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index bd0fe2da4f028..a18264533de72 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -1053,7 +1053,7 @@ f_dialog_textbox() --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --ok-label \"\$msg_ok\" \ - --msgbox \"\$msg_text\" $size + --msgbox \"\$contents\" $size fi } -- cgit v1.3 From de8fd13b917bb3c363999b614db2690996ce1992 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 01:47:59 +0000 Subject: Tighten-up displays that use a NULL prompt (e.g., networking and usermgmt). Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/share/dialog.subr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index a18264533de72..353a8da106bcc 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -272,7 +272,7 @@ f_dialog_infobox_size() # # Set height based on number of rows in prompt # - height=$( echo "$prompt" | f_number_of_lines ) + height=$( echo -n "$prompt" | f_number_of_lines ) height=$(( $height + 2 )) # -- cgit v1.3 From 198a44b55db62db24fe4174e090d0d692e7e2513 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 02:45:29 +0000 Subject: Resurrect and integrate stable/9/sysinstall/help/tcp.hlp Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/networking/include/Makefile | 2 +- usr.sbin/bsdconfig/networking/include/tcp.hlp | 33 +++++++++++++++++++++++++ usr.sbin/bsdconfig/networking/share/device.subr | 14 +++++++++-- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 usr.sbin/bsdconfig/networking/include/tcp.hlp (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/networking/include/Makefile b/usr.sbin/bsdconfig/networking/include/Makefile index c93d4866fe9f5..be6cacb5b2fc4 100644 --- a/usr.sbin/bsdconfig/networking/include/Makefile +++ b/usr.sbin/bsdconfig/networking/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/120.networking/include -FILES= messages.subr +FILES= messages.subr tcp.hlp beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/networking/include/tcp.hlp b/usr.sbin/bsdconfig/networking/include/tcp.hlp new file mode 100644 index 0000000000000..6d0ba10f60f10 --- /dev/null +++ b/usr.sbin/bsdconfig/networking/include/tcp.hlp @@ -0,0 +1,33 @@ +This screen allows you to set up your general network parameters +(hostname, domain name, DNS server, etc) as well as the settings for a +given interface (which was selected from the menu before this screen). + +PLIP/SLIP users - please read through to the end of this doc! + +The "options" field is kind of special (read: a hack :-): + +Any valid options to ifconfig can be specified here, so if you need +to do something "special" to get your interface working, then here +is the place to do it. + +If you're running SLIP or PLIP, you also need to use it for specifying +the remote end of the link (simply type the foreign IP address in). +In the specific case where you're running PLIP with a Linux host peer +rather than a FreeBSD one, you also must add the "-link0" flag after the +foreign address. + +If you're dealing with an ethernet adaptor with multiple media +connectors (e.g. AUI, 10BT, 10B2, etc), you can use this field to +specify which one to use. Examples of valid strings include: + + "media 10base5/AUI" - Select the AUI port. + "media 10baseT/UTP" - Select the twisted pair port. + "media 10base2/BNC" - Select the BNC connector. + "media 100baseTX" - Select 100BaseT on a 100/10 dual adaptor. + +If you have a wireless interface and must specify arguments such as a +WEP key here, you may use something like: + + "wepmode on wepkey 0xFEEDFACE" + +When you're done with this form, select OK. diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index 17c7467bbae06..a002b6f553201 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -42,6 +42,8 @@ f_include $BSDCFG_SHARE/networking/routing.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr +TCP_HELPFILE=$BSDCFG_LIBE/$APP_DIR/include/tcp.hlp + ############################################################ GLOBALS # @@ -342,6 +344,8 @@ f_dialog_menu_netdev_edit() --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ + --help-button \ + ${USE_XDIALOG:+--help \"\"} \ --menu \"\$prompt\" $size \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD @@ -351,8 +355,14 @@ f_dialog_menu_netdev_edit() setvar DIALOG_MENU_$$ "$dialog_menu" local tag="$( f_dialog_menutag )" - # Return if "Cancel" was chosen (-1) or ESC was pressed (255) - [ $retval -eq $SUCCESS ] || return $retval + if [ $retval -eq 2 ]; then + # The Help button was pressed + f_show_help "$TCP_HELPFILE" + continue + elif [ $retval -ne $SUCCESS ]; then + # "Cancel" was chosen (-1) or ESC was pressed (255) + return $retval + fi # # Call the below ``modifier functions'' whose job it is to take -- cgit v1.3 From 04308bcd134ff633e198a92e2b0023a28478071b Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 02:49:12 +0000 Subject: Don't mask fwe(4) lp(4) or plip(4) devices from ifconfig(8) list. Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/networking/share/device.subr | 3 --- 1 file changed, 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index a002b6f553201..1f2f3c0b35dbf 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -205,10 +205,7 @@ f_dialog_menu_netdev() s/lo$d//g s/ppp$d//g s/sl$d//g - s/lp$d//g - s/fwe$d//g s/faith$d//g - s/plip$d//g # Convert all colons back into spaces y/:/ / -- cgit v1.3 From ffb5f4541b28b234d97d16d031560e3ad6d75d9f Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 02:50:14 +0000 Subject: Remove unnecessary quotation to clean things up. Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/networking/share/device.subr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index 1f2f3c0b35dbf..4f870af9ae894 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -243,7 +243,7 @@ f_dialog_menu_netdev() } ' ) printf "'%s%s' '%s'\n" \ - "$ifn" "${active:+*}" "$(f_device_desc "$ifn")" + $ifn "${active:+*}" "$( f_device_desc $ifn )" done ) if [ ! "$interfaces" ]; then -- cgit v1.3 From b28f4468862a72ba9fe4ae706633f3801cddd943 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Fri, 26 Oct 2012 02:50:16 +0000 Subject: This utility builds without NO_WCAST_ALIGN Tested with make universe No objections from: stas Approved by: cperciva MFC after: 3 days --- usr.sbin/cpucontrol/Makefile | 2 -- 1 file changed, 2 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile index 2d51429ec4c70..759e13d13b113 100644 --- a/usr.sbin/cpucontrol/Makefile +++ b/usr.sbin/cpucontrol/Makefile @@ -4,6 +4,4 @@ PROG= cpucontrol MAN= cpucontrol.8 SRCS= cpucontrol.c intel.c amd.c via.c -NO_WCAST_ALIGN= - .include -- cgit v1.3 From 724b4bfdf1306e4f2c451b6d146fe0fe0353b2c8 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Fri, 26 Oct 2012 03:20:04 +0000 Subject: Resurrect and integrate stable/9/usr.sbin/sysinstall/help/usermgmt.hlp Approved by: adrian (co-mentor) (implicit) --- usr.sbin/bsdconfig/usermgmt/include/Makefile | 2 +- usr.sbin/bsdconfig/usermgmt/include/usermgmt.hlp | 76 ++++++++++++++++++++++++ usr.sbin/bsdconfig/usermgmt/usermgmt | 20 ++++++- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 usr.sbin/bsdconfig/usermgmt/include/usermgmt.hlp (limited to 'usr.sbin') diff --git a/usr.sbin/bsdconfig/usermgmt/include/Makefile b/usr.sbin/bsdconfig/usermgmt/include/Makefile index a026728015ec1..25d2a07ae0a05 100644 --- a/usr.sbin/bsdconfig/usermgmt/include/Makefile +++ b/usr.sbin/bsdconfig/usermgmt/include/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${LIBEXECDIR}/bsdconfig/070.usermgmt/include -FILES= messages.subr +FILES= messages.subr usermgmt.hlp beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/usermgmt/include/usermgmt.hlp b/usr.sbin/bsdconfig/usermgmt/include/usermgmt.hlp new file mode 100644 index 0000000000000..77be9bdeb5c64 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/include/usermgmt.hlp @@ -0,0 +1,76 @@ +These screens allow you to add groups and users to your system. + +Many of the settings get reasonable defaults if you leave them blank. +The first time you have entered the name of the new group or user, the +system will show you what it would chose for most of these fields. +You are free to change them, of course. + + +User groups +=========== + +It's certainly almost generally a good idea to first create a new +group for your users. Common names for such a group are "users", or +even simply "other". Group names are used to control file access +permissions for users that belong to the same group. Several group +names are already used for system files. + +The numerical user or group IDs are often nothing you want to care for +explicitly. If you don't fill in these fields, the system will choose +reasonable defaults. However, these numbers (rather than the +associated names) are what the operating system actually uses to +distinguish users and groups -- hence they should normally be unique +to each person or group, respectively. + + +Users +===== + +The user's login ID is a short (up to 15 characters) alphanumeric ID +that the user must enter when logging into the system. It's often the +initial letters of the user's name, and commonly used in lower case. +It's also the local mail name for this user (though it's possible to +also set up more descriptive mail alias names later). + +The user's login group determines which group access rights the user +will initially get when logging in. If an additional list of groups is +provided which the user will become a member of, (s)he will also be +able to access files of those groups later without providing any +additional password etc. Except for the "wheel" case mentioned below, +the additional group membership list should normally not contain the +login group again. + +The user's password can also be set here, and should be chosen with +care - 6 or more characters, intermixing punctuation and numerics, and +*not* a word from the dictionary or related to the username is a good +password choice. + +Some of the system's groups have a special meaning. In particular, +members of group "wheel" are the only people who are later allowed to +become superuser using the command su(1). So if you're going to add a +new user who should later perform administrative tasks, don't forget +to add him to this group! (Well, ``he'' will most likely be yourself +in the very first place. :) + +Also, members of group "operator" will by default get permissions for +minor administrative operations, like performing system backups, or +shutting down the system -- without first becoming superuser! So, +take care when adding people to this group. + +The ``full name'' field serves as a comment only. It is also used by +mail front ends to determine the real name of the user, hence you +should actually fill in the first and last name of this user. By +convention, this field can be divided into comma-separated subfields, +where the office location, the work phone number, and the home phone +number follow the full name of the user. + +The home directory is the directory in the filesystem where the user +is being logged into, and where his personalized setup files (``dot +files'', since they usually begin with a `.' and are not displayed by +the ls(1) command by default) will be looked up. It is often created +under /usr/home/ or /home/. + +Finally, the shell is the user's initial command interpreter. The +default shell is /bin/sh, some users prefer the more historic +/bin/csh. Other, often more user-friendly and comfortable shells can +be found in the ports and packages collection. diff --git a/usr.sbin/bsdconfig/usermgmt/usermgmt b/usr.sbin/bsdconfig/usermgmt/usermgmt index 0bfb0c01fc005..a2e4b40f11d6a 100755 --- a/usr.sbin/bsdconfig/usermgmt/usermgmt +++ b/usr.sbin/bsdconfig/usermgmt/usermgmt @@ -37,6 +37,8 @@ f_include $BSDCFG_SHARE/mustberoot.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr +USERMGMT_HELPFILE=$BSDCFG_LIBE/$APP_DIR/include/usermgmt.hlp + ipgm=$( f_index_menu_selection $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ) [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm" @@ -69,6 +71,14 @@ dialog_menu_main() \"\$hline\" \ $menu_list ) + if [ "$USE_XDIALOG" ]; then + # need to bump the width for the buttons + local height menu_height + height="${size%%[$IFS]*}" # first word + menu_height="${size##*[$IFS]}" # last word + size="$height 40 $menu_height" + fi + local dialog_menu dialog_menu=$( eval $DIALOG \ --clear --title \"\$DIALOG_TITLE\" \ @@ -76,6 +86,8 @@ dialog_menu_main() --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ + --help-button \ + ${USE_XDIALOG:+--help \"\"} \ --menu \"\" $size $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) @@ -116,7 +128,13 @@ while :; do mtag=$( f_dialog_menutag ) f_dprintf "retval=$retval mtag=[$mtag]" - [ $retval -eq 0 ] || f_die + if [ $retval -eq 2 ]; then + # The Help button was pressed + f_show_help "$USERMGMT_HELPFILE" + continue + elif [ $retval -ne $SUCCESS ]; then + f_die + fi case "$mtag" in X) # Exit -- cgit v1.3 From e8c6172f10cf6a0343bde47e79bb5e03cdd38c91 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Fri, 26 Oct 2012 20:25:05 +0000 Subject: While 'make universe' passed this didn't work with clang. This reverts r242120 Submitted by: Jan Beich Approved by: cperciva (implicit) --- usr.sbin/cpucontrol/Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile index 759e13d13b113..2d51429ec4c70 100644 --- a/usr.sbin/cpucontrol/Makefile +++ b/usr.sbin/cpucontrol/Makefile @@ -4,4 +4,6 @@ PROG= cpucontrol MAN= cpucontrol.8 SRCS= cpucontrol.c intel.c amd.c via.c +NO_WCAST_ALIGN= + .include -- cgit v1.3 From c0d1bdc0b42acf4aa10152799d8993ee621022d3 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Sat, 27 Oct 2012 19:56:57 +0000 Subject: Fix bug introduced by r241902 (MANIFEST uses TAB delimiter). PR: bin/173140 Approved by: adrian (co-mentor) --- usr.sbin/bsdinstall/scripts/auto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/bsdinstall/scripts/auto b/usr.sbin/bsdinstall/scripts/auto index 356a2152748c3..fe4e0412bb0a6 100755 --- a/usr.sbin/bsdinstall/scripts/auto +++ b/usr.sbin/bsdinstall/scripts/auto @@ -53,7 +53,7 @@ bsdinstall hostname || error export DISTRIBUTIONS="base.txz kernel.txz" if [ -f $BSDINSTALL_DISTDIR/MANIFEST ]; then - DISTMENU=`awk '!/^(kernel|base)/{print $4,$5,$6}' $BSDINSTALL_DISTDIR/MANIFEST` + DISTMENU=`awk -F'\t' '!/^(kernel|base)/{print $4,$5,$6}' $BSDINSTALL_DISTDIR/MANIFEST` exec 3>&1 EXTRA_DISTS=$( eval dialog \ -- cgit v1.3 From 5db4d6779fbc3cbb1197be71856f0a8bccb3a95e Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Mon, 29 Oct 2012 03:31:22 +0000 Subject: Clarify a warning message. --- usr.sbin/kldxref/ef.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'usr.sbin') diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c index b9686d34cdab3..b1b00ccc42500 100644 --- a/usr.sbin/kldxref/ef.c +++ b/usr.sbin/kldxref/ef.c @@ -600,7 +600,8 @@ ef_open(const char *filename, struct elf_file *efile, int verbose) printf("\n"); ef->ef_nsegs = nsegs; if (phdyn == NULL) { - warnx("file isn't dynamically-linked"); + warnx("Skipping %s: not dynamically-linked", + filename); break; } if (ef_read_entry(ef, phdyn->p_offset, -- cgit v1.3 From fb2db03194f64258e3b04f727380db43a99a2067 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Tue, 30 Oct 2012 08:00:53 +0000 Subject: Teach pw(8) about how to use pw/gr API to reduce code duplication MFC after: 2 months --- usr.sbin/pw/Makefile | 4 +- usr.sbin/pw/edgroup.c | 229 ------------------------------------------------- usr.sbin/pw/fileupd.c | 135 ----------------------------- usr.sbin/pw/grupd.c | 120 +++++++++----------------- usr.sbin/pw/pw.8 | 8 +- usr.sbin/pw/pw.c | 4 - usr.sbin/pw/pw.h | 9 -- usr.sbin/pw/pw_group.c | 5 +- usr.sbin/pw/pw_log.c | 2 +- usr.sbin/pw/pw_nis.c | 52 +++++++---- usr.sbin/pw/pw_user.c | 58 +++++++++++-- usr.sbin/pw/pw_vpw.c | 16 +--- usr.sbin/pw/pwupd.c | 114 +++++++++--------------- usr.sbin/pw/pwupd.h | 39 --------- 14 files changed, 175 insertions(+), 620 deletions(-) delete mode 100644 usr.sbin/pw/edgroup.c (limited to 'usr.sbin') diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile index ae0023c6a3232..eae0b879aa5b6 100644 --- a/usr.sbin/pw/Makefile +++ b/usr.sbin/pw/Makefile @@ -3,10 +3,10 @@ PROG= pw MAN= pw.conf.5 pw.8 SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c pw_nis.c pw_vpw.c \ - grupd.c pwupd.c fileupd.c edgroup.c psdate.c \ + grupd.c pwupd.c fileupd.c psdate.c \ bitmap.c cpdir.c rm_r.c -WARNS?= 1 +WARNS?= 2 DPADD= ${LIBCRYPT} ${LIBUTIL} LDADD= -lcrypt -lutil diff --git a/usr.sbin/pw/edgroup.c b/usr.sbin/pw/edgroup.c deleted file mode 100644 index 1cc46b4bdb487..0000000000000 --- a/usr.sbin/pw/edgroup.c +++ /dev/null @@ -1,229 +0,0 @@ -/*- - * Copyright (C) 1996 - * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pwupd.h" - -static int -isingroup(char const * name, char **mem) -{ - int i; - - for (i = 0; mem[i] != NULL; i++) - if (strcmp(name, mem[i]) == 0) - return i; - return -1; -} - -int -editgroups(char *name, char **groups) -{ - int rc = 0; - int infd; - char groupfile[MAXPATHLEN]; - char grouptmp[MAXPATHLEN]; - - strncpy(groupfile, getgrpath(_GROUP), MAXPATHLEN - 5); - groupfile[MAXPATHLEN - 5] = '\0'; - strcpy(grouptmp, groupfile); - strcat(grouptmp, ".new"); - - if ((infd = open(groupfile, O_RDWR | O_CREAT | O_EXLOCK, 0644)) != -1) { - FILE *infp; - - if ((infp = fdopen(infd, "r+")) == NULL) - close(infd); - else { - int outfd; - - if ((outfd = open(grouptmp, O_RDWR | O_CREAT | O_TRUNC, 0644)) != -1) { - FILE *outfp; - - if ((outfp = fdopen(outfd, "w+")) == NULL) - close(outfd); - else { - int linelen = PWBUFSZ; - int outlen = PWBUFSZ; - int memlen = 200; /* Arbitrary */ - char *line = malloc(linelen); - char *outl = malloc(outlen); - char **mems = malloc(memlen * sizeof(char *)); - int namlen = strlen(name); - - if (line == NULL || outl == NULL || mems == NULL) { - mem_abort: - rc = 0; - } else { - while (fgets(line, linelen, infp) != NULL) { - char *p; - int l; - - while ((p = strchr(line, '\n')) == NULL) - { - if (extendline(&line, &linelen, linelen + PWBUFSZ) == -1) { - goto mem_abort; - } - l = strlen(line); - if (fgets(line + l, linelen - l, infp) == NULL) - break; /* No newline terminator on last line */ - } - l = strlen(line) + namlen + 1; - if (extendline(&outl, &outlen, l) == -1) { - goto mem_abort; - } - if (*line == '#') - strcpy(outl, line); - else if (*line == '\n') - *outl = '\0'; - else { - int i, - mno = 0; - char *cp = line; - char const *sep = ":\n"; - struct group grp; - - memset(&grp, 0, sizeof grp); - for (i = 0; (p = strsep(&cp, sep)) != NULL; i++) { - switch (i) { - case 0: /* Group name */ - grp.gr_name = p; - break; - case 1: /* Group password */ - grp.gr_passwd = p; - break; - case 2: /* Group id */ - grp.gr_gid = atoi(p); - break; - case 3: /* Member list */ - cp = p; - sep = ",\n"; - break; - default: /* Individual members */ - if (*p) { - if (extendarray(&mems, &memlen, mno + 2) == -1) { - goto mem_abort; - } - mems[mno++] = p; - } - break; - } - } - if (i < 2) /* Bail out - insufficient fields */ - continue; - - grp.gr_mem = mems; - for (i = mno; i < memlen; i++) - mems[i] = NULL; - - /* - * Delete from group, or add to group? - */ - if (groups == NULL || isingroup(grp.gr_name, groups) == -1) { /* Delete */ - int idx; - - while ((idx = isingroup(name, mems)) != -1) { - for (i = idx; i < (memlen - 1); i++) - mems[i] = mems[i + 1]; - mems[i] = NULL; - --mno; - } - /* - * Special case - deleting user and group may be user's own - */ - if (groups == NULL && mems[0] == NULL && strcmp(name, grp.gr_name) == 0) { - /* - * First, make _sure_ we don't have other members - */ - struct passwd *pwd; - - SETPWENT(); - while ((pwd = GETPWENT()) != NULL && (gid_t)pwd->pw_gid != (gid_t)grp.gr_gid); - ENDPWENT(); - if (pwd == NULL) /* No members at all */ - continue; /* Drop the group */ - } - } else if (isingroup(name, mems) == -1) { - if (extendarray(&mems, &memlen, mno + 2) == -1) { - goto mem_abort; - } - grp.gr_mem = mems; /* May have realloced() */ - mems[mno++] = name; - mems[mno ] = NULL; - } - fmtgrentry(&outl, &outlen, &grp, PWF_GROUP); - } - fputs(outl, outfp); - } - if (fflush(outfp) != EOF) { - rc = 1; - - /* - * Copy data back into the original file and truncate - */ - rewind(infp); - rewind(outfp); - while (fgets(outl, outlen, outfp) != NULL) - fputs(outl, infp); - - /* - * This is a gross hack, but we may have corrupted the - * original file. - */ - if (fflush(infp) == EOF || ferror(infp)) - rc = rename(grouptmp, groupfile) == 0; - else - ftruncate(infd, ftell(infp)); - } - } - free(mems); - free(outl); - free(line); - fclose(outfp); - } - remove(grouptmp); - } - fclose(infp); - } - } - return rc; -} diff --git a/usr.sbin/pw/fileupd.c b/usr.sbin/pw/fileupd.c index b88f4fa8539f5..7df4bb190cbb0 100644 --- a/usr.sbin/pw/fileupd.c +++ b/usr.sbin/pw/fileupd.c @@ -66,138 +66,3 @@ extendarray(char ***buf, int * buflen, int needed) } return *buflen; } - - -int -fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode) -{ - int rc = 0; - - if (pfxlen <= 1) - rc = EINVAL; - else { - int infd = open(filename, O_RDWR | O_CREAT | O_EXLOCK, fmode); - - if (infd == -1) - rc = errno; - else { - FILE *infp = fdopen(infd, "r+"); - - if (infp == NULL) { - rc = errno; /* Assumes fopen(3) sets errno from open(2) */ - close(infd); - } else { - int outfd; - char file[MAXPATHLEN]; - - strcpy(file, filename); - strcat(file, ".new"); - outfd = open(file, O_RDWR | O_CREAT | O_TRUNC, fmode); - if (outfd == -1) - rc = errno; - else { - FILE *outfp = fdopen(outfd, "w+"); - - if (outfp == NULL) { - rc = errno; - close(outfd); - } else { - int updated = UPD_CREATE; - int linesize = PWBUFSZ; - char *line = malloc(linesize); - - nextline: - while (fgets(line, linesize, infp) != NULL) { - char *p = strchr(line, '\n'); - - while ((p = strchr(line, '\n')) == NULL) { - int l; - if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) { - int ch; - fputs(line, outfp); - while ((ch = fgetc(infp)) != EOF) { - fputc(ch, outfp); - if (ch == '\n') - break; - } - goto nextline; - } - l = strlen(line); - if (fgets(line + l, linesize - l, infp) == NULL) - break; - } - if (*line != '#' && *line != '\n') { - if (!updated && strncmp(line, prefix, pfxlen) == 0) { - updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE; - - /* - * Only actually write changes if updating - */ - if (updmode == UPD_REPLACE) - strcpy(line, newline); - else if (updmode == UPD_DELETE) - continue; - } - } - fputs(line, outfp); - } - - /* - * Now, we need to decide what to do: If we are in - * update mode, and no record was updated, then error If - * we are in insert mode, and record already exists, - * then error - */ - if (updmode != updated) - /* -1 return means: - * update,delete=no user entry - * create=entry exists - */ - rc = -1; - else { - - /* - * If adding a new record, append it to the end - */ - if (updmode == UPD_CREATE) - fputs(newline, outfp); - - /* - * Flush the file and check for the result - */ - if (fflush(outfp) == EOF) - rc = errno; /* Failed to update */ - else { - /* - * Copy data back into the - * original file and truncate - */ - rewind(infp); - rewind(outfp); - while (fgets(line, linesize, outfp) != NULL) - fputs(line, infp); - - /* - * If there was a problem with copying - * we will just rename 'file.new' - * to 'file'. - * This is a gross hack, but we may have - * corrupted the original file - */ - if (fflush(infp) == EOF || ferror(infp)) - rename(file, filename); - else - ftruncate(infd, ftell(infp)); - } - } - free(line); - fclose(outfp); - } - remove(file); - } - fclose(infp); - } - } - } - return rc; -} diff --git a/usr.sbin/pw/grupd.c b/usr.sbin/pw/grupd.c index edff76d1285c8..e9f6b5e4a5b81 100644 --- a/usr.sbin/pw/grupd.c +++ b/usr.sbin/pw/grupd.c @@ -29,6 +29,9 @@ static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ +#include +#include +#include #include #include #include @@ -65,107 +68,62 @@ getgrpath(const char * file) return pathbuf; } -int -grdb(char *arg,...) -{ - /* - * This is a stub for now, but maybe eventually be functional - * if ever an indexed version of /etc/groups is implemented. - */ - arg=arg; - return 0; -} - -int -fmtgrentry(char **buf, int * buflen, struct group * grp, int type) +static int +gr_update(struct group * grp, char const * group) { - int i, l; - - /* - * Since a group line is of arbitrary length, - * we need to calculate up-front just how long - * it will need to be... - */ - /* groupname : password : gid : */ - l = strlen(grp->gr_name) + 1 + strlen(grp->gr_passwd) + 1 + 5 + 1; - /* group members + comma separator */ - for (i = 0; grp->gr_mem[i] != NULL; i++) { - l += strlen(grp->gr_mem[i]) + 1; - } - l += 2; /* For newline & NUL */ - if (extendline(buf, buflen, l) == -1) - l = -1; - else{ - /* - * Now we can safely format - */ - if (type == PWF_STANDARD) - l = sprintf(*buf, "%s:*:%ld:", grp->gr_name, (long) grp->gr_gid); - else - l = sprintf(*buf, "%s:%s:%ld:", grp->gr_name, grp->gr_passwd, (long) grp->gr_gid); - - /* - * List members - */ - for (i = 0; grp->gr_mem[i] != NULL; i++) { - l += sprintf(*buf + l, "%s%s", i ? "," : "", grp->gr_mem[i]); - } - - (*buf)[l++] = '\n'; - (*buf)[l] = '\0'; - } - return l; -} + int pfd, tfd; + struct group *gr = NULL; + struct group *old_gr = NULL; + if (grp != NULL) + gr = gr_dup(grp); -int -fmtgrent(char **buf, int * buflen, struct group * grp) -{ - return fmtgrentry(buf, buflen, grp, PWF_STANDARD); -} + if (group != NULL) + old_gr = GETGRNAM(group); + if (gr_init(grpath, NULL)) + err(1, "gr_init()"); -static int -gr_update(struct group * grp, char const * group, int mode) -{ - int l; - char pfx[64]; - int grbuflen = 0; - char *grbuf = NULL; - - ENDGRENT(); - l = snprintf(pfx, sizeof pfx, "%s:", group); - - /* - * Update the group file - */ - if (grp != NULL && fmtgrentry(&grbuf, &grbuflen, grp, PWF_PASSWD) == -1) - l = -1; - else { - l = fileupdate(getgrpath(_GROUP), 0644, grbuf, pfx, l, mode); - if (l == 0) - l = grdb(NULL); + if ((pfd = gr_lock()) == -1) { + gr_fini(); + err(1, "gr_lock()"); + } + if ((tfd = gr_tmp(-1)) == -1) { + gr_fini(); + err(1, "gr_tmp()"); } - if (grbuf != NULL) - free(grbuf); - return l; + if (gr_copy(pfd, tfd, gr, old_gr) == -1) { + gr_fini(); + err(1, "gr_copy()"); + } + if (gr_mkdb() == -1) { + gr_fini(); + err(1, "gr_mkdb()"); + } + free(gr); + gr_fini(); + return 0; } int addgrent(struct group * grp) { - return gr_update(grp, grp->gr_name, UPD_CREATE); + return gr_update(grp, NULL); } int chggrent(char const * login, struct group * grp) { - return gr_update(grp, login, UPD_REPLACE); + return gr_update(grp, login); } int delgrent(struct group * grp) { - return gr_update(NULL, grp->gr_name, UPD_DELETE); + char group[MAXLOGNAME]; + + strlcpy(group, grp->gr_name, MAXLOGNAME); + + return gr_update(NULL, group); } diff --git a/usr.sbin/pw/pw.8 b/usr.sbin/pw/pw.8 index 8b211077dc1fb..076f2eb562adb 100644 --- a/usr.sbin/pw/pw.8 +++ b/usr.sbin/pw/pw.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 21, 2011 +.Dd October 29, 2012 .Dt PW 8 .Os .Sh NAME @@ -904,12 +904,6 @@ A Version 7 format password file The user capabilities database .It Pa /etc/group The group database -.It Pa /etc/master.passwd.new -Temporary copy of the master password file -.It Pa /etc/passwd.new -Temporary copy of the Version 7 password file -.It Pa /etc/group.new -Temporary copy of the group file .It Pa /etc/pw.conf Pw default options file .It Pa /var/log/userlog diff --git a/usr.sbin/pw/pw.c b/usr.sbin/pw/pw.c index e9d93632888f6..b0ac7285926d2 100644 --- a/usr.sbin/pw/pw.c +++ b/usr.sbin/pw/pw.c @@ -62,13 +62,11 @@ struct pwf PWF = getpwent, getpwuid, getpwnam, - pwdb, setgrent, endgrent, getgrent, getgrgid, getgrnam, - grdb }; struct pwf VPWF = @@ -79,13 +77,11 @@ struct pwf VPWF = vgetpwent, vgetpwuid, vgetpwnam, - vpwdb, vsetgrent, vendgrent, vgetgrent, vgetgrgid, vgetgrnam, - vgrdb }; static struct cargs arglist; diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h index 6e521d10a482f..1ff69a695fab9 100644 --- a/usr.sbin/pw/pw.h +++ b/usr.sbin/pw/pw.h @@ -109,19 +109,10 @@ int pw_user(struct userconf * cnf, int mode, struct cargs * _args); int pw_group(struct userconf * cnf, int mode, struct cargs * _args); char *pw_checkname(u_char *name, int gecos); -int addpwent(struct passwd * pwd); -int delpwent(struct passwd * pwd); -int chgpwent(char const * login, struct passwd * pwd); -int fmtpwent(char *buf, struct passwd * pwd); - int addnispwent(const char *path, struct passwd *pwd); int delnispwent(const char *path, const char *login); int chgnispwent(const char *path, const char *login, struct passwd *pwd); -int addgrent(struct group * grp); -int delgrent(struct group * grp); -int chggrent(char const * login, struct group * grp); - int boolean_val(char const * str, int dflt); char const *boolean_str(int val); char *newstr(char const * p); diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c index a8f182cae259e..f4f21165c3703 100644 --- a/usr.sbin/pw/pw_group.c +++ b/usr.sbin/pw/pw_group.c @@ -34,6 +34,8 @@ static const char rcsid[] = #include #include #include +#include +#include #include "pw.h" #include "bitmap.h" @@ -403,10 +405,9 @@ static int print_group(struct group * grp, int pretty) { if (!pretty) { - int buflen = 0; char *buf = NULL; - fmtgrent(&buf, &buflen, grp); + buf = gr_make(grp); fputs(buf, stdout); free(buf); } else { diff --git a/usr.sbin/pw/pw_log.c b/usr.sbin/pw/pw_log.c index fc85828b5bbfe..f16274f2be173 100644 --- a/usr.sbin/pw/pw_log.c +++ b/usr.sbin/pw/pw_log.c @@ -51,7 +51,7 @@ pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...) time_t now = time(NULL); struct tm *t = localtime(&now); char nfmt[256]; - char *name; + const char *name; if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL) name = "unknown"; diff --git a/usr.sbin/pw/pw_nis.c b/usr.sbin/pw/pw_nis.c index 74a3ed09a7a97..af5901aba6780 100644 --- a/usr.sbin/pw/pw_nis.c +++ b/usr.sbin/pw/pw_nis.c @@ -33,40 +33,62 @@ static const char rcsid[] = #include #include #include +#include +#include +#include #include "pw.h" static int -pw_nisupdate(const char * path, struct passwd * pwd, char const * user, int mode) +pw_nisupdate(const char * path, struct passwd * pwd, char const * user) { - char pfx[32]; - char pwbuf[PWBUFSZ]; - int l = sprintf(pfx, "%s:", user); + int pfd, tfd; + struct passwd *pw = NULL; + struct passwd *old_pw = NULL; - /* - * Update the passwd file first - */ - if (pwd == NULL) - *pwbuf = '\0'; - else - fmtpwentry(pwbuf, pwd, PWF_MASTER); - return fileupdate(path, 0600, pwbuf, pfx, l, mode) != 0; + if (pwd != NULL) + pw = pw_dup(pwd); + + if (user != NULL) + old_pw = GETPWNAM(user); + + if (pw_init(NULL, path)) + err(1,"pw_init()"); + if ((pfd = pw_lock()) == -1) { + pw_fini(); + err(1, "pw_lock()"); + } + if ((tfd = pw_tmp(-1)) == -1) { + pw_fini(); + err(1, "pw_tmp()"); + } + if (pw_copy(pfd, tfd, pw, old_pw) == -1) { + pw_fini(); + err(1, "pw_copy()"); + } + if (rename(pw_tempname(), path) == -1) + err(1, "rename()"); + + free(pw); + pw_fini(); + + return (0); } int addnispwent(const char *path, struct passwd * pwd) { - return pw_nisupdate(path, pwd, pwd->pw_name, UPD_CREATE); + return pw_nisupdate(path, pwd, NULL); } int chgnispwent(const char *path, char const * login, struct passwd * pwd) { - return pw_nisupdate(path, pwd, login, UPD_REPLACE); + return pw_nisupdate(path, pwd, login); } int delnispwent(const char *path, const char *login) { - return pw_nisupdate(path, NULL, login, UPD_DELETE); + return pw_nisupdate(path, NULL, login); } diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c index 1b72cbdb70eef..abf1c354bc03d 100644 --- a/usr.sbin/pw/pw_user.c +++ b/usr.sbin/pw/pw_user.c @@ -42,6 +42,9 @@ static const char rcsid[] = #include #include #include +#include +#include +#include #include "pw.h" #include "bitmap.h" @@ -292,7 +295,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if (mode == M_PRINT && getarg(args, 'a')) { int pretty = getarg(args, 'P') != NULL; int v7 = getarg(args, '7') != NULL; - SETPWENT(); while ((pwd = GETPWENT()) != NULL) print_user(pwd, pretty, v7); @@ -422,7 +424,24 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) /* non-fatal */ } - editgroups(a_name->val, NULL); + grp = GETGRNAM(a_name->val); + if (*grp->gr_mem == NULL) + delgrent(GETGRNAM(a_name->val)); + SETGRENT(); + while ((grp = GETGRENT()) != NULL) { + int i; + char group[MAXLOGNAME]; + for (i = 0; grp->gr_mem[i] != NULL; i++) { + if (!strcmp(grp->gr_mem[i], a_name->val)) { + while (grp->gr_mem[i] != NULL) { + grp->gr_mem[i] = grp->gr_mem[i+1]; + } + strlcpy(group, grp->gr_name, MAXLOGNAME); + chggrent(group, grp); + } + } + } + ENDGRENT(); pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid); @@ -725,8 +744,29 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * Ok, user is created or changed - now edit group file */ - if (mode == M_ADD || getarg(args, 'G') != NULL) - editgroups(pwd->pw_name, cnf->groups); + if (mode == M_ADD || getarg(args, 'G') != NULL) { + int i, j; + for (i = 0; cnf->groups[i] != NULL; i++) { + grp = GETGRNAM(cnf->groups[i]); + for (j = 0; grp->gr_mem[j] != NULL; j++) { + if (!strcmp(grp->gr_mem[j], pwd->pw_name)) + break; + } + if (grp->gr_mem[j] != NULL) /* user already member of group */ + continue; + + if (j == 0) + grp->gr_mem = NULL; + + grp->gr_mem = reallocf(grp->gr_mem, sizeof(*grp->gr_mem) * + (j + 2)); + + grp->gr_mem[j] = pwd->pw_name; + grp->gr_mem[j+1] = NULL; + chggrent(cnf->groups[i], grp); + } + } + /* go get a current version of pwd */ pwd = GETPWNAM(a_name->val); @@ -1090,10 +1130,14 @@ static int print_user(struct passwd * pwd, int pretty, int v7) { if (!pretty) { - char buf[_UC_MAXLINE]; + char *buf; + + if (!v7) + pwd->pw_passwd = (pwd->pw_passwd == NULL) ? "" : "*"; - fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD); - fputs(buf, stdout); + buf = v7 ? pw_make_v7(pwd) : pw_make(pwd); + printf("%s\n", buf); + free(buf); } else { int j; char *p; diff --git a/usr.sbin/pw/pw_vpw.c b/usr.sbin/pw/pw_vpw.c index 473cbb6c0c646..674b64f28e51f 100644 --- a/usr.sbin/pw/pw_vpw.c +++ b/usr.sbin/pw/pw_vpw.c @@ -170,13 +170,6 @@ vgetpwnam(const char * nam) return vnextpwent(nam, -1, 1); } -int vpwdb(char *arg, ...) -{ - arg=arg; - return 0; -} - - static FILE * grp_fp = NULL; @@ -221,7 +214,7 @@ vnextgrent(char const * nam, gid_t gid, int doclose) int i, quickout = 0; int mno = 0; char * q, * p; - char * sep = ":\n"; + const char * sep = ":\n"; if ((p = strchr(grtmp, '\n')) == NULL) { int l; @@ -307,10 +300,3 @@ vgetgrnam(const char * nam) return vnextgrent(nam, -1, 1); } -int -vgrdb(char *arg, ...) -{ - arg=arg; - return 0; -} - diff --git a/usr.sbin/pw/pwupd.c b/usr.sbin/pw/pwupd.c index 1e20cc4765a6d..4ab0f01967772 100644 --- a/usr.sbin/pw/pwupd.c +++ b/usr.sbin/pw/pwupd.c @@ -34,7 +34,10 @@ static const char rcsid[] = #include #include #include +#include +#include #include +#include #include #include #include @@ -71,7 +74,7 @@ getpwpath(char const * file) return pathbuf; } -int +static int pwdb(char *arg,...) { int i = 0; @@ -106,44 +109,11 @@ pwdb(char *arg,...) return i; } -int -fmtpwentry(char *buf, struct passwd * pwd, int type) -{ - int l; - char *pw; - - pw = (type == PWF_MASTER) ? - ((pwd->pw_passwd == NULL) ? "" : pwd->pw_passwd) : "*"; - - if (type == PWF_PASSWD) - l = sprintf(buf, "%s:*:%ld:%ld:%s:%s:%s\n", - pwd->pw_name, (long) pwd->pw_uid, (long) pwd->pw_gid, - pwd->pw_gecos ? pwd->pw_gecos : "User &", - pwd->pw_dir, pwd->pw_shell); - else - l = sprintf(buf, "%s:%s:%ld:%ld:%s:%lu:%lu:%s:%s:%s\n", - pwd->pw_name, pw, (long) pwd->pw_uid, (long) pwd->pw_gid, - pwd->pw_class ? pwd->pw_class : "", - (unsigned long) pwd->pw_change, - (unsigned long) pwd->pw_expire, - pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); - return l; -} - - -int -fmtpwent(char *buf, struct passwd * pwd) -{ - return fmtpwentry(buf, pwd, PWF_STANDARD); -} - static int -pw_update(struct passwd * pwd, char const * user, int mode) +pw_update(struct passwd * pwd, char const * user) { int rc = 0; - ENDPWENT(); - /* * First, let's check the see if the database is alright * Note: -C is only available in FreeBSD 2.2 and above @@ -154,61 +124,57 @@ pw_update(struct passwd * pwd, char const * user, int mode) #else { /* No -C */ #endif - char pfx[PWBUFSZ]; - char pwbuf[PWBUFSZ]; - int l = snprintf(pfx, PWBUFSZ, "%s:", user); -#ifdef HAVE_PWDB_U - int isrename = pwd!=NULL && strcmp(user, pwd->pw_name); -#endif + int pfd, tfd; + struct passwd *pw = NULL; + struct passwd *old_pw = NULL; - /* - * Update the passwd file first - */ - if (pwd == NULL) - *pwbuf = '\0'; - else - fmtpwentry(pwbuf, pwd, PWF_PASSWD); - - if (l < 0) - l = 0; - rc = fileupdate(getpwpath(_PASSWD), 0644, pwbuf, pfx, l, mode); - if (rc == 0) { - - /* - * Then the master.passwd file - */ - if (pwd != NULL) - fmtpwentry(pwbuf, pwd, PWF_MASTER); - rc = fileupdate(getpwpath(_MASTERPASSWD), 0600, pwbuf, pfx, l, mode); - if (rc == 0) { -#ifdef HAVE_PWDB_U - if (mode == UPD_DELETE || isrename) -#endif - rc = pwdb(NULL); -#ifdef HAVE_PWDB_U - else - rc = pwdb("-u", user, (char *)NULL); -#endif - } + if (pwd != NULL) + pw = pw_dup(pwd); + + if (user != NULL) + old_pw = GETPWNAM(user); + + if (pw_init(pwpath, NULL)) + err(1, "pw_init()"); + if ((pfd = pw_lock()) == -1) { + pw_fini(); + err(1, "pw_lock()"); + } + if ((tfd = pw_tmp(-1)) == -1) { + pw_fini(); + err(1, "pw_tmp()"); + } + if (pw_copy(pfd, tfd, pw, old_pw) == -1) { + pw_fini(); + err(1, "pw_copy()"); } + if (pw_mkdb(user) == -1) { + pw_fini(); + err(1, "pw_mkdb()"); + } + free(pw); + pw_fini(); } - return rc; + return 0; } int addpwent(struct passwd * pwd) { - return pw_update(pwd, pwd->pw_name, UPD_CREATE); + return pw_update(pwd, NULL); } int chgpwent(char const * login, struct passwd * pwd) { - return pw_update(pwd, login, UPD_REPLACE); + return pw_update(pwd, login); } int delpwent(struct passwd * pwd) { - return pw_update(NULL, pwd->pw_name, UPD_DELETE); + char login[MAXLOGNAME]; + + strlcpy(login, pwd->pw_name, MAXLOGNAME); + return pw_update(NULL, login); } diff --git a/usr.sbin/pw/pwupd.h b/usr.sbin/pw/pwupd.h index 728906500f417..200ffeec751c0 100644 --- a/usr.sbin/pw/pwupd.h +++ b/usr.sbin/pw/pwupd.h @@ -41,25 +41,6 @@ #define RET_SETGRENT void #endif -enum updtype -{ - UPD_DELETE = -1, - UPD_CREATE = 0, - UPD_REPLACE = 1 -}; - -__BEGIN_DECLS -int fileupdate(char const * fname, mode_t fm, char const * nline, char const * pfx, int pfxlen, int updmode); -__END_DECLS - -enum pwdfmttype -{ - PWF_STANDARD, /* MASTER format but with '*' as password */ - PWF_PASSWD, /* V7 format */ - PWF_GROUP = PWF_PASSWD, - PWF_MASTER /* MASTER format with password */ -}; - struct pwf { int _altdir; @@ -68,13 +49,11 @@ struct pwf struct passwd * (*_getpwent)(void); struct passwd * (*_getpwuid)(uid_t uid); struct passwd * (*_getpwnam)(const char * nam); - int (*_pwdb)(char *arg, ...); RET_SETGRENT (*_setgrent)(void); void (*_endgrent)(void); struct group * (*_getgrent)(void); struct group * (*_getgrgid)(gid_t gid); struct group * (*_getgrnam)(const char * nam); - int (*_grdb)(char *arg, ...); }; extern struct pwf PWF; @@ -85,14 +64,12 @@ extern struct pwf VPWF; #define GETPWENT() PWF._getpwent() #define GETPWUID(uid) PWF._getpwuid(uid) #define GETPWNAM(nam) PWF._getpwnam(nam) -#define PWDB(args) PWF._pwdb(args) #define SETGRENT() PWF._setgrent() #define ENDGRENT() PWF._endgrent() #define GETGRENT() PWF._getgrent() #define GETGRGID(gid) PWF._getgrgid(gid) #define GETGRNAM(nam) PWF._getgrnam(nam) -#define GRDB(args) PWF._grdb(args) #define PWALTDIR() PWF._altdir #ifndef _PATH_PWD @@ -101,51 +78,35 @@ extern struct pwf VPWF; #ifndef _GROUP #define _GROUP "group" #endif -#ifndef _PASSWD -#define _PASSWD "passwd" -#endif #ifndef _MASTERPASSWD #define _MASTERPASSWD "master.passwd" #endif -#ifndef _GROUP -#define _GROUP "group" -#endif __BEGIN_DECLS int addpwent(struct passwd * pwd); int delpwent(struct passwd * pwd); int chgpwent(char const * login, struct passwd * pwd); -int fmtpwent(char *buf, struct passwd * pwd); -int fmtpwentry(char *buf, struct passwd * pwd, int type); int setpwdir(const char * dir); char * getpwpath(char const * file); -int pwdb(char *arg, ...); int addgrent(struct group * grp); int delgrent(struct group * grp); int chggrent(char const * name, struct group * grp); -int fmtgrent(char **buf, int * buflen, struct group * grp); -int fmtgrentry(char **buf, int * buflen, struct group * grp, int type); int editgroups(char *name, char **groups); int setgrdir(const char * dir); char * getgrpath(const char *file); -int grdb(char *arg, ...); void vsetpwent(void); void vendpwent(void); struct passwd * vgetpwent(void); struct passwd * vgetpwuid(uid_t uid); struct passwd * vgetpwnam(const char * nam); -struct passwd * vgetpwent(void); -int vpwdb(char *arg, ...); struct group * vgetgrent(void); struct group * vgetgrgid(gid_t gid); struct group * vgetgrnam(const char * nam); -struct group * vgetgrent(void); -int vgrdb(char *arg, ...); RET_SETGRENT vsetgrent(void); void vendgrent(void); -- cgit v1.3 From 31c0b21fedca1256da335dfb0cfcc4ced89e3cc7 Mon Sep 17 00:00:00 2001 From: "Simon J. Gerraty" Date: Sat, 3 Nov 2012 00:10:30 +0000 Subject: If no contents keyword is specified, the default for files is the named file. Approved by: marcel (mentor) --- usr.sbin/makefs/mtree.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'usr.sbin') diff --git a/usr.sbin/makefs/mtree.c b/usr.sbin/makefs/mtree.c index 5d88ad6b04f87..836e7665635de 100644 --- a/usr.sbin/makefs/mtree.c +++ b/usr.sbin/makefs/mtree.c @@ -135,6 +135,47 @@ mtree_warning(const char *fmt, ...) fputc('\n', stderr); } +#ifndef MAKEFS_MAX_TREE_DEPTH +# define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2) +#endif + +/* construct path to node->name */ +static char * +mtree_file_path(fsnode *node) +{ + fsnode *pnode; + struct sbuf *sb; + char *res, *rp[MAKEFS_MAX_TREE_DEPTH]; + int depth; + + depth = 0; + rp[depth] = node->name; + for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH; + pnode = pnode->parent) { + if (strcmp(pnode->name, ".") == 0) + break; + rp[++depth] = pnode->name; + } + + sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + if (sb == NULL) { + errno = ENOMEM; + return (NULL); + } + while (depth > 0) { + sbuf_cat(sb, rp[depth--]); + sbuf_putc(sb, '/'); + } + sbuf_cat(sb, rp[depth]); + sbuf_finish(sb); + res = strdup(sbuf_data(sb)); + sbuf_delete(sb); + if (res == NULL) + errno = ENOMEM; + return res; + +} + /* mtree_resolve() sets errno to indicate why NULL was returned. */ static char * mtree_resolve(const char *spec, int *istemp) @@ -706,6 +747,12 @@ read_mtree_keywords(FILE *fp, fsnode *node) return (0); } type = S_IFREG; + } else if (node->type != 0) { + type = node->type; + if (type == S_IFREG) { + /* the named path is the default contents */ + node->contents = mtree_file_path(node); + } } else type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR; -- cgit v1.3 From 652c42600b63aa68fb6de4dbfc3d49ec9610b01b Mon Sep 17 00:00:00 2001 From: Xin LI Date: Sat, 3 Nov 2012 18:38:28 +0000 Subject: Replace log(3) with flsll(3) for watchdogd(8) and drop libm dependency. MFC after: 2 weeks --- usr.sbin/watchdogd/Makefile | 4 ++-- usr.sbin/watchdogd/watchdogd.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/watchdogd/Makefile b/usr.sbin/watchdogd/Makefile index be69d2bf5a847..5df7946cb1f78 100644 --- a/usr.sbin/watchdogd/Makefile +++ b/usr.sbin/watchdogd/Makefile @@ -4,8 +4,8 @@ PROG= watchdogd LINKS= ${BINDIR}/watchdogd ${BINDIR}/watchdog MAN= watchdogd.8 watchdog.8 -LDADD= -lm -lutil -DPADD= ${LIBM} ${LIBUTIL} +LDADD= -lutil +DPADD= ${LIBUTIL} .include diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c index cce84e1d9a1ef..8194f963a56ac 100644 --- a/usr.sbin/watchdogd/watchdogd.c +++ b/usr.sbin/watchdogd/watchdogd.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -280,7 +281,7 @@ parseargs(int argc, char *argv[]) if (a == 0) timeout = WD_TO_NEVER; else - timeout = 1.0 + log(a * 1e9) / log(2.0); + timeout = flsll(a * 1e9); if (debugging) printf("Timeout is 2^%d nanoseconds\n", timeout); -- cgit v1.3