diff options
| author | Max Laier <mlaier@FreeBSD.org> | 2004-06-16 23:26:00 +0000 | 
|---|---|---|
| committer | Max Laier <mlaier@FreeBSD.org> | 2004-06-16 23:26:00 +0000 | 
| commit | abff3868339c96bb3ce524f66fe7758d3870800a (patch) | |
| tree | 91591a3c3277d8525d725da18322a0f9ba6078a2 | |
| parent | 3178c893b859e7369b8cb4fde370daf272c62eb2 (diff) | |
27 files changed, 3368 insertions, 1036 deletions
| diff --git a/contrib/pf/authpf/authpf.8 b/contrib/pf/authpf/authpf.8 index 141aecf7d6a8..b6977da2fce9 100644 --- a/contrib/pf/authpf/authpf.8 +++ b/contrib/pf/authpf/authpf.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: authpf.8,v 1.30 2003/08/17 23:24:47 henning Exp $ +.\" $OpenBSD: authpf.8,v 1.31 2003/12/10 04:10:37 beck Exp $  .\"  .\" Copyright (c) 2002 Bob Beck (beck@openbsd.org>.  All rights reserved.  .\" @@ -84,9 +84,9 @@ shared by all  processes.  By default, the  .Pa anchor -name "authpf" is used, and the ruleset names equal the PIDs of the +name "authpf" is used, and the ruleset names equal the username and PID of the  .Nm -processes. +processes as "username(pid)".  The following rules need to be added to the main ruleset  .Pa /etc/pf.conf  in order to cause evaluation of any @@ -263,7 +263,8 @@ by creating an appropriate  .Pa /etc/authpf/authpf.conf  file.  .Sh EXAMPLES -\fBControl Files\fP - To illustrate the user-specific access control +.Sy Control Files +\- To illustrate the user-specific access control  mechanisms, let us consider a typical user named bob.  Normally, as long as bob can authenticate himself, the  .Nm @@ -298,7 +299,8 @@ file.  Though bob is listed in the allow file, he is prevented from using  this gateway due to the existence of a ban file.  .Pp -\fBDistributed Authentication\fP - It is often desirable to interface with a +.Sy Distributed Authentication +\- It is often desirable to interface with a  distributed password system rather than forcing the sysadmins to keep a large  number of local password files in sync.  The @@ -332,7 +334,8 @@ Using a default password file, all users will get  as their shell except for root who will get  .Pa /bin/csh .  .Pp -\fBSSH Configuration\fP - As stated earlier, +.Sy SSH Configuration +\- As stated earlier,  .Xr sshd 8  must be properly configured to detect and defeat network attacks.  To that end, the following options should be added to @@ -346,7 +349,8 @@ ClientAliveCountMax 3  This ensures that unresponsive or spoofed sessions are terminated within a  minute, since a hijacker should not be able to spoof ssh keepalive messages.  .Pp -\fBBanners\fP - Once authenticated, the user is shown the contents of +.Sy Banners +\- Once authenticated, the user is shown the contents of  .Pa /etc/authpf/authpf.message .  This message may be a screen-full of the appropriate use policy, the contents  of @@ -366,7 +370,8 @@ problem so we can fix it, please phone 1-900-314-1597 or send  an email to remove@bulkmailerz.net.  .Ed  .Pp -\fBPacket Filter Rules\fP - In areas where this gateway is used to protect a +.Sy Packet Filter Rules +\- In areas where this gateway is used to protect a  wireless network (a hub with several hundred ports), the default rule set as  well as the per-user rules should probably allow very few things beyond  encrypted protocols like @@ -378,15 +383,14 @@ On a securely switched network, with plug-in jacks for visitors who are  given authentication accounts, you might want to allow out everything.  In this context, a secure switch is one that tries to prevent address table  overflow attacks. -The examples below assume a switched wired net.  .Pp  Example  .Pa /etc/pf.conf :  .Bd -literal  # by default we allow internal clients to talk to us using  # ssh and use us as a dns server. -internal_if=\&"fxp1\&" -gateway_addr=\&"10.0.1.1\&" +internal_if="fxp1" +gateway_addr="10.0.1.1"  nat-anchor authpf  rdr-anchor authpf  binat-anchor authpf @@ -398,26 +402,28 @@ pass in quick on $internal_if proto udp from any to $gateway_addr \e  anchor authpf  .Ed  .Pp -Example -.Pa /etc/authpf/authpf.rules : +.Sy For a switched, wired net +\- This example +.Pa /etc/authpf/authpf.rules +makes no real restrictions; it turns the IP address on and off, logging +TCP connections.  .Bd -literal -# no real restrictions here, basically turn the network jack off or on. - -external_if = \&"xl0\&" -internal_if = \&"fxp0\&" +external_if = "xl0" +internal_if = "fxp0"  pass in log quick on $internal_if proto tcp from $user_ip to any \e        keep state  pass in quick on $internal_if from $user_ip to any  .Ed  .Pp -Another example +.Sy For a wireless or shared net +\- This example  .Pa /etc/authpf/authpf.rules -for an insecure network (such as a public wireless network) where +could be used for an insecure network (such as a public wireless network) where  we might need to be a bit more restrictive.  .Bd -literal -internal_if=\&"fxp1\&" -ipsec_gw=\&"10.2.3.4\&" +internal_if="fxp1" +ipsec_gw="10.2.3.4"  # rdr ftp for proxying by ftp-proxy(8)  rdr on $internal_if proto tcp from $user_ip to any port 21 \e @@ -433,6 +439,32 @@ pass in quick proto udp from $user_ip to $ipsec_gw port = isakmp \e        keep state  pass in quick proto esp from $user_ip to $ipsec_gw  .Ed +.Pp +.Sy Dealing with NAT +\- The following +.Pa /etc/authpf/authpf.rules +shows how to deal with NAT, using tags: +.Bd -literal +ext_if = "fxp1" +ext_addr = 129.128.11.10 +int_if = "fxp0" +# nat and tag connections... +nat on $ext_if from $user_ip to any tag $user_ip -> $ext_addr +pass in quick on $int_if from $user_ip to any +pass out log quick on $ext_if tagged $user_ip keep state +.Ed +.Pp +With the above rules added by +.Nm , +outbound connections corresponding to each users NAT'ed connections +will be logged as in the example below, where the user may be identified +from the ruleset name. +.Bd -literal +# tcpdump -n -e -ttt -i pflog0 +Oct 31 19:42:30.296553 rule 0.bbeck(20267).1/0(match): pass out on fxp1: \e +129.128.11.10.60539 > 198.137.240.92.22: S 2131494121:2131494121(0) win \e +16384 <mss 1460,nop,nop,sackOK> (DF) +.Ed  .Sh FILES  .Bl -tag -width "/etc/authpf/authpf.conf" -compact  .It Pa /etc/authpf/authpf.conf diff --git a/contrib/pf/authpf/authpf.c b/contrib/pf/authpf/authpf.c index 8828ffc2f37e..515cf8edea2b 100644 --- a/contrib/pf/authpf/authpf.c +++ b/contrib/pf/authpf/authpf.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: authpf.c,v 1.68 2003/08/21 19:13:23 frantzen Exp $	*/ +/*	$OpenBSD: authpf.c,v 1.75 2004/01/29 01:55:10 deraadt Exp $	*/  /*   * Copyright (C) 1998 - 2002 Bob Beck (beck@openbsd.org). @@ -46,6 +46,7 @@  #include <unistd.h>  #include <pfctl_parser.h> +#include <pfctl.h>  #include "pathnames.h" @@ -91,12 +92,6 @@ main(int argc, char *argv[])  	char		*cp;  	uid_t		 uid; -	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld", -	    (long)getpid())) < 0 || n >= sizeof(rulesetname)) { -		syslog(LOG_ERR, "pid too large for ruleset name"); -		exit(1); -	} -  	config = fopen(PATH_CONFFILE, "r");  	if ((cp = getenv("SSH_TTY")) == NULL) { @@ -124,7 +119,6 @@ main(int argc, char *argv[])  		    "cannot determine IP from SSH_CLIENT %s", ipsrc);  		exit(1);  	} -  	/* open the pf device */  	dev = open(PATH_DEVFILE, O_RDWR);  	if (dev == -1) { @@ -153,6 +147,18 @@ main(int argc, char *argv[])  		goto die;  	} +	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)", +	    luser, (long)getpid())) < 0 || n >= sizeof(rulesetname)) { +		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld", +		    luser, (long)getpid(), (long)getpid()); +		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld", +		    (long)getpid())) < 0 || n >= sizeof(rulesetname)) { +			syslog(LOG_ERR, "pid too large for ruleset name"); +			goto die; +		} +	} + +  	/* Make our entry in /var/authpf as /var/authpf/ipaddr */  	n = snprintf(pidfile, sizeof(pidfile), "%s/%s", PATH_PIDFILE, ipsrc);  	if (n < 0 || (u_int)n >= sizeof(pidfile)) { @@ -235,15 +241,22 @@ main(int argc, char *argv[])  	seteuid(getuid());  	setuid(getuid()); -	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) +	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON); + +	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) { +		syslog(LOG_INFO, "user %s prohibited", luser);  		do_death(0); +	} -	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON); -	if (config == NULL || read_config(config)) +	if (config == NULL || read_config(config)) { +		syslog(LOG_INFO, "bad or nonexistent %s", PATH_CONFFILE);  		do_death(0); +	} -	if (remove_stale_rulesets()) +	if (remove_stale_rulesets()) { +		syslog(LOG_INFO, "error removing stale rulesets");  		do_death(0); +	}  	/* We appear to be making headway, so actually mark our pid */  	rewind(pidfp); @@ -253,7 +266,7 @@ main(int argc, char *argv[])  	if (change_filter(1, luser, ipsrc) == -1) {  		printf("Unable to modify filters\r\n"); -		do_death(1); +		do_death(0);  	}  	signal(SIGTERM, need_death); @@ -536,15 +549,20 @@ remove_stale_rulesets(void)  	mnr = prs.nr;  	nr = 0;  	while (nr < mnr) { -		char	*s; +		char	*s, *t;  		pid_t	 pid;  		prs.nr = nr;  		if (ioctl(dev, DIOCGETRULESET, &prs))  			return (1);  		errno = 0; -		pid = strtoul(prs.name, &s, 10); -		if (!prs.name[0] || errno || *s) +		if ((t = strchr(prs.name, '(')) == NULL) +			t = prs.name; +		else +			t++; +		pid = strtoul(t, &s, 10); +		if (!prs.name[0] || errno || +		    (*s && (t == prs.name || *s != ')')))  			return (1);  		if (kill(pid, 0) && errno != EPERM) {  			int i; @@ -576,14 +594,11 @@ change_filter(int add, const char *luser, const char *ipsrc)  {  	char			 fn[MAXPATHLEN];  	FILE			*f = NULL; -	const int		 action[PF_RULESET_MAX] = { PF_SCRUB, -				    PF_PASS, PF_NAT, PF_BINAT, PF_RDR };  	struct pfctl		 pf; -	struct pfioc_rule	 pr[PF_RULESET_MAX]; +	struct pfr_buffer	 t;  	int			 i; -	if (luser == NULL || !luser[0] || strlen(luser) >= -	    PF_RULESET_NAME_SIZE || ipsrc == NULL || !ipsrc[0]) { +	if (luser == NULL || !luser[0] || ipsrc == NULL || !ipsrc[0]) {  		syslog(LOG_ERR, "invalid luser/ipsrc");  		goto error;  	} @@ -615,18 +630,18 @@ change_filter(int add, const char *luser, const char *ipsrc)  		syslog(LOG_ERR, "unable to load kernel's OS fingerprints");  		goto error;  	} - +	bzero(&t, sizeof(t)); +	t.pfrb_type = PFRB_TRANS;  	memset(&pf, 0, sizeof(pf));  	for (i = 0; i < PF_RULESET_MAX; ++i) { -		memset(&pr[i], 0, sizeof(pr[i])); -		pr[i].rule.action = action[i]; -		strlcpy(pr[i].anchor, anchorname, sizeof(pr[i].anchor)); -		strlcpy(pr[i].ruleset, rulesetname, sizeof(pr[i].ruleset)); -		if (ioctl(dev, DIOCBEGINRULES, &pr[i])) { -			syslog(LOG_ERR, "DIOCBEGINRULES %m"); +		if (pfctl_add_trans(&t, i, anchorname, rulesetname)) { +			syslog(LOG_ERR, "pfctl_add_trans %m");  			goto error;  		} -		pf.prule[i] = &pr[i]; +	} +	if (pfctl_trans(dev, &t, DIOCXBEGIN, 0)) { +		syslog(LOG_ERR, "DIOCXBEGIN (%s) %m", add?"add":"remove"); +		goto error;  	}  	if (add) { @@ -637,6 +652,10 @@ change_filter(int add, const char *luser, const char *ipsrc)  		}  		pf.dev = dev; +		pf.trans = &t; +		pf.anchor = anchorname; +		pf.ruleset = rulesetname; +  		infile = fn;  		if (parse_rules(f, &pf) < 0) {  			syslog(LOG_ERR, "syntax error in rule file: " @@ -649,16 +668,10 @@ change_filter(int add, const char *luser, const char *ipsrc)  		f = NULL;  	} -	for (i = 0; i < PF_RULESET_MAX; ++i) -		/* -		 * ignore EINVAL on removal, it means the anchor was -		 * already automatically removed by the kernel. -		 */ -		if (ioctl(dev, DIOCCOMMITRULES, &pr[i]) && -		    (add || errno != EINVAL)) { -			syslog(LOG_ERR, "DIOCCOMMITRULES %m"); -			goto error; -		} +	if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) { +		syslog(LOG_ERR, "DIOCXCOMMIT (%s) %m", add?"add":"remove"); +		goto error; +	}  	if (add) {  		gettimeofday(&Tstart, NULL); @@ -673,6 +686,8 @@ change_filter(int add, const char *luser, const char *ipsrc)  error:  	if (f != NULL)  		fclose(f); +	if (pfctl_trans(dev, &t, DIOCXROLLBACK, 0)) +		syslog(LOG_ERR, "DIOCXROLLBACK (%s) %m", add?"add":"remove");  	infile = NULL;  	return (-1); @@ -748,37 +763,44 @@ do_death(int active)  int  pfctl_add_rule(struct pfctl *pf, struct pf_rule *r)  { -	struct pfioc_rule	*pr; +	u_int8_t		rs_num; +	struct pfioc_rule	pr;  	switch (r->action) {  	case PF_PASS:  	case PF_DROP: -		pr = pf->prule[PF_RULESET_FILTER]; +		rs_num = PF_RULESET_FILTER;  		break;  	case PF_SCRUB: -		pr = pf->prule[PF_RULESET_SCRUB]; +		rs_num = PF_RULESET_SCRUB;  		break;  	case PF_NAT:  	case PF_NONAT: -		pr = pf->prule[PF_RULESET_NAT]; +		rs_num = PF_RULESET_NAT;  		break;  	case PF_RDR:  	case PF_NORDR: -		pr = pf->prule[PF_RULESET_RDR]; +		rs_num = PF_RULESET_RDR;  		break;  	case PF_BINAT:  	case PF_NOBINAT: -		pr = pf->prule[PF_RULESET_BINAT]; +		rs_num = PF_RULESET_BINAT;  		break;  	default:  		syslog(LOG_ERR, "invalid rule action %d", r->action);  		return (1);  	} + +	bzero(&pr, sizeof(pr)); +	strlcpy(pr.anchor, pf->anchor, sizeof(pr.anchor)); +	strlcpy(pr.ruleset, pf->ruleset, sizeof(pr.ruleset));  	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 (ioctl(pf->dev, DIOCADDRULE, pr)) { +	pr.ticket = pfctl_get_ticket(pf->trans, rs_num, pf->anchor, +	    pf->ruleset); +	pr.pool_ticket = pf->paddr.ticket; +	memcpy(&pr.rule, r, sizeof(pr.rule)); +	if (ioctl(pf->dev, DIOCADDRULE, &pr)) {  		syslog(LOG_ERR, "DIOCADDRULE %m");  		return (1);  	} @@ -839,6 +861,13 @@ pfctl_set_logif(struct pfctl *pf, char *ifname)  }  int +pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) +{ +	fprintf(stderr, "set hostid not supported in authpf\n"); +	return (1); +} + +int  pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)  {  	fprintf(stderr, "set timeout not supported in authpf\n"); @@ -853,6 +882,13 @@ pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)  }  int +pfctl_set_debug(struct pfctl *pf, char *d) +{ +	fprintf(stderr, "set debug not supported in authpf\n"); +	return (1); +} + +int  pfctl_define_table(char *name, int flags, int addrs, const char *anchor,      const char *ruleset, struct pfr_buffer *ab, u_int32_t ticket)  { @@ -862,10 +898,14 @@ pfctl_define_table(char *name, int flags, int addrs, const char *anchor,  int  pfctl_rules(int dev, char *filename, int opts, char *anchorname, -    char *rulesetname) +    char *rulesetname, struct pfr_buffer *t)  {  	/* never called, no anchors inside anchors, but we need the stub */  	fprintf(stderr, "load anchor not supported from authpf\n");  	return (1);  } +void +pfctl_print_title(char *title) +{ +} diff --git a/contrib/pf/ftp-proxy/ftp-proxy.8 b/contrib/pf/ftp-proxy/ftp-proxy.8 index 2832ddbb9d2e..e68bdde495cc 100644 --- a/contrib/pf/ftp-proxy/ftp-proxy.8 +++ b/contrib/pf/ftp-proxy/ftp-proxy.8 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: ftp-proxy.8,v 1.37 2003/09/05 12:27:47 jmc Exp $ +.\"	$OpenBSD: ftp-proxy.8,v 1.40 2004/03/16 08:50:07 jmc Exp $  .\"  .\" Copyright (c) 1996-2001  .\"	Obtuse Systems Corporation, All rights reserved. @@ -36,10 +36,11 @@  .Sh SYNOPSIS  .Nm ftp-proxy  .Op Fl AnrVw +.Op Fl a Ar address  .Op Fl D Ar debuglevel  .Op Fl g Ar group -.Op Fl m Ar minport  .Op Fl M Ar maxport +.Op Fl m Ar minport  .Op Fl t Ar timeout  .Op Fl u Ar user  .Sh DESCRIPTION @@ -65,6 +66,26 @@ or  .Qq anonymous  only.  Any attempt to log in as another user will be blocked by the proxy. +.It Fl a Ar address +Specify the local IP address to use in +.Xr bind 2 +as the source for connections made by +.Nm ftp-proxy +when connecting to destination FTP servers. +This may be necessary if the interface address of +your default route is not reachable from the destinations +.Nm +is attempting connections to, or this address is different from the one +connections are being NATed to. +In the usual case this means that +.Ar address +should be a publicly visible IP address assigned to one of +the interfaces on the machine running +.Nm +and should be the same address to which you are translating traffic +if you are using the +.Fl n +option.  .It Fl D Ar debuglevel  Specify a debug level, where the proxy emits verbose debug output  into @@ -80,14 +101,6 @@ lookups which require root.  By default,  .Nm  uses the default group of the user it drops privilege to. -.It Fl m Ar minport -Specify the lower end of the port range the proxy will use for all -data connections it establishes. -The default is -.Dv IPPORT_HIFIRSTAUTO -defined in -.Aq Pa netinet/in.h -as 49152.  .It Fl M Ar maxport  Specify the upper end of the port range the proxy will use for the  data connections it establishes. @@ -96,6 +109,14 @@ The default is  defined in  .Aq Pa netinet/in.h  as 65535. +.It Fl m Ar minport +Specify the lower end of the port range the proxy will use for all +data connections it establishes. +The default is +.Dv IPPORT_HIFIRSTAUTO +defined in +.Aq Pa netinet/in.h +as 49152.  .It Fl n  Activate network address translation  .Pq NAT @@ -173,8 +194,8 @@ A typical way to do this would be to use a  .Xr pf.conf 5  rule such as  .Bd -literal -offset 2n -int_if = xl0 -rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021 +int_if = \&"xl0\&" +rdr pass on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021  .Ed  .Pp  .Xr inetd 8 diff --git a/contrib/pf/ftp-proxy/ftp-proxy.c b/contrib/pf/ftp-proxy/ftp-proxy.c index 88b6fd16b867..18bc0a619a20 100644 --- a/contrib/pf/ftp-proxy/ftp-proxy.c +++ b/contrib/pf/ftp-proxy/ftp-proxy.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: ftp-proxy.c,v 1.33 2003/08/22 21:50:34 david Exp $ */ +/*	$OpenBSD: ftp-proxy.c,v 1.35 2004/03/14 21:51:44 dhartmei Exp $ */  /*   * Copyright (c) 1996-2001 @@ -67,7 +67,7 @@   * - per-user rules perhaps.   */ -#include <sys/types.h> +#include <sys/param.h>  #include <sys/time.h>  #include <sys/socket.h> @@ -148,6 +148,7 @@ char *Group;  extern int Debug_Level;  extern int Use_Rdns; +extern in_addr_t Bind_Addr;  extern char *__progname;  typedef enum { @@ -171,9 +172,8 @@ static void  usage(void)  {  	syslog(LOG_NOTICE, -	    "usage: %s [-AnrVw] [-D debuglevel] [-g group] %s %s", -	    __progname, "[-m minport] [-M maxport] [-t timeout]", -	    "[-u user]"); +	    "usage: %s [-AnrVw] [-a address] [-D debuglevel [-g group]" +	    " [-M maxport] [-m minport] [-t timeout] [-u user]", __progname);  	exit(EX_USAGE);  } @@ -973,9 +973,18 @@ main(int argc, char *argv[])  	int use_tcpwrapper = 0;  #endif /* LIBWRAP */ -	while ((ch = getopt(argc, argv, "D:g:m:M:t:u:AnVwr")) != -1) { +	while ((ch = getopt(argc, argv, "a:D:g:m:M:t:u:AnVwr")) != -1) {  		char *p;  		switch (ch) { +		case 'a': +			if (!*optarg) +				usage(); +			if ((Bind_Addr = inet_addr(optarg)) == INADDR_NONE) { +				syslog(LOG_NOTICE, +					"%s: invalid address", optarg); +				usage(); +			} +			break;  		case 'A':  			AnonFtpOnly = 1; /* restrict to anon usernames only */  			break; diff --git a/contrib/pf/ftp-proxy/util.c b/contrib/pf/ftp-proxy/util.c index 3c8b20eab912..17a88cae6431 100644 --- a/contrib/pf/ftp-proxy/util.c +++ b/contrib/pf/ftp-proxy/util.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: util.c,v 1.16 2003/06/28 01:04:57 deraadt Exp $ */ +/*	$OpenBSD: util.c,v 1.18 2004/01/22 16:10:30 beck Exp $ */  /*   * Copyright (c) 1996-2001 @@ -58,6 +58,7 @@  int Debug_Level;  int Use_Rdns; +in_addr_t Bind_Addr = INADDR_NONE;  void		debuglog(int debug_level, const char *fmt, ...); @@ -77,7 +78,8 @@ get_proxy_env(int connected_fd, struct sockaddr_in *real_server_sa_ptr,      struct sockaddr_in *client_sa_ptr)  {  	struct pfioc_natlook natlook; -	int slen, fd; +	socklen_t slen; +	int fd;  	slen = sizeof(*real_server_sa_ptr);  	if (getsockname(connected_fd, (struct sockaddr *)real_server_sa_ptr, @@ -257,10 +259,13 @@ get_backchannel_socket(int type, int min_port, int max_port, int start_port,  		bzero(&sa, sizeof sa);  		sa.sin_family = AF_INET; -		if (sap == NULL) -			sa.sin_addr.s_addr = INADDR_ANY; +		if (Bind_Addr == INADDR_NONE) +			if (sap == NULL) +				sa.sin_addr.s_addr = INADDR_ANY; +			else +				sa.sin_addr.s_addr = sap->sin_addr.s_addr;  		else -			sa.sin_addr.s_addr = sap->sin_addr.s_addr; +			sa.sin_addr.s_addr = Bind_Addr;  		/*  		 * Indicate that we want to reuse a port if it happens that the diff --git a/contrib/pf/man/pf.4 b/contrib/pf/man/pf.4 index f01dcb38aa6e..df0ff6c0fb83 100644 --- a/contrib/pf/man/pf.4 +++ b/contrib/pf/man/pf.4 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: pf.4,v 1.37 2003/08/28 09:41:22 jmc Exp $ +.\"	$OpenBSD: pf.4,v 1.48 2004/03/27 17:15:30 henning Exp $  .\"  .\" Copyright (C) 2001, Kjell Wooding.  All rights reserved.  .\" @@ -33,7 +33,7 @@  .Nm pf  .Nd packet filter  .Sh SYNOPSIS -.Cd "pseudo-device pf 1" +.Cd "pseudo-device pf"  .Sh DESCRIPTION  Packet filtering takes place in the kernel.  A pseudo-device, @@ -72,11 +72,7 @@ Stops the packet filter.  Starts the ALTQ bandwidth control system.  .It Dv DIOCSTOPALTQ  Stops the ALTQ bandwidth control system. -.It Dv DIOCBEGINADDRS  Fa "u_int32_t" -Clears the buffer address pool -and returns a ticket for subsequent DIOCADDADDR, DIOCADDRULE and -DIOCCHANGERULE calls. -.It Dv DIOCADDADDR     Fa "struct pfioc_pooladdr" +.It Dv DIOCBEGINADDRS  Fa "struct pfioc_pooladdr"  .Bd -literal  struct pfioc_pooladdr {  	u_int32_t		action; @@ -92,16 +88,17 @@ struct pfioc_pooladdr {  };  .Ed  .Pp +Clears the buffer address pool +and returns a +.Va ticket +for subsequent DIOCADDADDR, DIOCADDRULE and DIOCCHANGERULE calls. +.It Dv DIOCADDADDR     Fa "struct pfioc_pooladdr" +.Pp  Adds pool address  .Va addr  to the buffer address pool to be used in the following  DIOCADDRULE or DIOCCHANGERULE call.  All other members of the structure are ignored. -.It Dv DIOCBEGINRULES  Fa "u_int32_t" -Clears the inactive ruleset for the type of rule indicated by -.Va rule.action -and returns a ticket for subsequent -DIOCADDRULE and DIOCCOMMITRULES calls.  .It Dv DIOCADDRULE     Fa "struct pfioc_rule"  .Bd -literal  struct pfioc_rule { @@ -120,7 +117,7 @@ Adds  at the end of the inactive ruleset.  Requires  .Va ticket -obtained through preceding DIOCBEGINRULES call, and +obtained through preceding DIOCXBEGIN call, and  .Va pool_ticket  obtained through DIOCBEGINADDRS call.  DIOCADDADDR must also be called if any pool addresses are required. @@ -133,26 +130,16 @@ names indicate the anchor and ruleset in which to append the rule.  and  .Va action  are ignored. -.It Dv DIOCCOMMITRULES Fa "u_int32_t" -Switch inactive to active filter ruleset. -Requires -.Va ticket . -.It Dv DIOCBEGINALTQS  Fa "u_int32_t" -Clears the inactive list of queues and returns a ticket for subsequent -DIOCADDALTQ and DIOCCOMMITALTQS calls.  .It Dv DIOCADDALTQ     Fa "struct pfioc_altq"  Adds  .Bd -literal  struct pfioc_altq { +	u_int32_t	action;  	u_int32_t	ticket;  	u_int32_t	nr;  	struct pf_altq   altq;  };  .Ed -.It Dv DIOCCOMMITALTQS Fa "u_int32_t" -Switch inactive to active list of queues. -Requires -.Va ticket .  .It Dv DIOCGETRULES    Fa "struct pfioc_rule"  Returns  .Va ticket @@ -224,8 +211,6 @@ of length  .Va nbytes  for the queue specified by  .Va nr . -.It Dv DIOCCLRSTATES -Clears the state table.  .It Dv DIOCADDSTATE    Fa "struct pfioc_state"  Adds a state entry.  .It Dv DIOCGETSTATE    Fa "struct pfioc_state" @@ -246,8 +231,16 @@ struct pfioc_state_kill {  	int			psk_proto;  	struct pf_rule_addr	psk_src;  	struct pf_rule_addr	psk_dst; +	char			psk_ifname[IFNAMSIZ];  };  .Ed +.It Dv DIOCCLRSTATES  Fa "struct pfioc_state_kill" +Clears all states. +It works like +.Dv DIOCKILLSTATES , +but ignores the psk_af, psk_proto, psk_src and psk_dst fields of the +.Fa pfioc_state_kill +structure.  .It Dv DIOCSETSTATUSIF Fa "struct pfioc_if"  .Bd -literal  struct pfioc_if { @@ -259,14 +252,19 @@ Specifies the interface for which statistics are accumulated.  .It Dv DIOCGETSTATUS   Fa "struct pf_status"  .Bd -literal  struct pf_status { -	u_int64_t	 counters[PFRES_MAX]; -	u_int64_t	 fcounters[FCNT_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	 since; -	u_int32_t	 debug; +	u_int64_t	counters[PFRES_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_int64_t	stateid; +	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];  };  .Ed  .Pp @@ -285,7 +283,7 @@ struct pfioc_natlook {  	u_int16_t	 dport;  	u_int16_t	 rsport;  	u_int16_t	 rdport; -	u_int8_t	 af; +	sa_family_t	 af;  	u_int8_t	 proto;  	u_int8_t	 direction;  }; @@ -525,19 +523,6 @@ or deleted by the kernel.  Yes, tables can be deleted if one removes the  .Va persist  flag of an unreferenced table. -.It Dv DIOCRINABEGIN Fa "struct pfioc_table" -Starts a transaction with the inactive set of tables. -Cleans up any leftover from a previously aborted transaction, and returns -a new ticket. -On exit, pfrio_ndel contains the number of leftover table deleted, and -pfrio_ticket contains a valid ticket to use for the following two IOCTLs. -.It Dv DIOCRINACOMMIT Fa "struct pfioc_table" -Commit the inactive set of tables into the active set. -While copying the addresses, do a best effort to keep statistics for -addresses present before and after the commit. -On entry, io->pfrio_ticket takes a valid ticket. -On exit, io->pfrio_nadd and io->pfrio_nchange contain the number of tables -added and altered by the commit operation.  .It Dv DIOCRINADEFINE Fa "struct pfioc_table"  Defines a table in the inactive set.  On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size] @@ -546,6 +531,46 @@ A valid ticket must also be supplied to pfrio_ticket.  On exit, pfrio_nadd contains 0 if the table was already defined in the  inactive list, or 1 if a new table has been created.  pfrio_naddr contains the number of addresses effectively put in the table. +.It Dv DIOCXBEGIN Fa "struct pfioc_trans" +.Bd -literal +#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[PF_ANCHOR_NAME_SIZE]; +                char            ruleset[PF_RULESET_NAME_SIZE]; +                u_int32_t       ticket; +        }               *array; +}; +.Ed +.Pp +Clears all the inactive rulesets specified in the +.Fa "struct 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. +.It Dv DIOCXCOMMIT Fa "struct pfioc_trans" +Atomically switch a vector of inactive rulesets to the active rulesets. +Implemented as a standard 2-phase commit, which will either fail for all +rulesets or completely succeed. +All tickets need to be valid. +Returns +.Dv EBUSY +if a concurrent process is trying to update some of the same rulesets +concurrently. +.It Dv DIOCXROLLBACK Fa "struct pfioc_trans" +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 DIOCFPFLUSH  Flush the passive OS fingerprint table.  .It Dv DIOCFPADD Fa "struct pf_osfp_ioctl" @@ -623,6 +648,115 @@ 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 EBUSY. +.It Dv DIOCGETSRCNODES Fa "struct pfioc_src_nodes" +.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 the +.Ar sticky-address +and +.Ar source-track +options. +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 the ioctl call +.Va psn_len +will be set to the length of the buffer actually used. +.It Dv DIOCCLRSRCNODES Fa "struct pfioc_table" +Clear the tree of source tracking nodes. +.It Dv DIOCIGETIFACES Fa "struct pfioc_iface" +Gets 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; +}; + +#define PFI_FLAG_GROUP     0x0001  /* gets groups of interfaces */ +#define PFI_FLAG_INSTANCE  0x0002  /* gets single interfaces */ +#define PFI_FLAG_ALLMASK   0x0003 +.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 +represents the number of +.Va pfi_if +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 sizeof(struct pfi_if). +.Va pfiio_flags +should be set to +.Dv PFI_FLAG_GROUP , PFI_FLAG_INSTANCE , +or both to tell the kernel to return a group of interfaces +(drivers, like "fxp"), real interface instances (like "fxp1") or both. +The data is returned in the +.Va pfi_if +structure described below: +.Bd -literal +struct pfi_if { +        char                             pfif_name[IFNAMSIZ]; +        u_int64_t                        pfif_packets[2][2][2]; +        u_int64_t                        pfif_bytes[2][2][2]; +        u_int64_t                        pfif_addcnt; +        u_int64_t                        pfif_delcnt; +        long                             pfif_tzero; +        int                              pfif_states; +        int                              pfif_rules; +        int                              pfif_flags; +}; + +#define PFI_IFLAG_GROUP         0x0001  /* group of interfaces */ +#define PFI_IFLAG_INSTANCE      0x0002  /* single instance */ +#define PFI_IFLAG_CLONABLE      0x0010  /* clonable group */ +#define PFI_IFLAG_DYNAMIC       0x0020  /* dynamic group */ +#define PFI_IFLAG_ATTACHED      0x0040  /* interface attached */ +#define PFI_IFLAG_REFERENCED    0x0080  /* referenced by rules */ +.Ed +.It Dv DIOCICLRISTATS Fa "struct pfioc_iface" +Clear the statistics counters of one or more interfaces. +.Va pfiio_name +and +.Va pfrio_flags +can be used to select which interfaces need to be cleared. +The filtering process is the same as for +.Dv DIOCIGETIFACES . +.Va pfiio_nzero +will be set by the kernel to the number of interfaces and drivers +that have been cleared.  .El  .Sh EXAMPLES  The following example demonstrates how to use the DIOCNATLOOK command diff --git a/contrib/pf/man/pf.conf.5 b/contrib/pf/man/pf.conf.5 index 988131857aec..b5db41295518 100644 --- a/contrib/pf/man/pf.conf.5 +++ b/contrib/pf/man/pf.conf.5 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: pf.conf.5,v 1.271 2003/09/02 18:37:08 jmc Exp $ +.\"	$OpenBSD: pf.conf.5,v 1.292 2004/02/24 05:44:48 mcbride Exp $  .\"  .\" Copyright (c) 2002, Daniel Hartmeier  .\" All rights reserved. @@ -234,6 +234,9 @@ command.  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 @@ -366,10 +369,21 @@ sets the maximum number of entries in the memory pool used for fragment  reassembly (generated by  .Ar scrub  rules) to 20000. +Finally, +.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 source-track +options) to 2000.  .Pp  These can be combined:  .Bd -literal -offset indent -set limit { states 20000, frags 20000 } +set limit { states 20000, frags 20000, src-nodes 2000 }  .Ed  .Pp  .It Ar set optimization @@ -420,6 +434,24 @@ 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 group-bound +States are bound to interface group (i.e. ppp) +.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 require-order  By default  .Xr pfctl 8 @@ -450,6 +482,22 @@ ruleset finishes loading.  For example:  .Pp  .Dl set fingerprints \&"/etc/pf.os.devel\&" +.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 @@ -1092,15 +1140,17 @@ are specified, the rule will match packets in both directions.  .It Ar log  In addition to the action specified, a log message is generated.  All packets for that connection are logged, unless the -.Ar keep state -or +.Ar keep state ,  .Ar modulate state +or +.Ar synproxy state  options are specified, in which case only the  packet that establishes the state is logged.  (See -.Ar keep state -and +.Ar keep state ,  .Ar modulate state +and +.Ar synproxy state  below).  The logged packets are sent to the  .Xr pflog 4 @@ -1114,9 +1164,10 @@ in  binary format.  .It Ar log-all  Used with -.Ar keep state -or +.Ar keep state ,  .Ar modulate state +or +.Ar synproxy state  rules to force logging of all packets for a connection.  As with  .Ar log , @@ -1131,6 +1182,8 @@ is skipped.  .It Ar on <interface>  This rule applies only to packets coming in on, or going out through, this  particular interface. +It is also possible to simply give the interface driver name, like ppp or fxp, +to make the rule match packets flowing through a group of interfaces.  .It Ar <af>  This rule applies only to packets of this address family.  Supported values are @@ -1175,14 +1228,24 @@ Interface names can have modifiers appended:  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 in parentheses changes this behaviour. +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. @@ -1205,15 +1268,19 @@ Ports and ranges of ports are specified by using these operators:  <=	(less than or equal)  >	(greater than)  >=	(greater than or equal) -><	(range) +:	(range including boundaries) +><	(range excluding boundaries)  <>	(except range)  .Ed  .Pp ->< and <> -are binary operators (they take two arguments), and the range -does not include the limits. +><, <> and : +are binary operators (they take two arguments).  For instance:  .Bl -tag -width Fl +.It Ar port 2000:2004 +means +.Sq all ports >= 2000 and <= 2004 , +hence ports 2000, 2001, 2002, 2003 and 2004.  .It Ar port 2000 >< 2004  means  .Sq all ports > 2000 and < 2004 , @@ -1421,13 +1488,17 @@ A packet is only ever assigned one tag at a time.  rules that use the  .Ar tag  keyword must also use -.Ar keep state . +.Ar keep state , +.Ar modulate state +or +.Ar synproxy state .  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 <string>  Used with filter rules to specify that packets must already  be tagged with the given tag in order to match the rule. @@ -1533,6 +1604,23 @@ 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 source-track +See +.Sx STATEFUL TRACKING OPTIONS +for more ways to control the source tracking.  .Sh STATEFUL INSPECTION  .Xr pf 4  is a stateful packet filter, which means it can track the state of @@ -1579,6 +1667,37 @@ The initial packet of each connection has the SYN  flag set, will be passed and creates state.  All further packets of these connections are passed if they match a state.  .Pp +By default, packets coming in and out of any interface can match a state, +but it is also possible to change that behaviour by assigning states to a +single interface or a group of interfaces. +.Pp +The default policy is specified by the +.Ar state-policy +global option, but this can be adjusted on a per-rule basis by adding one +of the +.Ar if-bound , +.Ar group-bound +or +.Ar floating +keywords to the +.Ar keep state +option. +For example, if a rule is defined as: +.Bd -literal -offset indent +pass out on ppp from any to 10.12/16 keep state (group-bound) +.Ed +.Pp +A state created on ppp0 would match packets an all PPP interfaces, +but not packets flowing through fxp0 or any other interface. +.Pp +Keeping rules +.Ar floating +is the more flexible option when the firewall is in a dynamic routing +environment. +However, this has some security implications since a state created by one +trusted network could allow potentially hostile packets coming in from other +interfaces. +.Pp  Specifying  .Ar flags S/SA  restricts state creation to the initial SYN @@ -1695,7 +1814,7 @@ handshake.  The proxy is transparent to both endpoints, they each see a single  connection from/to the other endpoint.  .Xr pf 4 -choses random initial sequence numbers for both handshakes. +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. @@ -1730,8 +1849,26 @@ support the following options:  Limits the number of concurrent states the rule may create.  When this limit is reached, further packets matching the rule that would  create state are dropped, 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 Ar <timeout> <seconds>  Changes the timeout values used for states created by this rule. +.Pp +When the +.Ar source-track +keyword is specified, the number of states per source IP is tracked. +The following limits can be set: +.Pp +.Bl  -tag -width xxxx -compact +.It Ar max-src-nodes +Limits the maximum number of source addresses which can simultaneously +have state table entries. +.It Ar max-src-states +Limits the maximum number of simultaneous state entries that a single +source address can create with this rule. +.El  For a list of all valid timeout names, see  .Sx OPTIONS  above. @@ -1740,7 +1877,8 @@ Multiple options can be specified, separated by commas:  .Bd -literal  pass in proto tcp from any to any \e        port www flags S/SA keep state \e -      (max 100, tcp.established 60, tcp.closing 5) +      (max 100, source-track rule, max-src-nodes 75, \e +      max-src-states 3, tcp.established 60, tcp.closing 5)  .Ed  .El  .Sh OPERATING SYSTEM FINGERPRINTING @@ -1853,7 +1991,7 @@ to local addresses.  One should pass these explicitly.  .Sh FRAGMENT HANDLING  The size of IP datagrams (packets) can be significantly larger than the -the maximum transmission unit (MTU) of the network. +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. @@ -2027,7 +2165,7 @@ rule after the  rule:  .Bd -literal -offset indent  anchor spam -load anchor spam:manual from /etc/pf-spam.conf +load anchor spam:manual from "/etc/pf-spam.conf"  .Ed  .Pp  When @@ -2072,8 +2210,11 @@ 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 ne3 proto tcp from any to any port 80 -> 127.0.0.1 port 8080 +rdr on $ext_if proto tcp from any to any port 80 -> 127.0.0.1 port 8080  .Ed  .Pp  If the @@ -2081,7 +2222,8 @@ If the  modifier is given, packets matching the translation rule are passed without  inspecting the filter rules:  .Bd -literal -rdr pass on ne3 proto tcp from any to any port 80 -> 127.0.0.1 port 8080 +rdr pass on $ext_if proto tcp from any to any port 80 -> 127.0.0.1 \e +      port 8080  .Ed  .Pp  In the example below, vlan12 is configured as 192.168.168.1; @@ -2096,83 +2238,80 @@ for the nodes on vlan12.  nat on ! vlan12 from 192.168.168.0/24 to any -> 204.92.77.111  .Ed  .Pp -In the example below, fxp1 is the outside interface; the machine sits between a -fake internal 144.19.74.* network, and a routable external IP of 204.92.77.100. +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 fxp1 proto ah from 144.19.74.0/24 to any -nat on fxp1 from 144.19.74.0/24 to any -> 204.92.77.100 +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 -> 204.92.77.100  .Ed  .Pp -In the example below, fxp0 is the internal interface. -Packets bound -for one specific server, as well as those generated by the sysadmins -are not proxied; all other connections are. +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 fxp0 proto { tcp, udp } from any to $server port 80 -no rdr on fxp0 proto { tcp, udp } from $sysadmins to any port 80 -rdr on fxp0 proto { tcp, udp } from any to any port 80 -> 127.0.0.1 port 80 +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 -> 127.0.0.1 \e +      port 80  .Ed  .Pp  This longer example uses both a NAT and a redirection. -Interface kue0 is the outside interface, and its external address is -157.161.48.183. -Interface fxp0 is the inside interface, and we are running +The external interface has the address 157.161.48.183. +On the internal interface, we are running  .Xr ftp-proxy 8 ,  listening for outbound ftp sessions captured to port 8021.  .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 kue0 inet from ! (kue0) to any -> (kue0) +nat on $ext_if inet from ! ($ext_if) to any -> ($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 kue0 inet proto udp from any port = isakmp to any -> (kue0) \e +nat on $ext_if inet proto udp from any port = isakmp to any -> ($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 kue0 from 10.1.2.150 to any -> (kue0) +binat on $ext_if from 10.1.2.150 to any -> ($ext_if)  # RDR  # Translate incoming packets' destination addresses.  # As an example, redirect a TCP and UDP port to an internal machine. -rdr on kue0 inet proto tcp from any to (kue0) port 8080 -> 10.1.2.151 \e -      port 22 -rdr on kue0 inet proto udp from any to (kue0) port 8080 -> 10.1.2.151 \e -      port 53 +rdr on $ext_if inet proto tcp from any to ($ext_if) port 8080 \e +      -> 10.1.2.151 port 22 +rdr on $ext_if inet proto udp from any to ($ext_if) port 8080 \e +      -> 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 fxp0 proto tcp from any to any port 21 -> 127.0.0.1 port 8021 +rdr on $int_if proto tcp from any to any port 21 -> 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. -Interface fxp0 is the external interface.  .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 fxp0 inet from any to any -> 192.0.2.16/28 source-hash +nat on $ext_if inet from any to any -> 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 fxp0 proto tcp from any to any port 80 \e +rdr on $ext_if proto tcp from any to any port 80 \e        -> { 10.1.2.155, 10.1.2.160, 10.1.2.161 } round-robin  .Ed  .Sh FILTER EXAMPLES @@ -2283,8 +2422,11 @@ option         = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] |                   [ "limit" ( limit-item | "{" limit-list "}" ) ] |                   [ "loginterface" ( interface-name | "none" ) ] |                   [ "block-policy" ( "drop" | "return" ) ] | +		 [ "state-policy" ( "if-bound" | "group-bound" | +                 "floating" ) ]                   [ "require-order" ( "yes" | "no" ) ] -                 [ "fingerprints" filename ] ) +                 [ "fingerprints" filename ] | +                 [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ] )  pf-rule        = action [ ( "in" | "out" ) ]                   [ "log" | "log-all" ] [ "quick" ] @@ -2299,7 +2441,7 @@ filteropt      = user | group | flags | icmp-type | icmp6-type | tos |                   "max-mss" number | "random-id" | "reassemble tcp" |                   fragmentation | "allow-opts" |                   "label" string | "tag" string | [ ! ] "tagged" string -                 "queue" "(" string | ( string [ [ "," ] string ] ) ")" +                 "queue" ( string | "(" string [ [ "," ] string ] ")" )  nat-rule       = [ "no" ] "nat" [ "pass" ] [ "on" ifspec ] [ af ]                   [ protospec ] hosts [ "tag" string ] @@ -2341,7 +2483,7 @@ anchor-rule    = "anchor" string [ ( "in" | "out" ) ] [ "on" ifspec ]  trans-anchors  = ( "nat-anchor" | "rdr-anchor" | "binat-anchor" ) string                   [ "on" ifspec ] [ af ] [ "proto" ] [ protospec ] [ hosts ] -load-anchor    = "load" anchorname:rulesetname "from" filename +load-anchor    = "load anchor" anchorname:rulesetname "from" filename  queueopts-list = queueopts-list queueopts | queueopts  queueopts      = [ "bandwidth" bandwidth-spec ] | @@ -2350,7 +2492,7 @@ queueopts      = [ "bandwidth" bandwidth-spec ] |  schedulers     = ( cbq-def | priq-def | hfsc-def )  bandwidth-spec = "number" ( "b" | "Kb" | "Mb" | "Gb" | "%" ) -action         = "pass" | "block" [ "return" ] | "scrub" +action         = "pass" | "block" [ return ] | "scrub"  return         = "drop" | "return" | "return-rst" [ "( ttl" number ")" ] |                   "return-icmp" [ "(" icmpcode ["," icmp6code ] ")" ] |                   "return-icmp6" [ "(" icmp6code ")" ] @@ -2413,7 +2555,10 @@ tos            = "tos" ( "lowdelay" | "throughput" | "reliability" |                   [ "0x" ] number )  state-opts     = state-opt [ [ "," ] state-opts ] -state-opt      = ( "max" number ) | ( timeout ) +state-opt      = ( "max" number | "no-sync" | timeout | +                 "source-track" [ ( "rule" | "global" ) ] | +		 "max-src-nodes" number | "max-src-states" number | +                 "if-bound" | "group-bound" | "floating" )  fragmentation  = [ "fragment reassemble" | "fragment crop" |                   "fragment drop-ovl" ] @@ -2424,15 +2569,15 @@ timeout        = ( "tcp.first" | "tcp.opening" | "tcp.established" |                   "udp.first" | "udp.single" | "udp.multiple" |                   "icmp.first" | "icmp.error" |                   "other.first" | "other.single" | "other.multiple" | -                 "frag" | "interval" | +                 "frag" | "interval" | "src.track" |                   "adaptive.start" | "adaptive.end" ) number  limit-list     = limit-item [ [ "," ] limit-list ] -limit-item     = ( "states" | "frags" ) number +limit-item     = ( "states" | "frags" | "src-nodes" ) number  pooltype       = ( "bitmask" | "random" |                   "source-hash" [ ( hex-key | string-key ) ] | -                 "round-robin" ) +                 "round-robin" ) [ sticky-address ]  subqueue       = string | "{" queue-list "}"  queue-list     = string [ [ "," ] string ] @@ -2470,6 +2615,7 @@ Example rulesets.  .Xr ip 4 ,  .Xr ip6 4 ,  .Xr pf 4 , +.Xr pfsync 4 ,  .Xr tcp 4 ,  .Xr udp 4 ,  .Xr hosts 5 , diff --git a/contrib/pf/man/pf.os.5 b/contrib/pf/man/pf.os.5 index 485f69a73239..9978174ba544 100644 --- a/contrib/pf/man/pf.os.5 +++ b/contrib/pf/man/pf.os.5 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: pf.os.5,v 1.4 2003/08/28 09:41:23 jmc Exp $ +.\"	$OpenBSD: pf.os.5,v 1.5 2003/10/25 07:55:27 jmc Exp $  .\"  .\" Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>  .\" @@ -77,7 +77,7 @@ Allow any window size which is a multiple of the maximum transmission unit  The  .Ar ttl  value is the initial time to live in the IP header. -The fingerprint code will account for the volatility of the packets's TTL +The fingerprint code will account for the volatility of the packet's TTL  as it traverses a network.  .Pp  The diff --git a/contrib/pf/man/pflog.4 b/contrib/pf/man/pflog.4 index eb7a72e94cac..d7bee13a0835 100644 --- a/contrib/pf/man/pflog.4 +++ b/contrib/pf/man/pflog.4 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: pflog.4,v 1.4 2003/09/22 04:53:15 jmc Exp $ +.\"	$OpenBSD: pflog.4,v 1.7 2004/03/21 19:47:59 miod Exp $  .\"  .\" Copyright (c) 2001 Tobias Weingartner  .\" All rights reserved. @@ -30,19 +30,20 @@  .Nm pflog  .Nd packet filter logging interface  .Sh SYNOPSIS -.Sy pseudo-device Nm pflog Em <number> +.Cd "pseudo-device pflog"  .Sh DESCRIPTION  The  .Nm pflog -interface is the interface the packet filter, -.Xr pf 4 , -copies all the packets to which it has been configured to log. -In this way, all logged packets can easily be monitored in real +interface is a pseudo-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 8  on the  .Nm -interface. +interface, or stored to disk using +.Xr pflogd 8 .  .Pp  Each packet retrieved on this interface has a header associated  with it of length diff --git a/contrib/pf/man/pfsync.4 b/contrib/pf/man/pfsync.4 index 21dd7d5412f0..f7b39df4bcaa 100644 --- a/contrib/pf/man/pfsync.4 +++ b/contrib/pf/man/pfsync.4 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: pfsync.4,v 1.6 2003/06/06 10:29:41 jmc Exp $ +.\"	$OpenBSD: pfsync.4,v 1.16 2004/03/22 21:04:36 jmc Exp $  .\"  .\" Copyright (c) 2002 Michael Shalayeff  .\" All rights reserved. @@ -30,19 +30,48 @@  .Nm pfsync  .Nd packet filter states table logging interface  .Sh SYNOPSIS -.Sy pseudo-device Nm pfsync +.Cd "pseudo-device pfsync"  .Sh DESCRIPTION  The -.Nm pfsync -interface is the interface to the packet filter, -.Xr pf 4 , -exposing all the changes to the state table. -This allows for both debugging of rulesets and monitoring -for changes in the table by invoking +.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 8  on the  .Nm  interface. +If configured with a physical synchronisation interface, +.Nm +will also send state changes out on that interface using IP multicast, +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 . +However, state changes from packets received by +.Nm +over the network are not rebroadcast. +States created by a rule marked with the +.Ar no-sync +keyword are omitted from the +.Nm +interface (see +.Xr pf.conf 5 +for details). +.Pp +The +.Nm +interface will attempt to collapse multiple updates of the same +state into one message where possible. +The maximum number of times this can be done before the update is sent out +is controlled by the +.Ar maxupd +to ifconfig. +(see +.Xr ifconfig 8 +and the example below for more details)  .Pp  Each packet retrieved on this interface has a header associated  with it of length @@ -61,16 +90,133 @@ struct pfsync_header {  	u_int8_t count;  };  .Ed +.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 syncif fxp0 +.Ed +.Pp +State change messages are sent out on the synchronisation +interface using IP multicast packets. +The protocol is IP protocol 240, PFSYNC, and the multicast group +used is 224.0.0.240. +.Pp +It is important that the synchronisation interface be on a trusted +network as there is no authentication on the protocol and it would +be trivial to spoof packets which create states, bypassing the pf ruleset. +Ideally, this is a network dedicated to pfsync messages, +i.e. a crossover cable between two firewalls. +.Pp +There is a one-to-one correspondence between packets seen by +.Xr bpf 4 +on the +.Nm +interface, and packets sent out on the synchronisation interface, i.e.\& +a packet with 4 state deletion messages on +.Nm +means that the same 4 deletions were sent out on the synchronisation +interface. +However, the actual packet contents may differ as the messages +sent over the network are "compressed" where possible, containing +only the necessary information.  .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 handles all traffic \- if it dies or +is shut down, the second firewall takes 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 +.Pa /etc/hostname.sis0 : +.Bd -literal -offset indent +inet 10.0.0.254 255.255.255.0 NONE +.Ed +.Pp +.Pa /etc/hostname.sis1 : +.Bd -literal -offset indent +inet 192.168.0.254 255.255.255.0 NONE +.Ed +.Pp +.Pa /etc/hostname.sis2 : +.Bd -literal -offset indent +inet 192.168.254.254 255.255.255.0 NONE +.Ed +.Pp +.Pa /etc/hostname.carp0 : +.Bd -literal -offset indent +inet 10.0.0.1 255.255.255.0 10.0.0.255 vhid 1 pass foo +.Ed +.Pp +.Pa /etc/hostname.carp1 : +.Bd -literal -offset indent +inet 192.168.0.1 255.255.255.0 192.168.0.255 vhid 2 pass bar +.Ed +.Pp +.Pa /etc/hostname.pfsync0 : +.Bd -literal -offset indent +up syncif 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 +pass on { sis0 sis1 } proto carp keep state +.Ed +.Pp +If it is preferable that one firewall handle the traffic, +the +.Ar advskew +on the backup firewall's +.Xr carp 4 +interfaces should be set to something higher than +the primary's. +For example, if firewall B is the backup, its +.Pa /etc/hostname.carp1 +would look like this: +.Bd -literal -offset indent +inet 192.168.0.1 255.255.255.0 192.168.0.255 vhid 2 pass bar \e +	advskew 100 +.Ed +.Pp +The following must also be added to +.Pa /etc/sysctl.conf :  .Bd -literal -offset indent -# ifconfig pfsync0 up -# tcpdump -s1500 -evtni pfsync0 +net.inet.carp.preempt=1  .Ed  .Sh SEE ALSO +.Xr bpf 4 ,  .Xr inet 4 ,  .Xr inet6 4 ,  .Xr netintro 4 ,  .Xr pf 4 , +.Xr hostname.if 5 , +.Xr pf.conf 5 , +.Xr protocols 5 ,  .Xr ifconfig 8 ,  .Xr tcpdump 8  .Sh HISTORY diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y index e633fcbe63a7..bcfd8a3dc957 100644 --- a/contrib/pf/pfctl/parse.y +++ b/contrib/pf/pfctl/parse.y @@ -1,8 +1,10 @@ -/*	$OpenBSD: parse.y,v 1.415 2003/09/01 15:07:40 henning Exp $	*/ +/*	$OpenBSD: parse.y,v 1.449 2004/03/20 23:20:20 david 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 @@ -48,6 +50,7 @@  #include <string.h>  #include <ctype.h>  #include <err.h> +#include <limits.h>  #include <pwd.h>  #include <grp.h>  #include <md5.h> @@ -67,6 +70,7 @@ 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;  enum {  	PFCTL_STATE_NONE, @@ -112,11 +116,20 @@ struct node_icmp {  	struct node_icmp	*tail;  }; -enum	{ PF_STATE_OPT_MAX=0, PF_STATE_OPT_TIMEOUT=1 }; +enum	{ PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, +	    PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_NODES, +	    PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT }; + +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_nodes; +		u_int8_t	 src_track; +		u_int32_t	 statelock;  		struct {  			int		number;  			u_int32_t	seconds; @@ -151,6 +164,7 @@ struct filter_opts {  #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 { @@ -211,23 +225,37 @@ struct table_opts {  	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;  int	yyerror(const char *, ...);  int	disallow_table(struct node_host *, const char *); +int	disallow_alias(struct node_host *, const char *);  int	rule_consistent(struct pf_rule *);  int	filter_consistent(struct pf_rule *);  int	nat_consistent(struct pf_rule *);  int	rdr_consistent(struct pf_rule *);  int	process_tabledef(char *, struct table_opts *);  int	yyparse(void); -void	expand_label_str(char *, const char *, const char *); -void	expand_label_if(const char *, char *, const char *); -void	expand_label_addr(const char *, char *, u_int8_t, struct node_host *); -void	expand_label_port(const char *, char *, struct node_port *); -void	expand_label_proto(const char *, char *, u_int8_t); -void	expand_label_nr(const char *, char *); -void	expand_label(char *, const char *, u_int8_t, struct node_host *, +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 *, @@ -268,8 +296,9 @@ 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); +TAILQ_HEAD(loadanchorshead, loadanchors) +    loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead); +  struct loadanchors {  	TAILQ_ENTRY(loadanchors)	 entries;  	char				*anchorname; @@ -307,7 +336,6 @@ typedef struct {  			struct peer	 src, dst;  			struct node_os	*src_os;  		}			 fromto; -		struct pf_poolhashkey	*hashkey;  		struct {  			struct node_host	*host;  			u_int8_t		 rt; @@ -320,10 +348,6 @@ typedef struct {  			struct range		 rport;  		}			*redirection;  		struct { -			int			 type; -			struct pf_poolhashkey	*key; -		}			 pooltype; -		struct {  			int			 action;  			struct node_state_opt	*options;  		}			 keep_state; @@ -331,6 +355,7 @@ typedef struct {  			u_int8_t	 log;  			u_int8_t	 quick;  		}			 logquick; +		struct pf_poolhashkey	*hashkey;  		struct node_queue	*queue;  		struct node_queue_opt	 queue_options;  		struct node_queue_bw	 queue_bwspec; @@ -340,6 +365,7 @@ typedef struct {  		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; @@ -357,6 +383,10 @@ typedef struct {  		}						\  	} while (0) +#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 LOGALL QUICK ON FROM TO FLAGS @@ -366,24 +396,24 @@ typedef struct {  %token	NOROUTE 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 +%token	REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG HOSTID  %token	ANTISPOOF FOR  %token	BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT  %token	ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT  %token	QUEUE PRIORITY QLIMIT  %token	LOAD -%token	TAGGED TAG +%token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE +%token	TAGGED TAG IFBOUND GRBOUND FLOATING STATEPOLICY  %token	<v.string>		STRING  %token	<v.i>			PORTBINARY  %type	<v.interface>		interface if_list if_item_not if_item  %type	<v.number>		number icmptype icmp6type uid gid  %type	<v.number>		tos not yesno natpass -%type	<v.i>			no dir log af fragcache -%type	<v.i>			staticport unaryop +%type	<v.i>			no dir log af fragcache sourcetrack +%type	<v.i>			unaryop statelock  %type	<v.b>			action nataction flags flag blockspec  %type	<v.range>		port rport  %type	<v.hashkey>		hashkey -%type	<v.pooltype>		pooltype  %type	<v.proto>		proto proto_list proto_item  %type	<v.icmp>		icmpspec  %type	<v.icmp>		icmp_list icmp_item @@ -416,6 +446,7 @@ typedef struct {  %type	<v.queue_opts>		queue_opts queue_opt queue_opts_l  %type	<v.scrub_opts>		scrub_opts scrub_opt scrub_opts_l  %type	<v.table_opts>		table_opts table_opt table_opts_l +%type	<v.pool_opts>		pool_opts pool_opt pool_opts_l  %%  ruleset		: /* empty */ @@ -436,26 +467,45 @@ ruleset		: /* empty */  		;  option		: SET OPTIMIZATION STRING		{ -			if (check_rulestate(PFCTL_STATE_OPTION)) +			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 TIMEOUT timeout_spec  		| SET TIMEOUT '{' timeout_list '}'  		| SET LIMIT limit_spec  		| SET LIMIT '{' limit_list '}'  		| SET LOGINTERFACE STRING		{ -			if (check_rulestate(PFCTL_STATE_OPTION)) +			if (check_rulestate(PFCTL_STATE_OPTION)) { +				free($3);  				YYERROR; -			if ((ifa_exists($3) == NULL) && strcmp($3, "none")) { +			} +			if ((ifa_exists($3, 0) == NULL) && strcmp($3, "none")) {  				yyerror("interface %s doesn't exist", $3); +				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) { +				yyerror("hostid must be non-zero"); +				YYERROR; +			} +			if (pfctl_set_hostid(pf, $3) != 0) { +				yyerror("error setting loginterface %08x", $3);  				YYERROR;  			}  		} @@ -482,12 +532,44 @@ option		: SET OPTIMIZATION STRING		{  		| SET FINGERPRINTS STRING {  			if (pf->opts & PF_OPT_VERBOSE)  				printf("fingerprints %s\n", $3); -			if (check_rulestate(PFCTL_STATE_OPTION)) +			if (check_rulestate(PFCTL_STATE_OPTION)) { +				free($3);  				YYERROR; +			}  			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; +				case PFRULE_GRBOUND: +					printf("set state-policy " +					    "group-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);  		}  		; @@ -505,19 +587,32 @@ varset		: STRING '=' string		{  				printf("%s = \"%s\"\n", $1, $3);  			if (symset($1, $3, 0) == -1)  				err(1, "cannot store variable %s", $1); +			free($1); +			free($3);  		}  		; -anchorrule	: ANCHOR string	dir interface af proto fromto { +anchorrule	: ANCHOR string	dir interface af proto fromto filter_opts {  			struct pf_rule	r; -			if (check_rulestate(PFCTL_STATE_FILTER)) +			if (check_rulestate(PFCTL_STATE_FILTER)) { +				free($2);  				YYERROR; +			}  			PREPARE_ANCHOR_RULE(r, $2);  			r.direction = $3;  			r.af = $5; +			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; +  			decide_address_family($7.src.host, &r.af);  			decide_address_family($7.dst.host, &r.af); @@ -528,10 +623,13 @@ anchorrule	: ANCHOR string	dir interface af proto fromto {  		| NATANCHOR string interface af proto fromto {  			struct pf_rule	r; -			if (check_rulestate(PFCTL_STATE_NAT)) +			if (check_rulestate(PFCTL_STATE_NAT)) { +				free($2);  				YYERROR; +			}  			PREPARE_ANCHOR_RULE(r, $2); +			free($2);  			r.action = PF_NAT;  			r.af = $4; @@ -545,10 +643,13 @@ anchorrule	: ANCHOR string	dir interface af proto fromto {  		| RDRANCHOR string interface af proto fromto {  			struct pf_rule	r; -			if (check_rulestate(PFCTL_STATE_NAT)) +			if (check_rulestate(PFCTL_STATE_NAT)) { +				free($2);  				YYERROR; +			}  			PREPARE_ANCHOR_RULE(r, $2); +			free($2);  			r.action = PF_RDR;  			r.af = $4; @@ -583,10 +684,13 @@ anchorrule	: ANCHOR string	dir interface af proto fromto {  		| BINATANCHOR string interface af proto fromto {  			struct pf_rule	r; -			if (check_rulestate(PFCTL_STATE_NAT)) +			if (check_rulestate(PFCTL_STATE_NAT)) { +				free($2);  				YYERROR; +			}  			PREPARE_ANCHOR_RULE(r, $2); +			free($2);  			r.action = PF_BINAT;  			r.af = $4;  			if ($5 != NULL) { @@ -618,18 +722,21 @@ loadrule	: LOAD ANCHOR string FROM string	{  			struct loadanchors	*loadanchor;  			t = strsep(&$3, ":"); -			if (*t == '\0' || *$3 == '\0') { +			if (*t == '\0' || $3 == NULL || *$3 == '\0') {  				yyerror("anchor '%s' invalid\n", $3); +				free(t);  				YYERROR;  			}  			if (strlen(t) >= PF_ANCHOR_NAME_SIZE) {  				yyerror("anchorname %s too long, max %u\n",  				    t, PF_ANCHOR_NAME_SIZE - 1); +				free(t);  				YYERROR;  			}  			if (strlen($3) >= PF_RULESET_NAME_SIZE) {  				yyerror("rulesetname %s too long, max %u\n",  				    $3, PF_RULESET_NAME_SIZE - 1); +				free(t);  				YYERROR;  			} @@ -668,13 +775,6 @@ scrubrule	: SCRUB dir logquick interface af proto fromto scrub_opts  				YYERROR;  			} -			if ($4) { -				if ($4->not) { -					yyerror("scrub rules do not support " -					    "'! <if>'"); -					YYERROR; -				} -			}  			r.af = $5;  			if ($8.nodf)  				r.rule_flag |= PFRULE_NODF; @@ -704,7 +804,7 @@ scrubrule	: SCRUB dir logquick interface af proto fromto scrub_opts  scrub_opts	:	{  			bzero(&scrub_opts, sizeof scrub_opts);  		} -		  scrub_opts_l +		    scrub_opts_l  			{ $$ = scrub_opts; }  		| /* empty */ {  			bzero(&scrub_opts, sizeof scrub_opts); @@ -756,8 +856,11 @@ scrub_opt	: NODF	{  			scrub_opts.fragcache = $1;  		}  		| REASSEMBLE STRING { -			if (strcasecmp($2, "tcp") != 0) +			if (strcasecmp($2, "tcp") != 0) { +				free($2);  				YYERROR; +			} +			free($2);  			if (scrub_opts.reassemble_tcp) {  				yyerror("reassemble tcp cannot be respecified");  				YYERROR; @@ -806,10 +909,11 @@ antispoof	: ANTISPOOF logquick antispoof_ifspc af antispoof_opts {  					YYERROR;  				}  				j->not = 1; -				h = ifa_lookup(j->ifname, PFCTL_IFLOOKUP_NET); +				h = ifa_lookup(j->ifname, PFI_AFLAG_NETWORK); -				expand_rule(&r, j, NULL, NULL, NULL, h, NULL, -				    NULL, NULL, NULL, NULL, 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)); @@ -821,11 +925,11 @@ antispoof	: ANTISPOOF logquick antispoof_ifspc af antispoof_opts {  					r.af = $4;  					if (rule_label(&r, $5.label))  						YYERROR; -					h = ifa_lookup(i->ifname, -					    PFCTL_IFLOOKUP_HOST); -					expand_rule(&r, NULL, NULL, NULL, NULL, -					    h, NULL, NULL, NULL, NULL, NULL, -					    NULL); +					h = ifa_lookup(i->ifname, 0); +					if (h != NULL) +						expand_rule(&r, NULL, NULL, +						    NULL, NULL, h, NULL, NULL, +						    NULL, NULL, NULL, NULL);  				}  			}  			free($5.label); @@ -845,7 +949,7 @@ antispoof_iflst	: if_item			{ $$ = $1; }  		;  antispoof_opts	:	{ bzero(&antispoof_opts, sizeof antispoof_opts); } -		  antispoof_opts_l +		    antispoof_opts_l  			{ $$ = antispoof_opts; }  		| /* empty */	{  			bzero(&antispoof_opts, sizeof antispoof_opts); @@ -868,6 +972,7 @@ antispoof_opt	: label	{  not		: '!'		{ $$ = 1; }  		| /* empty */	{ $$ = 0; } +		;  tabledef	: TABLE '<' STRING '>' table_opts {  			struct node_host	 *h, *nh; @@ -876,11 +981,15 @@ tabledef	: TABLE '<' STRING '>' table_opts {  			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)) +				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) @@ -890,7 +999,7 @@ tabledef	: TABLE '<' STRING '>' table_opts {  					free(h);  				}  				nti = SIMPLEQ_NEXT(ti, entries); -				free (ti); +				free(ti);  			}  		}  		; @@ -899,7 +1008,7 @@ table_opts	:	{  			bzero(&table_opts, sizeof table_opts);  			SIMPLEQ_INIT(&table_opts.init_nodes);  		} -		   table_opts_l +		    table_opts_l  			{ $$ = table_opts; }  		| /* empty */  			{ @@ -918,8 +1027,11 @@ table_opt	: STRING		{  				table_opts.flags |= PFR_TFLAG_CONST;  			else if (!strcmp($1, "persist"))  				table_opts.flags |= PFR_TFLAG_PERSIST; -			else +			else { +				free($1);  				YYERROR; +			} +			free($1);  		}  		| '{' '}'		{ table_opts.init_addr = 1; }  		| '{' host_list '}'	{ @@ -927,7 +1039,7 @@ table_opt	: STRING		{  			struct node_tinit	*ti;  			for (n = $2; n != NULL; n = n->next) { -				switch(n->addr.type) { +				switch (n->addr.type) {  				case PF_ADDR_ADDRMASK:  					continue; /* ok */  				case PF_ADDR_DYNIFTL: @@ -993,8 +1105,10 @@ altqif		: ALTQ interface queue_opts QUEUE qassign {  queuespec	: QUEUE STRING interface queue_opts qassign {  			struct pf_altq	a; -			if (check_rulestate(PFCTL_STATE_QUEUE)) +			if (check_rulestate(PFCTL_STATE_QUEUE)) { +				free($2);  				YYERROR; +			}  			memset(&a, 0, sizeof(a)); @@ -1002,8 +1116,10 @@ queuespec	: QUEUE STRING interface queue_opts qassign {  			    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; @@ -1030,7 +1146,7 @@ queue_opts	:	{  			queue_opts.scheduler.qtype = ALTQT_NONE;  			queue_opts.queue_bwspec.bw_percent = 100;  		} -		  queue_opts_l +		    queue_opts_l  			{ $$ = queue_opts; }  		| /* empty */ {  			bzero(&queue_opts, sizeof queue_opts); @@ -1120,17 +1236,21 @@ bandwidth	: STRING {  					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;  		} +		;  scheduler	: CBQ				{  			$$.qtype = ALTQT_CBQ; @@ -1176,8 +1296,10 @@ cbqflags_item	: STRING	{  				$$ = CBQCLF_RIO;  			else {  				yyerror("unknown cbq flag \"%s\"", $1); +				free($1);  				YYERROR;  			} +			free($1);  		}  		; @@ -1196,8 +1318,10 @@ priqflags_item	: STRING	{  				$$ = PRCF_RIO;  			else {  				yyerror("unknown priq flag \"%s\"", $1); +				free($1);  				YYERROR;  			} +			free($1);  		}  		; @@ -1205,7 +1329,7 @@ hfsc_opts	:	{  				bzero(&hfsc_opts,  				    sizeof(struct node_hfsc_opts));  			} -		  hfscopts_list				{ +		    hfscopts_list				{  			$$ = hfsc_opts;  		}  		; @@ -1279,8 +1403,10 @@ hfscopts_item	: LINKSHARE bandwidth				{  				hfsc_opts.flags |= HFCF_RIO;  			else {  				yyerror("unknown hfsc flag \"%s\"", $1); +				free($1);  				YYERROR;  			} +			free($1);  		}  		; @@ -1303,22 +1429,26 @@ qassign_item	: STRING			{  				err(1, "qassign_item: calloc");  			if (strlcpy($$->queue, $1, sizeof($$->queue)) >=  			    sizeof($$->queue)) { -				free($$);  				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 +		    filter_opts  		{  			struct pf_rule		 r;  			struct node_state_opt	*o;  			struct node_proto	*proto; +			int			 srctrack = 0; +			int			 statelock = 0;  			if (check_rulestate(PFCTL_STATE_FILTER))  				YYERROR; @@ -1349,14 +1479,14 @@ pfrule		: action dir logquick interface route af proto fromto  			r.af = $6;  			if ($9.tag)  				if (strlcpy(r.tagname, $9.tag, -				    PF_TAG_NAME_SIZE) > PF_TAG_NAME_SIZE) { +				    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) { +				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {  					yyerror("tag too long, max %u chars",  					    PF_TAG_NAME_SIZE - 1);  					YYERROR; @@ -1386,7 +1516,7 @@ pfrule		: action dir logquick interface route af proto fromto  				if (($9.flags.b1 & parse_flags("S")) == 0 &&  				    $8.src_os) {  					yyerror("OS fingerprinting requires " -					     "the SYN TCP flag (flags S/SA)"); +					    "the SYN TCP flag (flags S/SA)");  					YYERROR;  				}  #endif @@ -1407,6 +1537,65 @@ pfrule		: action dir logquick interface route af proto fromto  					}  					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; +					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_nodes == 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_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_TIMEOUT:  					if (r.timeout[o->data.timeout.number]) {  						yyerror("state timeout %s " @@ -1421,6 +1610,20 @@ pfrule		: action dir logquick interface route af proto fromto  				o = o->next;  				free(p);  			} +			if (srctrack) { +				if (srctrack == PF_SRCTRACK_GLOBAL && +				    r.max_src_nodes) { +					yyerror("'max-src-nodes' is " +					    "incompatible with " +					    "'source-track global'"); +					YYERROR; +				} +				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; @@ -1449,17 +1652,24 @@ pfrule		: action dir logquick interface route af proto fromto  					    "matching address family found.");  					YYERROR;  				} -				if (r.rpool.opts == PF_POOL_NONE && ( -				    $5.host->next != NULL || -				    $5.host->addr.type == PF_ADDR_TABLE)) -					r.rpool.opts = PF_POOL_ROUNDROBIN; -				if (r.rpool.opts != PF_POOL_ROUNDROBIN) -					if (disallow_table($5.host, "tables " -					    "are only supported in round-robin " -					    "routing pools")) -						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 != +					if ((r.rpool.opts & PF_POOL_TYPEMASK) !=  					    PF_POOL_ROUNDROBIN) {  						yyerror("r.rpool.opts must "  						    "be PF_POOL_ROUNDROBIN"); @@ -1493,7 +1703,7 @@ pfrule		: action dir logquick interface route af proto fromto  		;  filter_opts	:	{ bzero(&filter_opts, sizeof filter_opts); } -		  filter_opts_l +		    filter_opts_l  			{ $$ = filter_opts; }  		| /* empty */	{  			bzero(&filter_opts, sizeof filter_opts); @@ -1620,22 +1830,32 @@ blockspec	: /* empty */		{  		}  		| RETURNICMP '(' STRING ')'	{  			$$.b2 = PFRULE_RETURNICMP; -			if (!($$.w = parseicmpspec($3, AF_INET))) +			if (!($$.w = parseicmpspec($3, AF_INET))) { +				free($3);  				YYERROR; +			} +			free($3);  			$$.w2 = returnicmp6default;  		}  		| RETURNICMP6 '(' STRING ')'	{  			$$.b2 = PFRULE_RETURNICMP;  			$$.w = returnicmpdefault; -			if (!($$.w2 = parseicmpspec($3, AF_INET6))) +			if (!($$.w2 = parseicmpspec($3, AF_INET6))) { +				free($3);  				YYERROR; +			} +			free($3);  		}  		| RETURNICMP '(' STRING comma STRING ')' {  			$$.b2 = PFRULE_RETURNICMP; -			if (!($$.w = parseicmpspec($3, AF_INET))) -				YYERROR; -			if (!($$.w2 = parseicmpspec($5, AF_INET6))) +			if (!($$.w = parseicmpspec($3, AF_INET)) || +			    !($$.w2 = parseicmpspec($5, AF_INET6))) { +				free($3); +				free($5);  				YYERROR; +			} +			free($3); +			free($5);  		}  		| RETURN {  			$$.b2 = PFRULE_RETURN; @@ -1679,8 +1899,9 @@ if_item_not	: not if_item			{ $$ = $2; $$->not = $1; }  if_item		: STRING			{  			struct node_host	*n; -			if ((n = ifa_exists($1)) == NULL) { +			if ((n = ifa_exists($1, 1)) == NULL) {  				yyerror("unknown interface %s", $1); +				free($1);  				YYERROR;  			}  			$$ = calloc(1, sizeof(struct node_if)); @@ -1688,10 +1909,12 @@ if_item		: STRING			{  				err(1, "if_item: calloc");  			if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >=  			    sizeof($$->ifname)) { +				free($1);  				free($$);  				yyerror("interface name too long");  				YYERROR;  			} +			free($1);  			$$->ifa_flags = n->ifa_flags;  			$$->not = 0;  			$$->next = NULL; @@ -1702,6 +1925,7 @@ if_item		: STRING			{  af		: /* empty */			{ $$ = 0; }  		| INET				{ $$ = AF_INET; }  		| INET6				{ $$ = AF_INET6; } +		;  proto		: /* empty */			{ $$ = NULL; }  		| PROTO proto_item		{ $$ = $2; } @@ -1723,6 +1947,7 @@ proto_item	: STRING			{  			if (atoul($1, &ulval) == 0) {  				if (ulval > 255) {  					yyerror("protocol outside range"); +					free($1);  					YYERROR;  				}  				pr = (u_int8_t)ulval; @@ -1732,10 +1957,12 @@ proto_item	: STRING			{  				p = getprotobyname($1);  				if (p == NULL) {  					yyerror("unknown protocol %s", $1); +					free($1);  					YYERROR;  				}  				pr = p->p_proto;  			} +			free($1);  			if (pr == 0) {  				yyerror("proto 0 cannot be used");  				YYERROR; @@ -1856,9 +2083,11 @@ xhost		: not host			{  host		: STRING			{  			if (($$ = host($1)) == NULL)	{  				/* error. "any" is handled elsewhere */ +				free($1);  				yyerror("could not parse host specification");  				YYERROR;  			} +			free($1);  		}  		| STRING '/' number		{ @@ -1866,6 +2095,7 @@ host		: STRING			{  			if (asprintf(&buf, "%s/%u", $1, $3) == -1)  				err(1, "host: asprintf"); +			free($1);  			if (($$ = host(buf)) == NULL)	{  				/* error. "any" is handled elsewhere */  				free(buf); @@ -1884,7 +2114,8 @@ host		: STRING			{  		}  		| '<' STRING '>'	{  			if (strlen($2) >= PF_TABLE_NAME_SIZE) { -				yyerror("table name '%s' too long"); +				yyerror("table name '%s' too long", $2); +				free($2);  				YYERROR;  			}  			$$ = calloc(1, sizeof(struct node_host)); @@ -1895,6 +2126,7 @@ host		: STRING			{  			    sizeof($$->addr.v.tblname)) >=  			    sizeof($$->addr.v.tblname))  				errx(1, "host: strlcpy"); +			free($2);  			$$->next = NULL;  			$$->tail = $$;  		} @@ -1905,15 +2137,45 @@ number		: STRING			{  			if (atoul($1, &ulval) == -1) {  				yyerror("%s is not a number", $1); +				free($1);  				YYERROR;  			} else  				$$ = ulval; +			free($1);  		}  		;  dynaddr		: '(' STRING ')'		{ -			if (ifa_exists($2) == NULL) { +			int	 flags = 0; +			char	*p, *op; + +			op = $2; +			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; +			} +			if (ifa_exists($2, 1) == NULL && strcmp($2, "self")) {  				yyerror("interface %s does not exist", $2); +				free(op);  				YYERROR;  			}  			$$ = calloc(1, sizeof(struct node_host)); @@ -1922,13 +2184,16 @@ dynaddr		: '(' STRING ')'		{  			$$->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 = $$;  		} @@ -1999,6 +2264,7 @@ port		: STRING			{  			if (p == NULL) {  				if (atoul($1, &ulval) == 0) {  					if (ulval > 65535) { +						free($1);  						yyerror("illegal port value %d",  						    ulval);  						YYERROR; @@ -2010,6 +2276,7 @@ port		: STRING			{  						s = getservbyname($1, "udp");  					if (s == NULL) {  						yyerror("unknown port %s", $1); +						free($1);  						YYERROR;  					}  					$$.a = s->s_port; @@ -2021,12 +2288,15 @@ port		: STRING			{  				*p++ = 0;  				if ((port[0] = getservice($1)) == -1 || -				    (port[1] = getservice(p)) == -1) +				    (port[1] = getservice(p)) == -1) { +					free($1);  					YYERROR; +				}  				$$.a = port[0];  				$$.b = port[1];  				$$.t = PF_OP_RRG;  			} +			free($1);  		}  		; @@ -2095,17 +2365,20 @@ uid		: STRING			{  					if ((pw = getpwnam($1)) == NULL) {  						yyerror("unknown user %s", $1); +						free($1);  						YYERROR;  					}  					$$ = pw->pw_uid;  				}  			} else {  				if (ulval >= UID_MAX) { +					free($1);  					yyerror("illegal uid value %lu", ulval);  					YYERROR;  				}  				$$ = ulval;  			} +			free($1);  		}  		; @@ -2174,6 +2447,7 @@ gid		: STRING			{  					if ((grp = getgrnam($1)) == NULL) {  						yyerror("unknown group %s", $1); +						free($1);  						YYERROR;  					}  					$$ = grp->gr_gid; @@ -2181,10 +2455,12 @@ gid		: STRING			{  			} else {  				if (ulval >= GID_MAX) {  					yyerror("illegal gid value %lu", ulval); +					free($1);  					YYERROR;  				}  				$$ = ulval;  			} +			free($1);  		}  		; @@ -2193,8 +2469,10 @@ flag		: STRING			{  			if ((f = parse_flags($1)) < 0) {  				yyerror("bad flags %s", $1); +				free($1);  				YYERROR;  			} +			free($1);  			$$.b1 = f;  		}  		; @@ -2241,6 +2519,7 @@ icmp_item	: icmptype		{  			if (atoul($3, &ulval) == 0) {  				if (ulval > 255) { +					free($3);  					yyerror("illegal icmp-code %d", ulval);  					YYERROR;  				} @@ -2248,10 +2527,12 @@ icmp_item	: icmptype		{  				if ((p = geticmpcodebyname($1-1, $3,  				    AF_INET)) == NULL) {  					yyerror("unknown icmp-code %s", $3); +					free($3);  					YYERROR;  				}  				ulval = p->code;  			} +			free($3);  			$$ = calloc(1, sizeof(struct node_icmp));  			if ($$ == NULL)  				err(1, "icmp_item: calloc"); @@ -2281,16 +2562,19 @@ icmp6_item	: icmp6type		{  				if (ulval > 255) {  					yyerror("illegal icmp6-code %ld",  					    ulval); +					free($3);  					YYERROR;  				}  			} else {  				if ((p = geticmpcodebyname($1-1, $3,  				    AF_INET6)) == NULL) {  					yyerror("unknown icmp6-code %s", $3); +					free($3);  					YYERROR;  				}  				ulval = p->code;  			} +			free($3);  			$$ = calloc(1, sizeof(struct node_icmp));  			if ($$ == NULL)  				err(1, "icmp_item: calloc"); @@ -2309,6 +2593,7 @@ icmptype	: STRING			{  			if (atoul($1, &ulval) == 0) {  				if (ulval > 255) {  					yyerror("illegal icmp-type %d", ulval); +					free($1);  					YYERROR;  				}  				$$ = ulval + 1; @@ -2316,10 +2601,12 @@ icmptype	: STRING			{  				if ((p = geticmptypebyname($1, AF_INET)) ==  				    NULL) {  					yyerror("unknown icmp-type %s", $1); +					free($1);  					YYERROR;  				}  				$$ = p->type + 1;  			} +			free($1);  		}  		; @@ -2330,6 +2617,7 @@ icmp6type	: STRING			{  			if (atoul($1, &ulval) == 0) {  				if (ulval > 255) {  					yyerror("illegal icmp6-type %d", ulval); +					free($1);  					YYERROR;  				}  				$$ = ulval + 1; @@ -2337,10 +2625,12 @@ icmp6type	: STRING			{  				if ((p = geticmptypebyname($1, AF_INET6)) ==  				    NULL) {  					yyerror("unknown icmp6-type %s", $1); +					free($1);  					YYERROR;  				}  				$$ = p->type + 1;  			} +			free($1);  		}  		; @@ -2357,8 +2647,26 @@ tos		: TOS STRING			{  				$$ = strtoul($2, NULL, 10);  			if (!$$ || $$ > 255) {  				yyerror("illegal tos value %s", $2); +				free($2);  				YYERROR;  			} +			free($2); +		} +		; + +sourcetrack	: SOURCETRACK		{ $$ = PF_SRCTRACK; } +		| SOURCETRACK GLOBAL	{ $$ = PF_SRCTRACK_GLOBAL; } +		| SOURCETRACK RULE	{ $$ = PF_SRCTRACK_RULE; } +		; + +statelock	: IFBOUND { +			$$ = PFRULE_IFBOUND; +		} +		| GRBOUND { +			$$ = PFRULE_GRBOUND; +		} +		| FLOATING { +			$$ = 0;  		}  		; @@ -2366,7 +2674,7 @@ keep		: KEEP STATE state_opt_spec	{  			$$.action = PF_STATE_NORMAL;  			$$.options = $3;  		} -		| MODULATE STATE state_opt_spec	{ +		| MODULATE STATE state_opt_spec {  			$$.action = PF_STATE_MODULATE;  			$$.options = $3;  		} @@ -2397,6 +2705,50 @@ state_opt_item	: MAXIMUM number		{  			$$->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			{ +			$$ = 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 = $$; +		} +		| MAXSRCNODES number			{ +			$$ = 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 = $$; +		}  		| STRING number			{  			int	i; @@ -2405,12 +2757,15 @@ state_opt_item	: MAXIMUM number		{  				;	/* 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"); @@ -2423,23 +2778,19 @@ state_opt_item	: MAXIMUM number		{  		;  label		: LABEL STRING			{ -			if (($$ = strdup($2)) == NULL) -				err(1, "rule label strdup() failed"); +			$$ = $2;  		}  		;  qname		: QUEUE STRING				{ -			if (($$.qname = strdup($2)) == NULL) -				err(1, "qname strdup() failed"); +			$$.qname = $2;  		}  		| QUEUE '(' STRING ')'			{ -			if (($$.qname = strdup($3)) == NULL) -				err(1, "qname strdup() failed"); +			$$.qname = $3;  		}  		| QUEUE '(' STRING comma STRING ')'	{ -			if (($$.qname = strdup($3)) == NULL || -			    ($$.pqname = strdup($5)) == NULL) -				err(1, "qname strdup() failed"); +			$$.qname = $3; +			$$.pqname = $5;  		}  		; @@ -2451,24 +2802,31 @@ rport		: STRING			{  			char	*p = strchr($1, ':');  			if (p == NULL) { -				if (($$.a = getservice($1)) == -1) +				if (($$.a = getservice($1)) == -1) { +					free($1);  					YYERROR; +				}  				$$.b = $$.t = 0;  			} else if (!strcmp(p+1, "*")) {  				*p = 0; -				if (($$.a = getservice($1)) == -1) +				if (($$.a = getservice($1)) == -1) { +					free($1);  					YYERROR; +				}  				$$.b = 0;  				$$.t = 1;  			} else {  				*p++ = 0;  				if (($$.a = getservice($1)) == -1 || -				    ($$.b = getservice(p)) == -1) +				    ($$.b = getservice(p)) == -1) { +					free($1);  					YYERROR; +				}  				if ($$.a == $$.b)  					$$.b = 0;  				$$.t = 0;  			} +			free($1);  		}  		; @@ -2515,6 +2873,7 @@ hashkey		: /* empty */  		{  			if (!strncmp($1, "0x", 2)) {  				if (strlen($1) != 34) { +					free($1);  					yyerror("hex key must be 128 bits "  						"(32 hex digits) long");  					YYERROR; @@ -2527,6 +2886,7 @@ hashkey		: /* empty */  				    &$$->key32[0], &$$->key32[1],  				    &$$->key32[2], &$$->key32[3]) != 4) {  					free($$); +					free($1);  					yyerror("invalid hex key");  					YYERROR;  				} @@ -2545,38 +2905,67 @@ hashkey		: /* empty */  				HTONL($$->key32[2]);  				HTONL($$->key32[3]);  			} +			free($1);  		}  		; -pooltype	: /* empty */ -		{ -			$$.type = PF_POOL_NONE; -			$$.key = NULL; +pool_opts	:	{ bzero(&pool_opts, sizeof pool_opts); } +		    pool_opts_l +			{ $$ = pool_opts; } +		| /* empty */	{ +			bzero(&pool_opts, sizeof pool_opts); +			$$ = pool_opts;  		} -		| BITMASK -		{ -			$$.type = PF_POOL_BITMASK; -			$$.key = NULL; +		; + +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 -		{ -			$$.type = PF_POOL_RANDOM; -			$$.key = NULL; +		| RANDOM	{ +			if (pool_opts.type) { +				yyerror("pool type cannot be redefined"); +				YYERROR; +			} +			pool_opts.type = PF_POOL_RANDOM;  		} -		| SOURCEHASH hashkey -		{ -			$$.type = PF_POOL_SRCHASH; -			$$.key = $2; +		| SOURCEHASH hashkey { +			if (pool_opts.type) { +				yyerror("pool type cannot be redefined"); +				YYERROR; +			} +			pool_opts.type = PF_POOL_SRCHASH; +			pool_opts.key = $2;  		} -		| ROUNDROBIN -		{ -			$$.type = PF_POOL_ROUNDROBIN; -			$$.key = NULL; +		| 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;  		} -		; - -staticport	: /* empty */			{ $$ = 0; } -		| STATICPORT			{ $$ = 1; }  		;  redirection	: /* empty */			{ $$ = NULL; } @@ -2618,8 +3007,7 @@ nataction	: no NAT natpass {  		}  		; -natrule		: nataction interface af proto fromto tag redirpool pooltype -		  staticport +natrule		: nataction interface af proto fromto tag redirpool pool_opts  		{  			struct pf_rule	r; @@ -2642,7 +3030,7 @@ natrule		: nataction interface af proto fromto tag redirpool pooltype  			}  			if ($6 != NULL) -				if (strlcpy(r.tagname, $6, PF_TAG_NAME_SIZE) > +				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); @@ -2678,14 +3066,17 @@ natrule		: nataction interface af proto fromto tag redirpool pooltype  					    $5.dst.port != NULL) {  						r.rpool.proxy_port[1] =  						    ntohs($7->rport.a) + -						    (ntohs($5.dst.port->port[1]) - -						    ntohs($5.dst.port->port[0])); +						    (ntohs( +						    $5.dst.port->port[1]) - +						    ntohs( +						    $5.dst.port->port[0]));  					} else  						r.rpool.proxy_port[1] =  						    ntohs($7->rport.b);  					break;  				case PF_NAT: -					r.rpool.proxy_port[1] = ntohs($7->rport.b); +					r.rpool.proxy_port[1] = +					    ntohs($7->rport.b);  					if (!r.rpool.proxy_port[0] &&  					    !r.rpool.proxy_port[1]) {  						r.rpool.proxy_port[0] = @@ -2701,30 +3092,31 @@ natrule		: nataction interface af proto fromto tag redirpool pooltype  				}  				r.rpool.opts = $8.type; -				if (r.rpool.opts == PF_POOL_NONE) +				if ((r.rpool.opts & PF_POOL_TYPEMASK) == +				    PF_POOL_NONE && ($7->host->next != NULL || +				    $7->host->addr.type == PF_ADDR_TABLE || +				    DYNIF_MULTIADDR($7->host->addr)))  					r.rpool.opts = PF_POOL_ROUNDROBIN; -				if (r.rpool.opts != PF_POOL_ROUNDROBIN) -					if (disallow_table($7->host, "tables " -					    "are only supported in round-robin " -					    "redirection pools")) -						YYERROR; -				if ($7->host->next) { -					if (r.rpool.opts != +				if ((r.rpool.opts & PF_POOL_TYPEMASK) != +				    PF_POOL_ROUNDROBIN && +				    disallow_table($7->host, "tables are only " +				    "supported in round-robin redirection " +				    "pools")) +					YYERROR; +				if ((r.rpool.opts & PF_POOL_TYPEMASK) != +				    PF_POOL_ROUNDROBIN && +				    disallow_alias($7->host, "interface (%s) " +				    "is only supported in round-robin " +				    "redirection pools")) +					YYERROR; +				if ($7->host->next != NULL) { +					if ((r.rpool.opts & PF_POOL_TYPEMASK) !=  					    PF_POOL_ROUNDROBIN) {  						yyerror("only round-robin "  						    "valid for multiple "  						    "redirection addresses");  						YYERROR;  					} -				} else { -					if ((r.af == AF_INET && -					    unmask(&$7->host->addr.v.a.mask, -					    r.af) == 32) || -					    (r.af == AF_INET6 && -					    unmask(&$7->host->addr.v.a.mask, -					    r.af) == 128)) { -						r.rpool.opts = PF_POOL_NONE; -					}  				}  			} @@ -2732,7 +3124,10 @@ natrule		: nataction interface af proto fromto tag redirpool pooltype  				memcpy(&r.rpool.key, $8.key,  				    sizeof(struct pf_poolhashkey)); -			if ($9 != NULL) { +			 if ($8.opts) +				r.rpool.opts |= $8.opts; + +			if ($8.staticport) {  				if (r.action != PF_NAT) {  					yyerror("the 'static-port' option is "  					    "only valid with nat rules"); @@ -2759,7 +3154,7 @@ natrule		: nataction interface af proto fromto tag redirpool pooltype  		;  binatrule	: no BINAT natpass interface af proto FROM host TO ipspec tag -		  redirection +		    redirection  		{  			struct pf_rule		binat;  			struct pf_pooladdr	*pa; @@ -2790,11 +3185,12 @@ binatrule	: no BINAT natpass interface af proto FROM host TO ipspec tag  			if ($4 != NULL) {  				memcpy(binat.ifname, $4->ifname,  				    sizeof(binat.ifname)); +				binat.ifnot = $4->not;  				free($4);  			}  			if ($11 != NULL)  				if (strlcpy(binat.tagname, $11, -				    PF_TAG_NAME_SIZE) > PF_TAG_NAME_SIZE) { +				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {  					yyerror("tag too long, max %u chars",  					    PF_TAG_NAME_SIZE - 1);  					YYERROR; @@ -2808,10 +3204,18 @@ binatrule	: no BINAT natpass interface af proto FROM host TO ipspec tag  			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 ($12 != NULL && $12->host != NULL && disallow_table(  			    $12->host, "invalid use of table <%s> as the "  			    "redirect address of a binat rule"))  				YYERROR; +			if ($12 != NULL && $12->host != NULL && disallow_alias( +			    $12->host, "invalid use of interface (%s) as the " +			    "redirect address of a binat rule")) +				YYERROR;  			if ($8 != NULL) {  				if ($8->next) { @@ -2898,18 +3302,18 @@ binatrule	: no BINAT natpass interface af proto FROM host TO ipspec tag  tag		: /* empty */		{ $$ = NULL; }  		| TAG STRING		{ $$ = $2; } +		;  route_host	: STRING			{ -			struct node_host	*n; -  			$$ = calloc(1, sizeof(struct node_host));  			if ($$ == NULL)  				err(1, "route_host: calloc"); -			if (($$->ifname = strdup($1)) == NULL) -				err(1, "routeto: strdup"); -			if ((n = ifa_exists($$->ifname)) == NULL) { +			$$->ifname = $1; +			if (ifa_exists($$->ifname, 0) == NULL) {  				yyerror("routeto: unknown interface %s",  				    $$->ifname); +				free($1); +				free($$);  				YYERROR;  			}  			set_ipmask($$, 128); @@ -2917,12 +3321,9 @@ route_host	: STRING			{  			$$->tail = $$;  		}  		| '(' STRING host ')'		{ -			struct node_host	*n; -  			$$ = $3; -			if (($$->ifname = strdup($2)) == NULL) -				err(1, "routeto: strdup"); -			if ((n = ifa_exists($$->ifname)) == NULL) { +			$$->ifname = $2; +			if (ifa_exists($$->ifname, 0) == NULL) {  				yyerror("routeto: unknown interface %s",  				    $$->ifname);  				YYERROR; @@ -2959,24 +3360,24 @@ route		: /* empty */			{  			$$.rt = PF_FASTROUTE;  			$$.pool_opts = 0;  		} -		| ROUTETO routespec pooltype { +		| ROUTETO routespec pool_opts {  			$$.host = $2;  			$$.rt = PF_ROUTETO; -			$$.pool_opts = $3.type; +			$$.pool_opts = $3.type | $3.opts;  			if ($3.key != NULL)  				$$.key = $3.key;  		} -		| REPLYTO routespec pooltype { +		| REPLYTO routespec pool_opts {  			$$.host = $2;  			$$.rt = PF_REPLYTO; -			$$.pool_opts = $3.type; +			$$.pool_opts = $3.type | $3.opts;  			if ($3.key != NULL)  				$$.key = $3.key;  		} -		| DUPTO routespec pooltype { +		| DUPTO routespec pool_opts {  			$$.host = $2;  			$$.rt = PF_DUPTO; -			$$.pool_opts = $3.type; +			$$.pool_opts = $3.type | $3.opts;  			if ($3.key != NULL)  				$$.key = $3.key;  		} @@ -2984,12 +3385,16 @@ route		: /* empty */			{  timeout_spec	: STRING number  		{ -			if (check_rulestate(PFCTL_STATE_OPTION)) +			if (check_rulestate(PFCTL_STATE_OPTION)) { +				free($1);  				YYERROR; +			}  			if (pfctl_set_timeout(pf, $1, $2, 0) != 0) {  				yyerror("unknown timeout %s", $1); +				free($1);  				YYERROR;  			} +			free($1);  		}  		; @@ -2999,13 +3404,18 @@ timeout_list	: timeout_list comma timeout_spec  limit_spec	: STRING number  		{ -			if (check_rulestate(PFCTL_STATE_OPTION)) +			if (check_rulestate(PFCTL_STATE_OPTION)) { +				free($1);  				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  		| limit_spec @@ -3019,9 +3429,13 @@ yesno		: NO			{ $$ = 0; }  		| STRING		{  			if (!strcmp($1, "yes"))  				$$ = 1; -			else +			else { +				free($1);  				YYERROR; +			} +			free($1);  		} +		;  unaryop		: '='		{ $$ = PF_OP_EQ; }  		| '!' '='	{ $$ = PF_OP_NE; } @@ -3060,6 +3474,17 @@ disallow_table(struct node_host *h, const char *fmt)  }  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	problems = 0; @@ -3096,10 +3521,6 @@ filter_consistent(struct pf_rule *r)  		yyerror("port only applies to tcp/udp");  		problems++;  	} -	if (r->src.port_op == PF_OP_RRG || r->dst.port_op == PF_OP_RRG) { -		yyerror("the ':' port operator only applies to rdr"); -		problems++; -	}  	if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 &&  	    (r->type || r->code)) {  		yyerror("icmp-type/code only applies to icmp"); @@ -3116,22 +3537,10 @@ filter_consistent(struct pf_rule *r)  		    r->af == AF_INET ? "inet" : "inet6");  		problems++;  	} -	if ((r->keep_state == PF_STATE_MODULATE || r->keep_state == -	    PF_STATE_SYNPROXY) && r->proto && r->proto != IPPROTO_TCP) { -		yyerror("modulate/synproxy state can only be applied to " -		    "TCP rules"); -		problems++; -	}  	if (r->allow_opts && r->action != PF_PASS) {  		yyerror("allow-opts can only be specified for pass rules");  		problems++;  	} -	if (!r->af && (r->src.addr.type == PF_ADDR_DYNIFTL || -	    r->dst.addr.type == PF_ADDR_DYNIFTL)) { -		yyerror("dynamic addresses require address family " -		    "(inet/inet6)"); -		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"); @@ -3141,12 +3550,16 @@ filter_consistent(struct pf_rule *r)  		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->tagname[0] || r->match_tagname[0]) && !r->keep_state && -	    r->action == PF_PASS) { +	    r->action == PF_PASS && !r->anchorname[0]) {  		yyerror("tags cannot be used without keep state");  		problems++;  	} @@ -3156,31 +3569,13 @@ filter_consistent(struct pf_rule *r)  int  nat_consistent(struct pf_rule *r)  { -	int			 problems = 0; -	struct pf_pooladdr	*pa; - -	if (r->src.port_op == PF_OP_RRG || r->dst.port_op == PF_OP_RRG) { -		yyerror("the ':' port operator only applies to rdr"); -		problems++; -	} -	if (!r->af) { -		TAILQ_FOREACH(pa, &r->rpool.list, entries) { -			if (pa->addr.type == PF_ADDR_DYNIFTL) { -				yyerror("dynamic addresses require " -				    "address family (inet/inet6)"); -				problems++; -				break; -			} -		} -	} -	return (-problems); +	return (0);	/* yeah! */  }  int  rdr_consistent(struct pf_rule *r)  {  	int			 problems = 0; -	struct pf_pooladdr	*pa;  	if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) {  		if (r->src.port_op) { @@ -3201,28 +3596,6 @@ rdr_consistent(struct pf_rule *r)  		yyerror("invalid port operator for rdr destination port");  		problems++;  	} -	if (r->src.port_op == PF_OP_RRG) { -		yyerror("the ':' port operator only applies to rdr " -		    "destination port"); -		problems++; -	} -	if (!r->af) { -		if (r->src.addr.type == PF_ADDR_DYNIFTL || -		    r->dst.addr.type == PF_ADDR_DYNIFTL) { -			yyerror("dynamic addresses require address family " -			    "(inet/inet6)"); -			problems++; -		} else { -			TAILQ_FOREACH(pa, &r->rpool.list, entries) { -				if (pa->addr.type == PF_ADDR_DYNIFTL) { -					yyerror("dynamic addresses require " -					    "address family (inet/inet6)"); -					problems++; -					break; -				} -			} -		} -	}  	return (-problems);  } @@ -3306,38 +3679,41 @@ struct keywords {  	} while (0)  void -expand_label_str(char *label, const char *srch, const char *repl) +expand_label_str(char *label, size_t len, const char *srch, const char *repl)  { -	char tmp[PF_RULE_LABEL_SIZE] = ""; +	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, sizeof(tmp)) >= sizeof(tmp)) || -		    (strlcat(tmp, repl, sizeof(tmp)) >= sizeof(tmp))) -			err(1, "expand_label: label too long"); +		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, sizeof(tmp)) >= sizeof(tmp)) -		err(1, "expand_label: label too long"); -	strlcpy(label, tmp, PF_RULE_LABEL_SIZE);	/* always fits */ +	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, const char *ifname) +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, name, "any"); +			expand_label_str(label, len, name, "any");  		else -			expand_label_str(label, name, ifname); +			expand_label_str(label, len, name, ifname);  	}  }  void -expand_label_addr(const char *name, char *label, sa_family_t af, +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]; @@ -3369,7 +3745,7 @@ expand_label_addr(const char *name, char *label, sa_family_t af,  					if ((af == AF_INET && bits < 32) ||  					    (af == AF_INET6 && bits < 128))  						snprintf(tmp, sizeof(tmp), -						   "%s/%d", a, bits); +						    "%s/%d", a, bits);  					else  						snprintf(tmp, sizeof(tmp),  						    "%s", a); @@ -3383,14 +3759,15 @@ expand_label_addr(const char *name, char *label, sa_family_t af,  		if (h->not) {  			snprintf(tmp_not, sizeof(tmp_not), "! %s", tmp); -			expand_label_str(label, name, tmp_not); +			expand_label_str(label, len, name, tmp_not);  		} else -			expand_label_str(label, name, tmp); +			expand_label_str(label, len, name, tmp);  	}  }  void -expand_label_port(const char *name, char *label, struct node_port *port) +expand_label_port(const char *name, char *label, size_t len, +    struct node_port *port)  {  	char	 a1[6], a2[6], op[13] = ""; @@ -3415,12 +3792,12 @@ expand_label_port(const char *name, char *label, struct node_port *port)  			snprintf(op, sizeof(op), ">%s", a1);  		else if (port->op == PF_OP_GE)  			snprintf(op, sizeof(op), ">=%s", a1); -		expand_label_str(label, name, op); +		expand_label_str(label, len, name, op);  	}  }  void -expand_label_proto(const char *name, char *label, u_int8_t proto) +expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto)  {  	struct protoent *pe;  	char n[4]; @@ -3428,38 +3805,38 @@ expand_label_proto(const char *name, char *label, u_int8_t proto)  	if (strstr(label, name) != NULL) {  		pe = getprotobynumber(proto);  		if (pe != NULL) -			expand_label_str(label, name, pe->p_name); +			expand_label_str(label, len, name, pe->p_name);  		else {  			snprintf(n, sizeof(n), "%u", proto); -			expand_label_str(label, name, n); +			expand_label_str(label, len, name, n);  		}  	}  }  void -expand_label_nr(const char *name, char *label) +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->rule_nr); -		expand_label_str(label, name, n); +		expand_label_str(label, len, name, n);  	}  }  void -expand_label(char *label, const char *ifname, sa_family_t af, +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, ifname); -	expand_label_addr("$srcaddr", label, af, src_host); -	expand_label_addr("$dstaddr", label, af, dst_host); -	expand_label_port("$srcport", label, src_port); -	expand_label_port("$dstport", label, dst_port); -	expand_label_proto("$proto", label, proto); -	expand_label_nr("$nr", label); +	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 @@ -3725,14 +4102,22 @@ expand_rule(struct pf_rule *r,  	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; +	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, @@ -3753,9 +4138,9 @@ expand_rule(struct pf_rule *r,  		    src_host->af != dst_host->af) ||  		    (src_host->ifindex && dst_host->ifindex &&  		    src_host->ifindex != dst_host->ifindex) || -		    (src_host->ifindex && if_nametoindex(interface->ifname) && +		    (src_host->ifindex && *interface->ifname &&  		    src_host->ifindex != if_nametoindex(interface->ifname)) || -		    (dst_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) @@ -3763,18 +4148,31 @@ expand_rule(struct pf_rule *r,  		else if (!r->af && dst_host->af)  			r->af = dst_host->af; -		if (if_indextoname(src_host->ifindex, ifname)) +		if (*interface->ifname) +			memcpy(r->ifname, interface->ifname, sizeof(r->ifname)); +		else if (if_indextoname(src_host->ifindex, ifname))  			memcpy(r->ifname, ifname, sizeof(r->ifname));  		else if (if_indextoname(dst_host->ifindex, ifname))  			memcpy(r->ifname, ifname, sizeof(r->ifname));  		else -			memcpy(r->ifname, interface->ifname, sizeof(r->ifname)); +			memset(r->ifname, '\0', sizeof(r->ifname));  		if (strlcpy(r->label, label, sizeof(r->label)) >=  		    sizeof(r->label))  			errx(1, "expand_rule: strlcpy"); -		expand_label(r->label, r->ifname, r->af, src_host, src_port, -		    dst_host, dst_port, proto->proto); +		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); @@ -3800,6 +4198,13 @@ expand_rule(struct pf_rule *r,  		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; @@ -3906,6 +4311,7 @@ lookup(char *s)  		{ "cbq",		CBQ},  		{ "code",		CODE},  		{ "crop",		FRAGCROP}, +		{ "debug",		DEBUG},  		{ "drop",		DROP},  		{ "drop-ovl",		FRAGDROP},  		{ "dup-to",		DUPTO}, @@ -3913,13 +4319,18 @@ lookup(char *s)  		{ "file",		FILENAME},  		{ "fingerprints",	FINGERPRINTS},  		{ "flags",		FLAGS}, +		{ "floating",		FLOATING},  		{ "for",		FOR},  		{ "fragment",		FRAGMENT},  		{ "from",		FROM}, +		{ "global",		GLOBAL},  		{ "group",		GROUP}, +		{ "group-bound",	GRBOUND},  		{ "hfsc",		HFSC}, +		{ "hostid",		HOSTID},  		{ "icmp-type",		ICMPTYPE},  		{ "icmp6-type",		ICMP6TYPE}, +		{ "if-bound",		IFBOUND},  		{ "in",			IN},  		{ "inet",		INET},  		{ "inet6",		INET6}, @@ -3933,6 +4344,8 @@ lookup(char *s)  		{ "loginterface",	LOGINTERFACE},  		{ "max",		MAXIMUM},  		{ "max-mss",		MAXMSS}, +		{ "max-src-nodes",	MAXSRCNODES}, +		{ "max-src-states",	MAXSRCSTATES},  		{ "min-ttl",		MINTTL},  		{ "modulate",		MODULATE},  		{ "nat",		NAT}, @@ -3940,6 +4353,7 @@ lookup(char *s)  		{ "no",			NO},  		{ "no-df",		NODF},  		{ "no-route",		NOROUTE}, +		{ "no-sync",		NOSYNC},  		{ "on",			ON},  		{ "optimization",	OPTIMIZATION},  		{ "os",			OS}, @@ -3966,11 +4380,15 @@ lookup(char *s)  		{ "return-rst",		RETURNRST},  		{ "round-robin",	ROUNDROBIN},  		{ "route-to",		ROUTETO}, +		{ "rule",		RULE},  		{ "scrub",		SCRUB},  		{ "set",		SET},  		{ "source-hash",	SOURCEHASH}, +		{ "source-track",	SOURCETRACK},  		{ "state",		STATE}, +		{ "state-policy",	STATEPOLICY},  		{ "static-port",	STATICPORT}, +		{ "sticky-address",	STICKYADDRESS},  		{ "synproxy",		SYNPROXY},  		{ "table",		TABLE},  		{ "tag",		TAG}, @@ -4194,10 +4612,9 @@ top:  		} while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));  		lungetc(c);  		*p = '\0'; -		token = lookup(buf); -		yylval.v.string = strdup(buf); -		if (yylval.v.string == NULL) -			err(1, "yylex: strdup"); +		if ((token = lookup(buf)) == STRING) +			if ((yylval.v.string = strdup(buf)) == NULL) +				err(1, "yylex: strdup");  		return (token);  	}  	if (c == '\n') { @@ -4212,7 +4629,7 @@ top:  int  parse_rules(FILE *input, struct pfctl *xpf)  { -	struct sym	*sym; +	struct sym	*sym, *next;  	fin = input;  	pf = xpf; @@ -4228,13 +4645,15 @@ parse_rules(FILE *input, struct pfctl *xpf)  	yyparse();  	/* Free macros and check which have not been used. */ -	TAILQ_FOREACH(sym, &symhead, entries) { +	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { +		next = TAILQ_NEXT(sym, entries);  		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, entries); +		free(sym);  	}  	return (errors ? -1 : 0); @@ -4372,9 +4791,10 @@ invalid_redirect(struct node_host *nh, sa_family_t af)  	if (!af) {  		struct node_host *n; -		/* only tables are ok without an address family */ +		/* tables and dyniftl are ok without an address family */  		for (n = nh; n != NULL; n = n->next) { -			if (n->addr.type != PF_ADDR_TABLE) { +			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"); @@ -4471,7 +4891,7 @@ parseicmpspec(char *w, sa_family_t af)  }  int -pfctl_load_anchors(int dev, int opts) +pfctl_load_anchors(int dev, int opts, struct pfr_buffer *trans)  {  	struct loadanchors	*la; @@ -4480,7 +4900,7 @@ pfctl_load_anchors(int dev, int opts)  			fprintf(stderr, "\nLoading anchor %s:%s from %s\n",  			    la->anchorname, la->rulesetname, la->filename);  		if (pfctl_rules(dev, la->filename, opts, la->anchorname, -		    la->rulesetname) == -1) +		    la->rulesetname, trans) == -1)  			return (-1);  	} diff --git a/contrib/pf/pfctl/pf_print_state.c b/contrib/pf/pfctl/pf_print_state.c index 58ce23a9aeef..b7cf5ca9ac5e 100644 --- a/contrib/pf/pfctl/pf_print_state.c +++ b/contrib/pf/pfctl/pf_print_state.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pf_print_state.c,v 1.33 2003/07/06 22:01:28 deraadt Exp $	*/ +/*	$OpenBSD: pf_print_state.c,v 1.39 2004/02/10 17:48:08 henning Exp $	*/  /*   * Copyright (c) 2001 Daniel Hartmeier @@ -50,9 +50,24 @@ 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) { +	switch (addr->type) {  	case PF_ADDR_DYNIFTL: -		printf("(%s)", addr->v.ifname); +		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) @@ -85,7 +100,10 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)  		printf("?");  		return;  	} -	if (! PF_AZERO(&addr->v.a.mask, af)) { + +	/* mask if not _both_ address and mask are zero */ +	if (!(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)) @@ -140,8 +158,10 @@ print_host(struct pf_state_host *h, sa_family_t af, int opts)  		aw.v.a.addr = h->addr;  		if (af == AF_INET)  			aw.v.a.mask.addr32[0] = 0xffffffff; -		else +		else {  			memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); +			af = AF_INET6; +		}  		print_addr(&aw, af, opts & PF_OPT_VERBOSE2);  	} @@ -177,6 +197,7 @@ print_state(struct pf_state *s, int opts)  		src = &s->dst;  		dst = &s->src;  	} +	printf("%s ", s->u.ifname);  	if ((p = getprotobynumber(s->proto)) != NULL)  		printf("%s ", p->p_name);  	else @@ -256,8 +277,16 @@ print_state(struct pf_state *s, int opts)  			printf(", anchor %u", s->anchor.nr);  		if (s->rule.nr != -1)  			printf(", rule %u", s->rule.nr); +		if (s->src_node != NULL) +			printf(", source-track"); +		if (s->nat_src_node != NULL) +			printf(", sticky-address");  		printf("\n");  	} +	if (opts & PF_OPT_VERBOSE2) { +		printf("   id: %016llx creatorid: %08x\n", +		    betoh64(s->id), ntohl(s->creatorid)); +	}  }  int diff --git a/contrib/pf/pfctl/pfctl.8 b/contrib/pf/pfctl/pfctl.8 index fb73ce222aab..6fac2d5bc64d 100644 --- a/contrib/pf/pfctl/pfctl.8 +++ b/contrib/pf/pfctl/pfctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pfctl.8,v 1.102 2003/09/18 09:18:51 jmc Exp $ +.\" $OpenBSD: pfctl.8,v 1.110 2004/03/20 09:31:42 david Exp $  .\"  .\" Copyright (c) 2001 Kjell Wooding.  All rights reserved.  .\" @@ -33,15 +33,17 @@  .Sh SYNOPSIS  .Nm pfctl  .Bk -words -.Op Fl AdeghnNqrROvz +.Op Fl AdeghNnOqRrvz  .Op Fl a Ar anchor Ns Op Ar :ruleset  .Op Fl D Ar macro=value -.Op Fl f Ar file  .Op Fl F Ar modifier +.Op Fl f Ar file +.Op Fl i Ar interface  .Op Fl k Ar host +.Op Fl p Ar device  .Op Fl s Ar modifier -.Op Fl t Ar table  .Op Fl T Ar command Op Ar address ... +.Op Fl t Ar table  .Op Fl x Ar level  .Ek  .Sh DESCRIPTION @@ -93,6 +95,9 @@ The  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 Ns Op Ar :ruleset  Apply flags  .Fl f , @@ -134,11 +139,6 @@ This is similar to C rules for variables.  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. -.It Fl A -Load only the queue rules present in the rule file. -Other rules and options are ignored. -.It Fl d -Disable the packet filter.  .It Fl D Ar macro=value  Define  .Ar macro @@ -148,17 +148,10 @@ 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 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 F Ar modifier  Flush the filter parameters specified by  .Ar modifier @@ -173,6 +166,8 @@ Flush the queue rules.  Flush the filter rules.  .It Fl F Ar state  Flush the state table (NAT and filter). +.It Fl F Ar Sources +Flush the source tracking table.  .It Fl F Ar info  Flush the filter information (statistics that are not bound to rules).  .It Fl F Ar Tables @@ -182,8 +177,22 @@ Flush the passive operating system fingerprints.  .It Fl F Ar 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  Kill all of the state entries originating from the specified  .Ar host . @@ -207,29 +216,32 @@ to  .Bd -literal -offset indent  # pfctl -k host1 -k host2  .Ed -.It Fl h -Help. -.It Fl n -Do not actually load rules, just parse them.  .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 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 -Perform reverse DNS lookups on states when displaying them.  .It Fl R  Load only the filter rules present in the rule file.  Other rules and options are ignored. -.It Fl O -Load only the options 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 xxxxxxxxxxxx -compact +.Bl -tag -width xxxxxxxxxxxxx -compact  .It Fl s Ar nat  Show the currently loaded NAT rules.  .It Fl s Ar queue @@ -261,8 +273,13 @@ is specified as well, the named rulesets currently loaded in the specified  anchor are shown instead.  .It Fl s Ar state  Show the contents of the state table. +.It Fl s Ar Sources +Show the contents of the source tracking table.  .It Fl s Ar info  Show filter information (statistics and counters). +When used together with +.Fl v , +source tracking statistics are also shown.  .It Fl s Ar labels  Show per-rule statistics (label, evaluations, packets, bytes) of  filter rules with labels, useful for accounting. @@ -274,16 +291,17 @@ Show the current pool memory hard limits.  Show the list of tables.  .It Fl s Ar osfp  Show the list of operating system fingerprints. -Can be used in combination with -.Fl o Ar file -to list the fingerprints in a -.Xr pf.os 5 -file. +.It Fl s Ar Interfaces +Show the list of interfaces and interface drivers available to PF. +When used together with a double +.Fl v , +interface statistics are also shown. +.Fl i +can be used to select an interface or a group of interfaces.  .It Fl s Ar all -Show all of the above. +Show all of the above, except for the lists of interfaces and operating +system fingerprints.  .El -.It Fl t Ar table -Specify the name of the table.  .It Fl T Ar command Op Ar address ...  Specify the  .Ar command @@ -334,7 +352,7 @@ Comments starting with a "#" are allowed in the text file.  With these commands, the  .Fl v  flag can also be used once or twice, in which case -.Nm pfctl +.Nm  will print the  detailed result of the operation for each individual address, prefixed by  one of the following letters: @@ -359,7 +377,7 @@ The address/network has been cleared (statistics).  Each table maintains a set of counters that can be retrieved using the  .Fl v  flag of -.Nm pfctl . +.Nm .  For example, the following commands define a wide open firewall which will keep  track of packets going to or coming from the  .Ox @@ -367,8 +385,8 @@ ftp server.  The following commands configure the firewall and send 10 pings to the ftp  server:  .Bd -literal -offset indent -# printf \&"table <test> { ftp.openbsd.org }\en \e -\  \  pass out to <test> keep state\en" \&| pfctl -f- +# printf "table <test> { ftp.openbsd.org }\en \e +    pass out to <test> keep state\en" | pfctl -f-  # ping -qc10 ftp.openbsd.org  .Ed  .Pp @@ -381,12 +399,12 @@ The time at which the current accounting started is also shown with the  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 \ \ \ \ \ ] +   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 @@ -401,19 +419,19 @@ packet statistics for the whole table:  .Bd -literal -offset indent  # pfctl -vvsTables  --a-r-  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 \ \ \ \ \ \ \ ] +    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 +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. @@ -421,14 +439,14 @@ The two  .Ar XPass  counters are incremented instead of the  .Ar Pass -counters when a \&"stateful\&" packet is passed but doesn't match the table +counters when a "stateful" packet is passed but doesn't match the table  anymore.  This will happen in our example if someone flushes the table while the ping  command is running.  .Pp  When used with a single  .Fl v , -.Nm pfctl +.Nm  will only display the first line containing the table flags and name.  The flags are defined as follows:  .Pp @@ -459,6 +477,8 @@ For tables which are referenced (used) by rules.  This flag is set when a table in the main ruleset is hidden by one or more  tables of the same name in sub-rulesets (anchors).  .El +.It Fl t Ar table +Specify the name of the table.  .It Fl v  Produce more verbose output.  A second use of diff --git a/contrib/pf/pfctl/pfctl.c b/contrib/pf/pfctl/pfctl.c index 0e52476aaddd..e13e1f44bfc0 100644 --- a/contrib/pf/pfctl/pfctl.c +++ b/contrib/pf/pfctl/pfctl.c @@ -1,7 +1,8 @@ -/*	$OpenBSD: pfctl.c,v 1.188 2003/08/29 21:47:36 cedric Exp $ */ +/*	$OpenBSD: pfctl.c,v 1.213 2004/03/20 09:31:42 david 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 @@ -60,17 +61,19 @@ int	 pfctl_clear_stats(int, int);  int	 pfctl_clear_rules(int, int, char *, char *);  int	 pfctl_clear_nat(int, int, char *, char *);  int	 pfctl_clear_altq(int, int); -int	 pfctl_clear_states(int, int); -int	 pfctl_kill_states(int, int); +int	 pfctl_clear_src_nodes(int, int); +int	 pfctl_clear_states(int, const char *, int); +int	 pfctl_kill_states(int, const char *, int);  int	 pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int, -	     char *, char *); +	    char *, char *);  void	 pfctl_print_rule_counters(struct pf_rule *, int);  int	 pfctl_show_rules(int, int, int, char *, char *);  int	 pfctl_show_nat(int, int, char *, char *); -int	 pfctl_show_states(int, u_int8_t, int); -int	 pfctl_show_status(int); -int	 pfctl_show_timeouts(int); -int	 pfctl_show_limits(int); +int	 pfctl_show_src_nodes(int, int); +int	 pfctl_show_states(int, const char *, int); +int	 pfctl_show_status(int, int); +int	 pfctl_show_timeouts(int, int); +int	 pfctl_show_limits(int, int);  int	 pfctl_debug(int, u_int32_t, int);  int	 pfctl_clear_rule_counters(int, int);  int	 pfctl_test_altqsupport(int, int); @@ -82,6 +85,8 @@ char		*rulesopt;  const char	*showopt;  const char	*debugopt;  char		*anchoropt; +char		*pf_device = "/dev/pf"; +char		*ifaceopt;  char		*tableopt;  const char	*tblcmdopt;  int		 state_killers; @@ -90,6 +95,8 @@ int		 loadopt;  int		 altqsupport;  int		 dev = -1; +int		 first_title = 1; +int		 labels = 0;  const char	*infile; @@ -98,6 +105,7 @@ static const struct {  	int		index;  } pf_limits[] = {  	{ "states",	PF_LIMIT_STATES }, +	{ "src-nodes",	PF_LIMIT_SRC_NODES },  	{ "frags",	PF_LIMIT_FRAGS },  	{ NULL,		0 }  }; @@ -156,12 +164,14 @@ static const struct {  };  static const char *clearopt_list[] = { -	"nat", "queue", "rules", "state", "info", "Tables", "osfp", "all", NULL +	"nat", "queue", "rules", "Sources", +	"state", "info", "Tables", "osfp", "all", NULL  };  static const char *showopt_list[] = { -	"nat", "queue", "rules", "Anchors", "state", "info", "labels", -	"timeouts", "memory", "Tables", "osfp", "all", NULL +	"nat", "queue", "rules", "Anchors", "Sources", "state", "info", +	"Interfaces", "labels", "timeouts", "memory", "Tables", "osfp", +	"all", NULL  };  static const char *tblcmdopt_list[] = { @@ -179,12 +189,14 @@ usage(void)  {  	extern char *__progname; -	fprintf(stderr, "usage: %s [-AdeghnNqrROvz] ", __progname); +	fprintf(stderr, "usage: %s [-AdeghNnOqRrvz] ", __progname);  	fprintf(stderr, "[-a anchor[:ruleset]] [-D macro=value]\n");  	fprintf(stderr, "             "); -	fprintf(stderr, "[-f file] [-F modifier] [-k host] [-s modifier]\n"); +	fprintf(stderr, "[-F modifier] [-f file] [-i interface] "); +	fprintf(stderr, "[-k host] [-p device]\n");  	fprintf(stderr, "             "); -	fprintf(stderr, "[-t table] [-T command [address ...]] [-x level]\n"); +	fprintf(stderr, "[-s modifier] [-T command [address ...]] "); +	fprintf(stderr, "[-t table] [-x level]\n");  	exit(1);  } @@ -239,7 +251,7 @@ pfctl_clear_stats(int dev, int opts)  int  pfctl_clear_rules(int dev, int opts, char *anchorname, char *rulesetname)  { -	struct pfioc_rule pr; +	struct pfr_buffer t;  	if (*anchorname && !*rulesetname) {  		struct pfioc_ruleset pr; @@ -269,19 +281,13 @@ pfctl_clear_rules(int dev, int opts, char *anchorname, char *rulesetname)  			fprintf(stderr, "rules cleared\n");  		return (0);  	} -	memset(&pr, 0, sizeof(pr)); -	memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); -	memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); -	pr.rule.action = PF_SCRUB; -	if (ioctl(dev, DIOCBEGINRULES, &pr)) -		err(1, "DIOCBEGINRULES"); -	else if (ioctl(dev, DIOCCOMMITRULES, &pr)) -		err(1, "DIOCCOMMITRULES"); -	pr.rule.action = PF_PASS; -	if (ioctl(dev, DIOCBEGINRULES, &pr)) -		err(1, "DIOCBEGINRULES"); -	else if (ioctl(dev, DIOCCOMMITRULES, &pr)) -		err(1, "DIOCCOMMITRULES"); +	memset(&t, 0, sizeof(t)); +	t.pfrb_type = PFRB_TRANS; +	if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname, rulesetname) || +	    pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname, rulesetname) || +	    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); @@ -290,7 +296,7 @@ pfctl_clear_rules(int dev, int opts, char *anchorname, char *rulesetname)  int  pfctl_clear_nat(int dev, int opts, char *anchorname, char *rulesetname)  { -	struct pfioc_rule pr; +	struct pfr_buffer t;  	if (*anchorname && !*rulesetname) {  		struct pfioc_ruleset pr; @@ -320,24 +326,14 @@ pfctl_clear_nat(int dev, int opts, char *anchorname, char *rulesetname)  			fprintf(stderr, "nat cleared\n");  		return (0);  	} -	memset(&pr, 0, sizeof(pr)); -	memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); -	memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); -	pr.rule.action = PF_NAT; -	if (ioctl(dev, DIOCBEGINRULES, &pr)) -		err(1, "DIOCBEGINRULES"); -	else if (ioctl(dev, DIOCCOMMITRULES, &pr)) -		err(1, "DIOCCOMMITRULES"); -	pr.rule.action = PF_BINAT; -	if (ioctl(dev, DIOCBEGINRULES, &pr)) -		err(1, "DIOCBEGINRULES"); -	else if (ioctl(dev, DIOCCOMMITRULES, &pr)) -		err(1, "DIOCCOMMITRULES"); -	pr.rule.action = PF_RDR; -	if (ioctl(dev, DIOCBEGINRULES, &pr)) -		err(1, "DIOCBEGINRULES"); -	else if (ioctl(dev, DIOCCOMMITRULES, &pr)) -		err(1, "DIOCCOMMITRULES"); +	memset(&t, 0, sizeof(t)); +	t.pfrb_type = PFRB_TRANS; +	if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname, rulesetname) || +	    pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname, rulesetname) || +	    pfctl_add_trans(&t, PF_RULESET_RDR, anchorname, rulesetname) || +	    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); @@ -346,32 +342,50 @@ pfctl_clear_nat(int dev, int opts, char *anchorname, char *rulesetname)  int  pfctl_clear_altq(int dev, int opts)  { -	struct pfioc_altq pa; +	struct pfr_buffer t;  	if (!altqsupport)  		return (-1); -	memset(&pa, 0, sizeof(pa)); -	if (ioctl(dev, DIOCBEGINALTQS, &pa.ticket)) -		err(1, "DIOCBEGINALTQS"); -	else if (ioctl(dev, DIOCCOMMITALTQS, &pa.ticket)) -		err(1, "DIOCCOMMITALTQS"); +	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_states(int dev, int opts) +pfctl_clear_src_nodes(int dev, int opts)  { -	if (ioctl(dev, DIOCCLRSTATES)) +	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, "states cleared\n"); +		fprintf(stderr, "%d states cleared\n", psk.psk_af);  	return (0);  }  int -pfctl_kill_states(int dev, int opts) +pfctl_kill_states(int dev, const char *iface, int opts)  {  	struct pfioc_state_kill psk;  	struct addrinfo *res[2], *resp[2]; @@ -386,6 +400,9 @@ pfctl_kill_states(int dev, int opts)  	    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);  	if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {  		errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); @@ -419,7 +436,8 @@ pfctl_kill_states(int dev, int opts)  			memset(&last_dst, 0xff, sizeof(last_dst));  			if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL,  			    &res[1]))) { -				errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); +				errx(1, "getaddrinfo: %s", +				    gai_strerror(ret_ga));  				/* NOTREACHED */  			}  			for (resp[1] = res[1]; resp[1]; @@ -545,8 +563,18 @@ pfctl_print_rule_counters(struct pf_rule *rule, int opts)  	if (opts & PF_OPT_VERBOSE)  		printf("  [ Evaluations: %-8llu  Packets: %-8llu  "  			    "Bytes: %-10llu  States: %-6u]\n", -			    rule->evaluations, rule->packets, -			    rule->bytes, rule->states); +			    (unsigned long long)rule->evaluations, +			    (unsigned long long)rule->packets, +			    (unsigned long long)rule->bytes, rule->states); +} + +void +pfctl_print_title(char *title) +{ +	if (!first_title) +		printf("\n"); +	first_title = 0; +	printf("%s\n", title);  }  int @@ -554,7 +582,7 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname,      char *rulesetname)  {  	struct pfioc_rule pr; -	u_int32_t nr, mnr; +	u_int32_t nr, mnr, header = 0;  	int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);  	if (*anchorname && !*rulesetname) { @@ -571,6 +599,8 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname,  				err(1, "DIOCGETRULESETS");  			return (-1);  		} +		if (opts & PF_OPT_SHOWALL && pr.nr) +			pfctl_print_title("FILTER RULES:");  		mnr = pr.nr;  		for (nr = 0; nr < mnr; ++nr) {  			pr.nr = nr; @@ -587,11 +617,25 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname,  	memset(&pr, 0, sizeof(pr));  	memcpy(pr.anchor, anchorname, sizeof(pr.anchor));  	memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); +	if (opts & PF_OPT_SHOWALL) { +		pr.rule.action = PF_PASS; +		if (ioctl(dev, DIOCGETRULES, &pr)) { +			warn("DIOCGETRULES"); +			return (-1); +		} +		header++; +	}  	pr.rule.action = PF_SCRUB;  	if (ioctl(dev, DIOCGETRULES, &pr)) {  		warn("DIOCGETRULES");  		return (-1);  	} +	if (opts & PF_OPT_SHOWALL) { +		if (format == 0 && (pr.nr > 0 || header)) +			pfctl_print_title("FILTER RULES:"); +		else if (format == 1 && labels) +			pfctl_print_title("LABEL COUNTERS:"); +	}  	mnr = pr.nr;  	for (nr = 0; nr < mnr; ++nr) {  		pr.nr = nr; @@ -609,11 +653,14 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname,  			if (pr.rule.label[0]) {  				printf("%s ", pr.rule.label);  				printf("%llu %llu %llu\n", -				    pr.rule.evaluations, pr.rule.packets, -				    pr.rule.bytes); +				    (unsigned long long)pr.rule.evaluations, +				    (unsigned long long)pr.rule.packets, +				    (unsigned long long)pr.rule.bytes);  			}  			break;  		default: +			if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) +				labels = 1;  			print_rule(&pr.rule, rule_numbers);  			pfctl_print_rule_counters(&pr.rule, opts);  		} @@ -641,11 +688,14 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname,  			if (pr.rule.label[0]) {  				printf("%s ", pr.rule.label);  				printf("%llu %llu %llu\n", -				    pr.rule.evaluations, pr.rule.packets, -				    pr.rule.bytes); +				    (unsigned long long)pr.rule.evaluations, +				    (unsigned long long)pr.rule.packets, +				    (unsigned long long)pr.rule.bytes);  			}  			break;  		default: +			if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL)) +				labels = 1;  			print_rule(&pr.rule, rule_numbers);  			pfctl_print_rule_counters(&pr.rule, opts);  		} @@ -660,7 +710,7 @@ pfctl_show_nat(int dev, int opts, char *anchorname, char *rulesetname)  	struct pfioc_rule pr;  	u_int32_t mnr, nr;  	static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT }; -	int i; +	int i, dotitle = opts & PF_OPT_SHOWALL;  	if (*anchorname && !*rulesetname) {  		struct pfioc_ruleset pr; @@ -708,6 +758,10 @@ pfctl_show_nat(int dev, int opts, char *anchorname, char *rulesetname)  			    pr.ticket, nattype[i], anchorname,  			    rulesetname) != 0)  				return (-1); +			if (dotitle) { +				pfctl_print_title("TRANSLATION RULES:"); +				dotitle = 0; +			}  			print_rule(&pr.rule, opts & PF_OPT_VERBOSE2);  			pfctl_print_rule_counters(&pr.rule, opts);  			pfctl_clear_pool(&pr.rule.rpool); @@ -717,21 +771,64 @@ pfctl_show_nat(int dev, int opts, char *anchorname, char *rulesetname)  }  int -pfctl_show_states(int dev, u_int8_t proto, int opts) +pfctl_show_src_nodes(int dev, int opts) +{ +	struct pfioc_src_nodes psn; +	struct pf_src_node *p; +	char *inbuf = NULL, *newinbuf = NULL; +	unsigned 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"); +			return (-1); +		} +		if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len) +			break; +		if (len == 0 && psn.psn_len == 0) +			return (0); +		if (len == 0 && psn.psn_len != 0) +			len = psn.psn_len; +		if (psn.psn_len == 0) +			return (0);	/* 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++; +	} +	return (0); +} + +int +pfctl_show_states(int dev, const char *iface, int opts)  {  	struct pfioc_states ps;  	struct pf_state *p; -	char *inbuf = NULL; +	char *inbuf = NULL, *newinbuf = NULL;  	unsigned len = 0; -	int i; +	int i, dotitle = (opts & PF_OPT_SHOWALL);  	memset(&ps, 0, sizeof(ps));  	for (;;) {  		ps.ps_len = len;  		if (len) { -			ps.ps_buf = inbuf = realloc(inbuf, len); -			if (inbuf == NULL) +			newinbuf = realloc(inbuf, len); +			if (newinbuf == NULL)  				err(1, "realloc"); +			ps.ps_buf = inbuf = newinbuf;  		}  		if (ioctl(dev, DIOCGETSTATES, &ps) < 0) {  			warn("DIOCGETSTATES"); @@ -748,16 +845,20 @@ pfctl_show_states(int dev, u_int8_t proto, int opts)  		len *= 2;  	}  	p = ps.ps_states; -	for (i = 0; i < ps.ps_len; i += sizeof(*p)) { -		if (!proto || (p->proto == proto)) -			print_state(p, opts); -		p++; +	for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) { +		if (iface != NULL && strcmp(p->u.ifname, iface)) +			continue; +		if (dotitle) { +			pfctl_print_title("STATES:"); +			dotitle = 0; +		} +		print_state(p, opts);  	}  	return (0);  }  int -pfctl_show_status(int dev) +pfctl_show_status(int dev, int opts)  {  	struct pf_status status; @@ -765,16 +866,20 @@ pfctl_show_status(int dev)  		warn("DIOCGETSTATUS");  		return (-1);  	} -	print_status(&status); +	if (opts & PF_OPT_SHOWALL) +		pfctl_print_title("INFO:"); +	print_status(&status, opts);  	return (0);  }  int -pfctl_show_timeouts(int dev) +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; @@ -792,14 +897,16 @@ pfctl_show_timeouts(int dev)  }  int -pfctl_show_limits(int dev) +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 = i; +		pl.index = pf_limits[i].index;  		if (ioctl(dev, DIOCGETLIMIT, &pl))  			err(1, "DIOCGETLIMIT");  		printf("%-10s ", pf_limits[i].name); @@ -836,7 +943,8 @@ pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af)  int  pfctl_add_rule(struct pfctl *pf, struct pf_rule *r)  { -	u_int8_t rs_num; +	u_int8_t		rs_num; +	struct pfioc_rule	pr;  	switch (r->action) {  	case PF_SCRUB: @@ -874,12 +982,19 @@ pfctl_add_rule(struct pfctl *pf, struct pf_rule *r)  	}  	if ((pf->opts & PF_OPT_NOACTION) == 0) { +		bzero(&pr, sizeof(pr)); +		if (strlcpy(pr.anchor, pf->anchor, sizeof(pr.anchor)) >= +		    sizeof(pr.anchor) || +		    strlcpy(pr.ruleset, pf->ruleset, sizeof(pr.ruleset)) >= +		    sizeof(pr.ruleset)) +			errx(1, "pfctl_add_rule: strlcpy");  		if (pfctl_add_pool(pf, &r->rpool, r->af))  			return (1); -		memcpy(&pf->prule[rs_num]->rule, r, -		    sizeof(pf->prule[rs_num]->rule)); -		pf->prule[rs_num]->pool_ticket = pf->paddr.ticket; -		if (ioctl(pf->dev, DIOCADDRULE, pf->prule[rs_num])) +		pr.ticket = pfctl_get_ticket(pf->trans, rs_num, pf->anchor, +		    pf->ruleset); +		pr.pool_ticket = pf->paddr.ticket; +		memcpy(&pr.rule, r, sizeof(pr.rule)); +		if (ioctl(pf->dev, DIOCADDRULE, &pr))  			err(1, "DIOCADDRULE");  	}  	if (pf->opts & PF_OPT_VERBOSE) @@ -912,26 +1027,31 @@ pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)  int  pfctl_rules(int dev, char *filename, int opts, char *anchorname, -    char *rulesetname) +    char *rulesetname, struct pfr_buffer *trans)  {  #define ERR(x) do { warn(x); goto _error; } while(0)  #define ERRX(x) do { warnx(x); goto _error; } while(0) -	FILE *fin; -	struct pfioc_rule	pr[PF_RULESET_MAX]; -	struct pfioc_altq	pa; -	struct pfctl		pf; -	struct pfr_table	trs; -	int			i; +	FILE			*fin; +	struct pfr_buffer	*t, buf; +	struct pfioc_altq	 pa; +	struct pfctl		 pf; +	struct pfr_table	 trs; +	int			 osize; + +	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)); -	for (i = 0; i < PF_RULESET_MAX; i++) { -		memset(&pr[i], 0, sizeof(pr[i])); -		memcpy(pr[i].anchor, anchorname, sizeof(pr[i].anchor)); -		memcpy(pr[i].ruleset, rulesetname, sizeof(pr[i].ruleset)); -	}  	if (strlcpy(trs.pfrt_anchor, anchorname,  	    sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor) ||  	    strlcpy(trs.pfrt_ruleset, rulesetname, @@ -947,46 +1067,53 @@ pfctl_rules(int dev, char *filename, int opts, char *anchorname,  		}  		infile = filename;  	} -	if ((opts & PF_OPT_NOACTION) == 0) { -		if ((loadopt & PFCTL_FLAG_NAT) != 0) { -			pr[PF_RULESET_NAT].rule.action = PF_NAT; -			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_NAT])) -				ERR("DIOCBEGINRULES"); -			pr[PF_RULESET_RDR].rule.action = PF_RDR; -			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_RDR])) -				ERR("DIOCBEGINRULES"); -			pr[PF_RULESET_BINAT].rule.action = PF_BINAT; -			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_BINAT])) -				ERR("DIOCBEGINRULES"); -		} -		if (((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) && -		    ioctl(dev, DIOCBEGINALTQS, &pa.ticket)) { -			ERR("DIOCBEGINALTQS"); -		} -		if ((loadopt & PFCTL_FLAG_FILTER) != 0) { -			pr[PF_RULESET_SCRUB].rule.action = PF_SCRUB; -			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_SCRUB])) -				ERR("DIOCBEGINRULES"); -			pr[PF_RULESET_FILTER].rule.action = PF_PASS; -			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_FILTER])) -				ERR("DIOCBEGINRULES"); -		} -		if (loadopt & PFCTL_FLAG_TABLE) { -			if (pfr_ina_begin(&trs, &pf.tticket, NULL, 0) != 0) -				ERR("begin table"); -		} -	} -	/* fill in callback data */  	pf.dev = dev;  	pf.opts = opts;  	pf.loadopt = loadopt; +	if (anchorname[0]) +		pf.loadopt &= ~PFCTL_FLAG_ALTQ;  	pf.paltq = &pa; -	for (i = 0; i < PF_RULESET_MAX; i++) { -		pf.prule[i] = &pr[i]; -	} +	pf.trans = t;  	pf.rule_nr = 0;  	pf.anchor = anchorname;  	pf.ruleset = rulesetname; + +	if ((opts & PF_OPT_NOACTION) == 0) { +		if ((pf.loadopt & PFCTL_FLAG_NAT) != 0) { +			if (pfctl_add_trans(t, PF_RULESET_NAT, anchorname, +			    rulesetname) || +			    pfctl_add_trans(t, PF_RULESET_BINAT, anchorname, +			    rulesetname) || +			    pfctl_add_trans(t, PF_RULESET_RDR, anchorname, +			    rulesetname)) +				ERR("pfctl_rules"); +		} +		if (((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))) { +			if (pfctl_add_trans(t, PF_RULESET_ALTQ, anchorname, +			    rulesetname)) +				ERR("pfctl_rules"); +		} +		if ((pf.loadopt & PFCTL_FLAG_FILTER) != 0) { +			if (pfctl_add_trans(t, PF_RULESET_SCRUB, anchorname, +			    rulesetname) || +			    pfctl_add_trans(t, PF_RULESET_FILTER, anchorname, +			    rulesetname)) +				ERR("pfctl_rules"); +		} +		if (pf.loadopt & PFCTL_FLAG_TABLE) { +			if (pfctl_add_trans(t, PF_RULESET_TABLE, anchorname, +			    rulesetname)) +				ERR("pfctl_rules"); +		} +		if (pfctl_trans(dev, t, DIOCXBEGIN, osize)) +			ERR("DIOCXBEGIN"); +		if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) +			pa.ticket = pfctl_get_ticket(t, PF_RULESET_ALTQ, +			    anchorname, rulesetname); +		if (pf.loadopt & PFCTL_FLAG_TABLE) +			pf.tticket = pfctl_get_ticket(t, PF_RULESET_TABLE, +			    anchorname, rulesetname); +	}  	if (parse_rules(fin, &pf) < 0) {  		if ((opts & PF_OPT_NOACTION) == 0)  			ERRX("Syntax error in config file: " @@ -994,57 +1121,30 @@ pfctl_rules(int dev, char *filename, int opts, char *anchorname,  		else  			goto _error;  	} -	if ((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) +	if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))  		if (check_commit_altq(dev, opts) != 0)  			ERRX("errors in altq config"); -	if ((opts & PF_OPT_NOACTION) == 0) { -		if ((loadopt & PFCTL_FLAG_NAT) != 0) { -			pr[PF_RULESET_NAT].rule.action = PF_NAT; -			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_NAT]) && -			    (errno != EINVAL || pf.rule_nr)) -				ERR("DIOCCOMMITRULES NAT"); -			pr[PF_RULESET_RDR].rule.action = PF_RDR; -			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_RDR]) && -			    (errno != EINVAL || pf.rule_nr)) -				ERR("DIOCCOMMITRULES RDR"); -			pr[PF_RULESET_BINAT].rule.action = PF_BINAT; -			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_BINAT]) && -			    (errno != EINVAL || pf.rule_nr)) -				ERR("DIOCCOMMITRULES BINAT"); -		} -		if (((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) && -		    ioctl(dev, DIOCCOMMITALTQS, &pa.ticket)) -			ERR("DIOCCOMMITALTQS"); -		if ((loadopt & PFCTL_FLAG_FILTER) != 0) { -			pr[PF_RULESET_SCRUB].rule.action = PF_SCRUB; -			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_SCRUB]) && -			    (errno != EINVAL || pf.rule_nr)) -				ERR("DIOCCOMMITRULES SCRUB"); -			pr[PF_RULESET_FILTER].rule.action = PF_PASS; -			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_FILTER]) && -			    (errno != EINVAL || pf.rule_nr)) -				ERR("DIOCCOMMITRULES FILTER"); -		} -		if (loadopt & PFCTL_FLAG_TABLE) { -			if (pfr_ina_commit(&trs, pf.tticket, NULL, NULL, 0)) -				ERR("commit table"); -			pf.tdirty = 0; -		} -	}  	if (fin != stdin)  		fclose(fin);  	/* process "load anchor" directives */  	if (!anchorname[0] && !rulesetname[0]) -		if (pfctl_load_anchors(dev, opts) == -1) +		if (pfctl_load_anchors(dev, opts, t) == -1)  			ERRX("load anchors"); +	if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) +		if (pfctl_trans(dev, t, DIOCXCOMMIT, 0)) +			ERR("DIOCXCOMMIT");  	return (0);  _error: -	if (pf.tdirty) /* cleanup kernel leftover */ -		pfr_ina_begin(&trs, NULL, NULL, 0); -	exit(1); +	if (trans == NULL) {	/* main ruleset */ +		if ((opts & PF_OPT_NOACTION) == 0) +			if (pfctl_trans(dev, t, DIOCXROLLBACK, 0)) +				err(1, "DIOCXROLLBACK"); +		exit(1); +	} else			/* sub ruleset */ +		return (-1);  #undef ERR  #undef ERRX @@ -1062,7 +1162,7 @@ pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)  	memset(&pl, 0, sizeof(pl));  	for (i = 0; pf_limits[i].name; i++) {  		if (strcasecmp(opt, pf_limits[i].name) == 0) { -			pl.index = i; +			pl.index = pf_limits[i].index;  			pl.limit = limit;  			if ((pf->opts & PF_OPT_NOACTION) == 0) {  				if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) { @@ -1181,6 +1281,55 @@ pfctl_set_logif(struct pfctl *pf, char *ifname)  }  int +pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) +{ +	if ((loadopt & PFCTL_FLAG_OPTION) == 0) +		return (0); + +	HTONL(hostid); + +	if ((pf->opts & PF_OPT_NOACTION) == 0) +		if (ioctl(dev, DIOCSETHOSTID, &hostid)) +			err(1, "DIOCSETHOSTID"); + +	if (pf->opts & PF_OPT_VERBOSE) +		printf("set hostid 0x%08x\n", ntohl(hostid)); + +	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")) +		level = PF_DEBUG_NONE; +	else if (!strcmp(d, "urgent")) +		level = PF_DEBUG_URGENT; +	else if (!strcmp(d, "misc")) +		level = PF_DEBUG_MISC; +	else if (!strcmp(d, "loud")) +		level = PF_DEBUG_NOISY; +	else { +		warnx("unknown debug level \"%s\"", d); +		return (-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_debug(int dev, u_int32_t level, int opts)  {  	if (ioctl(dev, DIOCSETDEBUG, &level)) @@ -1250,14 +1399,15 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)  			return (-1);  		}  		mnr = pa.nr; -		if (!(opts & PF_OPT_QUIET)) -			printf("%u anchors:\n", mnr);  		for (nr = 0; nr < mnr; ++nr) {  			pa.nr = nr;  			if (ioctl(dev, DIOCGETANCHOR, &pa)) {  				warn("DIOCGETANCHOR");  				return (-1);  			} +			if (!(opts & PF_OPT_VERBOSE) && +			    !strcmp(pa.name, PF_RESERVED_ANCHOR)) +				continue;  			printf("  %s\n", pa.name);  		}  	} else { @@ -1274,8 +1424,6 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)  			return (-1);  		}  		mnr = pr.nr; -		if (!(opts & PF_OPT_QUIET)) -			printf("%u rulesets in anchor %s:\n", mnr, anchorname);  		for (nr = 0; nr < mnr; ++nr) {  			pr.nr = nr;  			if (ioctl(dev, DIOCGETRULESET, &pr)) @@ -1309,8 +1457,8 @@ main(int argc, char *argv[])  	if (argc < 2)  		usage(); -	while ((ch = getopt(argc, argv, "a:AdD:eqf:F:ghk:nNOrRs:t:T:vx:z")) != -		-1) { +	while ((ch = getopt(argc, argv, +	    "a:AdD:eqf:F:ghi:k:nNOp:rRs:t:T:vx:z")) != -1) {  		switch (ch) {  		case 'a':  			anchoropt = optarg; @@ -1339,6 +1487,9 @@ main(int argc, char *argv[])  			}  			mode = O_RDWR;  			break; +		case 'i': +			ifaceopt = optarg; +			break;  		case 'k':  			if (state_killers >= 2) {  				warnx("can only specify -k twice"); @@ -1373,6 +1524,9 @@ main(int argc, char *argv[])  		case 'O':  			loadopt |= PFCTL_FLAG_OPTION;  			break; +		case 'p': +			pf_device = optarg; +			break;  		case 's':  			showopt = pfctl_lookup_option(optarg, showopt_list);  			if (showopt == NULL) { @@ -1422,14 +1576,8 @@ main(int argc, char *argv[])  		if (ch == 'l') {  			loadopt |= PFCTL_FLAG_TABLE;  			tblcmdopt = NULL; -		} else { +		} else  			mode = strchr("acdfkrz", ch) ? O_RDWR : O_RDONLY; -			if (opts & PF_OPT_NOACTION) { -				dev = open("/dev/pf", mode); -				if (dev >= 0) -					opts |= PF_OPT_DUMMYACTION; -			} -		}  	} else if (argc != optind) {  		warnx("unknown command line argument: %s ...", argv[optind]);  		usage(); @@ -1468,11 +1616,14 @@ main(int argc, char *argv[])  	}  	if ((opts & PF_OPT_NOACTION) == 0) { -		dev = open("/dev/pf", mode); +		dev = open(pf_device, mode);  		if (dev == -1) -			err(1, "/dev/pf"); +			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; @@ -1503,32 +1654,38 @@ main(int argc, char *argv[])  			pfctl_show_nat(dev, opts, anchorname, rulesetname);  			break;  		case 'q': -			pfctl_show_altq(dev, opts, opts & PF_OPT_VERBOSE2); +			pfctl_show_altq(dev, ifaceopt, opts, +			    opts & PF_OPT_VERBOSE2);  			break;  		case 's': -			pfctl_show_states(dev, 0, opts); +			pfctl_show_states(dev, ifaceopt, opts); +			break; +		case 'S': +			pfctl_show_src_nodes(dev, opts);  			break;  		case 'i': -			pfctl_show_status(dev); +			pfctl_show_status(dev, opts);  			break;  		case 't': -			pfctl_show_timeouts(dev); +			pfctl_show_timeouts(dev, opts);  			break;  		case 'm': -			pfctl_show_limits(dev); +			pfctl_show_limits(dev, opts);  			break;  		case 'a': +			opts |= PF_OPT_SHOWALL;  			pfctl_load_fingerprints(dev, opts); +			pfctl_show_nat(dev, opts, anchorname, rulesetname);  			pfctl_show_rules(dev, opts, 0, anchorname,  			    rulesetname); -			pfctl_show_nat(dev, opts, anchorname, rulesetname); -			pfctl_show_altq(dev, opts, 0); -			pfctl_show_states(dev, 0, opts); -			pfctl_show_status(dev); +			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, opts, 1, anchorname, rulesetname); -			pfctl_show_timeouts(dev); -			pfctl_show_limits(dev); +			pfctl_show_timeouts(dev, opts); +			pfctl_show_limits(dev, opts);  			pfctl_show_tables(anchorname, rulesetname, opts);  			pfctl_show_fingerprints(opts);  			break; @@ -1539,6 +1696,9 @@ main(int argc, char *argv[])  			pfctl_load_fingerprints(dev, opts);  			pfctl_show_fingerprints(opts);  			break; +		case 'I': +			pfctl_show_ifaces(ifaceopt, opts); +			break;  		}  	} @@ -1554,7 +1714,10 @@ main(int argc, char *argv[])  			pfctl_clear_altq(dev, opts);  			break;  		case 's': -			pfctl_clear_states(dev, opts); +			pfctl_clear_states(dev, ifaceopt, opts); +			break; +		case 'S': +			pfctl_clear_src_nodes(dev, opts);  			break;  		case 'i':  			pfctl_clear_stats(dev, opts); @@ -1562,11 +1725,14 @@ main(int argc, char *argv[])  		case 'a':  			pfctl_clear_rules(dev, opts, anchorname, rulesetname);  			pfctl_clear_nat(dev, opts, anchorname, rulesetname); -			pfctl_clear_altq(dev, opts); -			pfctl_clear_states(dev, opts); -			pfctl_clear_stats(dev, opts);  			pfctl_clear_tables(anchorname, rulesetname, opts); -			pfctl_clear_fingerprints(dev, opts); +			if (!*anchorname && !*rulesetname) { +				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); +			}  			break;  		case 'o':  			pfctl_clear_fingerprints(dev, opts); @@ -1577,7 +1743,7 @@ main(int argc, char *argv[])  		}  	}  	if (state_killers) -		pfctl_kill_states(dev, opts); +		pfctl_kill_states(dev, ifaceopt, opts);  	if (tblcmdopt != NULL) {  		error = pfctl_command_tables(argc, argv, tableopt, @@ -1590,7 +1756,8 @@ main(int argc, char *argv[])  			error = 1;  	if (rulesopt != NULL) { -		if (pfctl_rules(dev, rulesopt, opts, anchorname, rulesetname)) +		if (pfctl_rules(dev, rulesopt, opts, anchorname, rulesetname, +		    NULL))  			error = 1;  		else if (!(opts & PF_OPT_NOACTION) &&  		    (loadopt & PFCTL_FLAG_TABLE)) diff --git a/contrib/pf/pfctl/pfctl.h b/contrib/pf/pfctl/pfctl.h index 2149ac14f965..dd39abab3192 100644 --- a/contrib/pf/pfctl/pfctl.h +++ b/contrib/pf/pfctl/pfctl.h @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl.h,v 1.25 2003/08/29 21:47:36 cedric Exp $ */ +/*	$OpenBSD: pfctl.h,v 1.33 2004/02/19 21:37:01 cedric Exp $ */  /*   * Copyright (c) 2001 Daniel Hartmeier @@ -33,7 +33,8 @@  #ifndef _PFCTL_H_  #define _PFCTL_H_ -enum {	PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS, PFRB_MAX }; +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 */ @@ -57,7 +58,7 @@ 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 *, 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_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int); @@ -74,13 +75,17 @@ 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_if *, int *, int); +int	 pfi_clr_istats(const char *, int *, int); +void	 pfctl_print_title(char *);  int	 pfctl_clear_tables(const char *, const char *, int);  int	 pfctl_show_tables(const char *, const char *, int);  int	 pfctl_command_tables(int, char *[], char *, const char *, char *,  	    const char *, const char *, int); -int	 pfctl_show_altq(int, int, int); +int	 pfctl_show_altq(int, const char *, int, int);  void	 warn_namespace_collision(const char *); +int	 pfctl_show_ifaces(const char *, int);  #ifndef DEFAULT_PRIORITY  #define DEFAULT_PRIORITY	1 @@ -111,5 +116,9 @@ void	 print_state(struct pf_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 *, const char *); +u_int32_t +	 pfctl_get_ticket(struct pfr_buffer *, int, const char *, 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 index efe92ab9965b..04e3da6686f7 100644 --- a/contrib/pf/pfctl/pfctl_altq.c +++ b/contrib/pf/pfctl/pfctl_altq.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl_altq.c,v 1.77 2003/08/22 21:50:34 david Exp $	*/ +/*	$OpenBSD: pfctl_altq.c,v 1.83 2004/03/14 21:51:44 dhartmei Exp $	*/  /*   * Copyright (c) 2002 @@ -21,7 +21,6 @@  #include <sys/types.h>  #include <sys/ioctl.h>  #include <sys/socket.h> -#include <sys/limits.h>  #include <net/if.h>  #include <netinet/in.h> @@ -29,6 +28,7 @@  #include <err.h>  #include <errno.h> +#include <limits.h>  #include <math.h>  #include <stdio.h>  #include <stdlib.h> @@ -82,8 +82,6 @@ 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 *); -static u_int32_t	 max_qid = 1; -  void  pfaltq_store(struct pf_altq *a)  { @@ -158,14 +156,14 @@ void  print_altq(const struct pf_altq *a, unsigned level, struct node_queue_bw *bw,  	struct node_queue_opt *qopts)  { -	if (a->qname[0] != NULL) { +	if (a->qname[0] != 0) {  		print_queue(a, level, bw, 0, qopts);  		return;  	}  	printf("altq on %s ", a->ifname); -	switch(a->scheduler) { +	switch (a->scheduler) {  	case ALTQT_CBQ:  		if (!print_cbq_opts(a))  			printf("cbq "); @@ -261,6 +259,8 @@ eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,  		else  			size = 24;  		size = size * getifmtu(pa->ifname); +		if (size > 0xffff) +			size = 0xffff;  		pa->tbrsize = size;  	}  	return (errors); @@ -410,8 +410,6 @@ eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa)  	if (pa->parent[0] == 0)  		opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); -	else if (pa->qid == 0 && (opts->flags & CBQCLF_DEFCLASS) == 0) -		pa->qid = ++max_qid;  	cbq_compute_idletime(pf, pa);  	return (0); @@ -485,9 +483,12 @@ cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)  	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 * 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; @@ -495,10 +496,10 @@ cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)  	opts->minburst = minburst;  	opts->maxburst = maxburst; -	opts->ns_per_byte = (u_int) nsPerByte; -	opts->maxidle = (u_int) fabs(maxidle); +	opts->ns_per_byte = (u_int)nsPerByte; +	opts->maxidle = (u_int)fabs(maxidle);  	opts->minidle = (int)minidle; -	opts->offtime = (u_int) fabs(offtime); +	opts->offtime = (u_int)fabs(offtime);  	return (0);  } @@ -593,9 +594,6 @@ eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa)  		}  	} -	if (pa->qid == 0) -		pa->qid = ++max_qid; -  	return (0);  } @@ -665,13 +663,11 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)  	if (pa->parent[0] == 0) {  		/* root queue */ -		pa->qid = HFSC_ROOTCLASS_HANDLE;  		opts->lssc_m1 = pa->ifbandwidth;  		opts->lssc_m2 = pa->ifbandwidth;  		opts->lssc_d = 0;  		return (0); -	} else if (pa->qid == 0) -		pa->qid = ++max_qid; +	}  	LIST_INIT(&rtsc);  	LIST_INIT(&lssc); @@ -718,7 +714,7 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)  		/* 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.d = altq->pq_u.hfsc_opts.rtsc_d;  			sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2;  			gsc_add_sc(&rtsc, &sc);  		} @@ -729,7 +725,7 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)  		/* if the class has a link-sharing 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.d = altq->pq_u.hfsc_opts.lssc_d;  			sc.m2 = altq->pq_u.hfsc_opts.lssc_m2;  			gsc_add_sc(&lssc, &sc);  		} @@ -738,7 +734,7 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)  	/* check the real-time service curve.  reserve 20% of interface bw */  	if (opts->rtsc_m2 != 0) {  		sc.m1 = 0; -		sc.d  = 0; +		sc.d = 0;  		sc.m2 = pa->ifbandwidth / 100 * 80;  		if (!is_gsc_under_sc(&rtsc, &sc)) {  			warnx("real-time sc exceeds the interface bandwidth"); @@ -749,7 +745,7 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)  	/* check the link-sharing service curve. */  	if (opts->lssc_m2 != 0) {  		sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; -		sc.d  = parent->pq_u.hfsc_opts.lssc_d; +		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("link-sharing sc exceeds parent's sc"); @@ -1007,7 +1003,7 @@ gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m)  	else  		x2 = x + d;  	start = gsc_getentry(gsc, x); -	end   = gsc_getentry(gsc, x2); +	end = gsc_getentry(gsc, x2);  	if (start == NULL || end == NULL)  		return (-1); diff --git a/contrib/pf/pfctl/pfctl_osfp.c b/contrib/pf/pfctl/pfctl_osfp.c index 958531450a15..6d1fb9902574 100644 --- a/contrib/pf/pfctl/pfctl_osfp.c +++ b/contrib/pf/pfctl/pfctl_osfp.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl_osfp.c,v 1.4 2003/08/27 17:42:00 frantzen Exp $ */ +/*	$OpenBSD: pfctl_osfp.c,v 1.8 2004/02/27 10:42:00 henning Exp $ */  /*   * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org> @@ -31,6 +31,7 @@  #include <string.h>  #include "pfctl_parser.h" +#include "pfctl.h"  #ifndef MIN  # define MIN(a,b)	(((a) < (b)) ? (a) : (b)) @@ -308,11 +309,17 @@ pfctl_load_fingerprints(int dev, int opts)  void  pfctl_show_fingerprints(int opts)  { -	printf("Passive OS Fingerprints:\n"); -	printf("\tClass\tVersion\tSubtype(subversion)\n"); -	printf("\t-----\t-------\t-------------------\n"); -	sort_name_list(opts, &classes); -	print_name_list(opts, &classes, "\t"); +	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 */ @@ -825,7 +832,7 @@ get_int(char **line, size_t *len, int *var, int *mod,  	}  	for (; i < fieldlen; i++) { -		if (field[i] < '0'  || field[i] > '9') { +		if (field[i] < '0' || field[i] > '9') {  			fprintf(stderr, "%s:%d non-digit character in %s\n",  			    filename, lineno, name);  			return (1); diff --git a/contrib/pf/pfctl/pfctl_parser.c b/contrib/pf/pfctl/pfctl_parser.c index 7c051ac4bd48..406c3931b6f3 100644 --- a/contrib/pf/pfctl/pfctl_parser.c +++ b/contrib/pf/pfctl/pfctl_parser.c @@ -1,7 +1,8 @@ -/*	$OpenBSD: pfctl_parser.c,v 1.175 2003/09/18 20:27:58 cedric Exp $ */ +/*	$OpenBSD: pfctl_parser.c,v 1.194 2004/03/15 15:25:44 dhartmei 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 @@ -31,6 +32,7 @@   */  #include <sys/types.h> +#include <sys/ioctl.h>  #include <sys/socket.h>  #include <net/if.h>  #include <netinet/in.h> @@ -192,6 +194,7 @@ const struct pf_timeout pf_timeouts[] = {  	{ "interval",		PFTM_INTERVAL },  	{ "adaptive.start",	PFTM_ADAPTIVE_START },  	{ "adaptive.end",	PFTM_ADAPTIVE_END }, +	{ "src.track",		PFTM_SRC_NODE },  	{ NULL,			0 }  }; @@ -251,7 +254,7 @@ geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)  		}  	} else {  		for (i=0; i < (sizeof (icmp6_code) / -		   sizeof(icmp6_code[0])); i++) { +		    sizeof(icmp6_code[0])); i++) {  			if (type == icmp6_code[i].type &&  			    code == icmp6_code[i].code)  				return (&icmp6_code[i]); @@ -458,23 +461,27 @@ print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2,  		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_fcounters[FCNT_MAX+1] = FCNT_NAMES; +const char	*pf_scounters[FCNT_MAX+1] = FCNT_NAMES;  void -print_status(struct pf_status *s) +print_status(struct pf_status *s, int opts)  { -	char	statline[80]; +	char	statline[80], *running;  	time_t	runtime;  	int	i;  	runtime = time(NULL) - s->since; +	running = s->running ? "Enabled" : "Disabled"; -	if (s->running) { +	if (s->since) {  		unsigned	sec, min, hrs, day = runtime;  		sec = day % 60; @@ -484,48 +491,54 @@ print_status(struct pf_status *s)  		hrs = day % 24;  		day /= 24;  		snprintf(statline, sizeof(statline), -		    "Status: Enabled for %u days %.2u:%.2u:%.2u", -		    day, hrs, min, sec); +		    "Status: %s for %u days %.2u:%.2u:%.2u", +		    running, day, hrs, min, sec);  	} else -		snprintf(statline, sizeof(statline), "Status: Disabled"); +		snprintf(statline, sizeof(statline), "Status: %s", running);  	printf("%-44s", statline);  	switch (s->debug) { -	case 0: +	case PF_DEBUG_NONE:  		printf("%15s\n\n", "Debug: None");  		break; -	case 1: +	case PF_DEBUG_URGENT:  		printf("%15s\n\n", "Debug: Urgent");  		break; -	case 2: +	case PF_DEBUG_MISC:  		printf("%15s\n\n", "Debug: Misc");  		break; +	case PF_DEBUG_NOISY: +		printf("%15s\n\n", "Debug: Loud"); +		break;  	} +	printf("Hostid: 0x%08x\n\n", ntohl(s->hostid));  	if (s->ifname[0] != 0) {  		printf("Interface Stats for %-16s %5s %16s\n",  		    s->ifname, "IPv4", "IPv6");  		printf("  %-25s %14llu %16llu\n", "Bytes In", -		    s->bcounters[0][0], s->bcounters[1][0]); +		    (unsigned long long)s->bcounters[0][0], +		    (unsigned long long)s->bcounters[1][0]);  		printf("  %-25s %14llu %16llu\n", "Bytes Out", -		    s->bcounters[0][1], s->bcounters[1][1]); +		    (unsigned long long)s->bcounters[0][1], +		    (unsigned long long)s->bcounters[1][1]);  		printf("  Packets In\n");  		printf("    %-23s %14llu %16llu\n", "Passed", -		    s->pcounters[0][0][PF_PASS], -		    s->pcounters[1][0][PF_PASS]); +		    (unsigned long long)s->pcounters[0][0][PF_PASS], +		    (unsigned long long)s->pcounters[1][0][PF_PASS]);  		printf("    %-23s %14llu %16llu\n", "Blocked", -		    s->pcounters[0][0][PF_DROP], -		    s->pcounters[1][0][PF_DROP]); +		    (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", -		    s->pcounters[0][1][PF_PASS], -		    s->pcounters[1][1][PF_PASS]); +		    (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", -		    s->pcounters[0][1][PF_DROP], -		    s->pcounters[1][1][PF_DROP]); +		    (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], +		printf("  %-25s %14llu ", pf_fcounters[i],  			    (unsigned long long)s->fcounters[i]);  		if (runtime > 0)  			printf("%14.1f/s\n", @@ -533,6 +546,20 @@ print_status(struct pf_status *s)  		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], +				    s->scounters[i]); +			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], @@ -546,6 +573,57 @@ print_status(struct pf_status *s)  }  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(" (%d states)\n", sn->states); +	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(", %u pkts, %u bytes", sn->packets, sn->bytes); +		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, int verbose)  {  	static const char *actiontypes[] = { "pass", "block", "scrub", "nat", @@ -582,7 +660,7 @@ print_rule(struct pf_rule *r, int verbose)  			ic6 = geticmpcodebynumber(r->return_icmp6 >> 8,  			    r->return_icmp6 & 255, AF_INET6); -			switch(r->af) { +			switch (r->af) {  			case AF_INET:  				printf(" return-icmp");  				if (ic == NULL) @@ -701,7 +779,13 @@ print_rule(struct pf_rule *r, int verbose)  	else if (r->keep_state == PF_STATE_SYNPROXY)  		printf(" synproxy state");  	opts = 0; -	if (r->max_states) +	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 | PFRULE_GRBOUND))  		opts = 1;  	for (i = 0; !opts && i < PFTM_MAX; ++i)  		if (r->timeout[i]) @@ -712,6 +796,46 @@ print_rule(struct pf_rule *r, int verbose)  			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_nodes) { +			if (!opts) +				printf(", "); +			printf("max-src-nodes %u", r->max_src_nodes); +			opts = 0; +		} +		if (r->rule_flag & PFRULE_IFBOUND) { +			if (!opts) +				printf(", "); +			printf("if-bound"); +			opts = 0; +		} +		if (r->rule_flag & PFRULE_GRBOUND) { +			if (!opts) +				printf(", "); +			printf("group-bound"); +			opts = 0; +		}  		for (i = 0; i < PFTM_MAX; ++i)  			if (r->timeout[i]) {  				if (!opts) @@ -879,6 +1003,8 @@ ifa_load(void)  {  	struct ifaddrs		*ifap, *ifa;  	struct node_host	*n = NULL, *h = NULL; +	struct pfr_buffer	 b; +	struct pfi_if		*p;  	if (getifaddrs(&ifap) < 0)  		err(1, "getifaddrs"); @@ -897,7 +1023,8 @@ ifa_load(void)  		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 *)ifa->ifa_addr)->sin6_scope_id == +		    0) {  			struct sockaddr_in6	*sin6;  			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; @@ -919,6 +1046,10 @@ ifa_load(void)  				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, @@ -930,6 +1061,10 @@ ifa_load(void)  				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;  		} @@ -944,15 +1079,58 @@ ifa_load(void)  			h->tail = n;  		}  	} + +	/* add interface groups, including clonable and dynamic stuff */ +	bzero(&b, sizeof(b)); +	b.pfrb_type = PFRB_IFACES; +	for (;;) { +		if (pfr_buf_grow(&b, b.pfrb_size)) +			err(1, "ifa_load: pfr_buf_grow"); +		b.pfrb_size = b.pfrb_msize; +		if (pfi_get_ifaces(NULL, b.pfrb_caddr, &b.pfrb_size, +		    PFI_FLAG_GROUP)) +			err(1, "ifa_load: pfi_get_ifaces"); +		if (b.pfrb_size <= b.pfrb_msize) +			break; +	} +	PFRB_FOREACH(p, &b) { +		n = calloc(1, sizeof(struct node_host)); +		if (n == NULL) +			err(1, "address: calloc"); +		n->af = AF_LINK; +		n->ifa_flags = PF_IFA_FLAG_GROUP; +		if (p->pfif_flags & PFI_IFLAG_DYNAMIC) +			n->ifa_flags |= PF_IFA_FLAG_DYNAMIC; +		if (p->pfif_flags & PFI_IFLAG_CLONABLE) +			n->ifa_flags |= PF_IFA_FLAG_CLONABLE; +		if (!strcmp(p->pfif_name, "lo")) +			n->ifa_flags |= IFF_LOOPBACK; +		if ((n->ifname = strdup(p->pfif_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) +ifa_exists(const char *ifa_name, int group_ok)  {  	struct node_host	*n; +	char			*p, buf[IFNAMSIZ]; +	int			 group; +	group = !isdigit(ifa_name[strlen(ifa_name) - 1]); +	if (group && !group_ok) +		return (NULL);  	if (iftab == NULL)  		ifa_load(); @@ -960,14 +1138,28 @@ ifa_exists(const char *ifa_name)  		if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ))  			return (n);  	} +	if (!group) { +		/* look for clonable and/or dynamic interface */ +		strlcpy(buf, ifa_name, sizeof(buf)); +		for (p = buf + strlen(buf) - 1; p > buf && isdigit(*p); p--) +			*p = '\0'; +		for (n = iftab; n != NULL; n = n->next) +			if (n->af == AF_LINK && +			    !strncmp(n->ifname, buf, IFNAMSIZ)) +				break; +		if (n != NULL && n->ifa_flags & +		    (PF_IFA_FLAG_DYNAMIC | PF_IFA_FLAG_CLONABLE)) +			return (n);	/* XXX */ +	}  	return (NULL);  }  struct node_host * -ifa_lookup(const char *ifa_name, enum pfctl_iflookup_mode mode) +ifa_lookup(const char *ifa_name, int flags)  {  	struct node_host	*p = NULL, *h = NULL, *n = NULL; -	int			 return_all = 0; +	int			 return_all = 0, got4 = 0, got6 = 0; +	const char		 *last_if = NULL;  	if (!strncmp(ifa_name, "self", IFNAMSIZ))  		return_all = 1; @@ -977,23 +1169,44 @@ ifa_lookup(const char *ifa_name, enum pfctl_iflookup_mode mode)  	for (p = iftab; p; p = p->next) {  		if (!((p->af == AF_INET || p->af == AF_INET6) && -		    (!strncmp(p->ifname, ifa_name, IFNAMSIZ) || return_all))) +		    (!strncmp(p->ifname, ifa_name, strlen(ifa_name)) || +		    return_all))) +			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 (mode == PFCTL_IFLOOKUP_BCAST && p->af != AF_INET) +		if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0)  			continue; -		if (mode == PFCTL_IFLOOKUP_NET && p->ifindex > 0) +		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 (mode == PFCTL_IFLOOKUP_BCAST) +		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 (mode == PFCTL_IFLOOKUP_NET) +		if (flags & PFI_AFLAG_NETWORK)  			set_ipmask(n, unmask(&p->addr.v.a.mask, n->af));  		else {  			if (n->af == AF_INET) { @@ -1018,9 +1231,6 @@ ifa_lookup(const char *ifa_name, enum pfctl_iflookup_mode mode)  			h->tail = n;  		}  	} -	if (h == NULL && mode == PFCTL_IFLOOKUP_HOST) { -		fprintf(stderr, "no IP address found for %s\n", ifa_name); -	}  	return (h);  } @@ -1078,29 +1288,39 @@ host_if(const char *s, int mask)  {  	struct node_host	*n, *h = NULL;  	char			*p, *ps; -	int			 mode = PFCTL_IFLOOKUP_HOST; +	int			 flags = 0; -	if ((p = strrchr(s, ':')) != NULL && -	    (!strcmp(p+1, "network") || !strcmp(p+1, "broadcast"))) { +	if ((ps = strdup(s)) == NULL) +		err(1, "host_if: strdup"); +	while ((p = strrchr(ps, ':')) != NULL) {  		if (!strcmp(p+1, "network")) -			mode = PFCTL_IFLOOKUP_NET; -		if (!strcmp(p+1, "broadcast")) -			mode = PFCTL_IFLOOKUP_BCAST; -		if (mask > -1) { -			fprintf(stderr, "network or broadcast lookup, but " -			    "extra netmask given\n"); +			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);  		} -		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) -			err(1, "host: malloc"); -		strlcpy(ps, s, strlen(s) - strlen(p) + 1); -	} else -		if ((ps = strdup(s)) == NULL) -			err(1, "host_if: strdup"); - -	if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) { +		*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, 1) || !strncmp(ps, "self", IFNAMSIZ)) {  		/* interface with this name exists */ -		h = ifa_lookup(ps, mode); +		h = ifa_lookup(ps, flags);  		for (n = h; n != NULL && mask > -1; n = n->next)  			set_ipmask(n, mask);  	} @@ -1114,21 +1334,27 @@ host_v4(const char *s, int mask)  {  	struct node_host	*h = NULL;  	struct in_addr		 ina; -	int			 bits; +	int			 bits = 32;  	memset(&ina, 0, sizeof(struct in_addr)); -	if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) > -1) { -		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; +	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);  } @@ -1167,12 +1393,20 @@ host_dns(const char *s, int v4mask, int v6mask)  {  	struct addrinfo		 hints, *res0, *res;  	struct node_host	*n, *h = NULL; -	int			 error; +	int			 error, noalias = 0; +	int			 got4 = 0, got6 = 0; +	char			*p, *ps; +	if ((ps = strdup(s)) == NULL) +		err(1, "host_if: 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(s, NULL, &hints, &res0); +	error = getaddrinfo(ps, NULL, &hints, &res0);  	if (error)  		return (h); @@ -1180,6 +1414,17 @@ host_dns(const char *s, int v4mask, int v6mask)  		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"); @@ -1211,6 +1456,7 @@ host_dns(const char *s, int v4mask, int v6mask)  		}  	}  	freeaddrinfo(res0); +	free(ps);  	return (h);  } @@ -1284,3 +1530,45 @@ append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)  	return (0);  } + +int +pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor, +    const char *ruleset) +{ +	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) || +	    strlcpy(trans.ruleset, ruleset, +	    sizeof(trans.ruleset)) >= sizeof(trans.ruleset)) +		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, +    const char *ruleset) +{ +	struct pfioc_trans_e *p; + +	PFRB_FOREACH(p, buf) +		if (rs_num == p->rs_num && !strcmp(anchor, p->anchor) && +		    !strcmp(ruleset, p->ruleset)) +			return (p->ticket); +	errx(1, "pfr_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 index 88047e597324..125201f4f4cf 100644 --- a/contrib/pf/pfctl/pfctl_parser.h +++ b/contrib/pf/pfctl/pfctl_parser.h @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl_parser.h,v 1.67 2003/08/21 19:12:09 frantzen Exp $ */ +/*	$OpenBSD: pfctl_parser.h,v 1.74 2004/02/10 22:26:56 dhartmei Exp $ */  /*   * Copyright (c) 2001 Daniel Hartmeier @@ -45,6 +45,7 @@  #define PF_OPT_VERBOSE2		0x0080  #define PF_OPT_DUMMYACTION	0x0100  #define PF_OPT_DEBUG		0x0200 +#define PF_OPT_SHOWALL		0x0400  #define PF_TH_ALL		0xFF @@ -66,19 +67,13 @@ struct pfctl {  	int tdirty;			/* kernel dirty */  	u_int32_t rule_nr;  	struct pfioc_pooladdr paddr; -	struct pfioc_rule *prule[PF_RULESET_MAX];  	struct pfioc_altq *paltq;  	struct pfioc_queue *pqueue; +	struct pfr_buffer *trans;  	const char *anchor;  	const char *ruleset;  }; -enum pfctl_iflookup_mode { -	PFCTL_IFLOOKUP_HOST, -	PFCTL_IFLOOKUP_NET, -	PFCTL_IFLOOKUP_BCAST -}; -  struct node_if {  	char			 ifname[IFNAMSIZ];  	u_int8_t		 not; @@ -90,6 +85,7 @@ struct node_if {  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 */ @@ -98,6 +94,10 @@ struct node_host {  	struct node_host	*next;  	struct node_host	*tail;  }; +/* special flags used by ifa_exists */ +#define PF_IFA_FLAG_GROUP	0x10000 +#define PF_IFA_FLAG_DYNAMIC	0x20000 +#define PF_IFA_FLAG_CLONABLE	0x40000  struct node_os {  	char			*os; @@ -143,7 +143,7 @@ struct node_tinit {	/* table initializer */  struct pfr_buffer;	/* forward definition */ -int	pfctl_rules(int, char *, int, char *, char *); +int	pfctl_rules(int, char *, int, char *, char *, struct pfr_buffer *);  int	pfctl_add_rule(struct pfctl *, struct pf_rule *);  int	pfctl_add_altq(struct pfctl *, struct pf_altq *); @@ -154,15 +154,18 @@ 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	parse_rules(FILE *, struct pfctl *);  int	parse_flags(char *); -int	pfctl_load_anchors(int, int); +int	pfctl_load_anchors(int, int, 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 *, int);  void	print_tabledef(const char *, int, int, struct node_tinithead *); -void	print_status(struct pf_status *); +void	print_status(struct pf_status *, int);  int	eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *,  	    struct node_queue_opt *); @@ -170,9 +173,9 @@ 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 *); +	    struct node_queue_opt *);  void	 print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *, -	     int, struct node_queue_opt *); +	    int, struct node_queue_opt *);  int	pfctl_define_table(char *, int, int, const char *, const char *,  	    struct pfr_buffer *, u_int32_t); @@ -217,8 +220,8 @@ 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);  void			 ifa_load(void); -struct node_host	*ifa_exists(const char *); -struct node_host	*ifa_lookup(const char *, enum pfctl_iflookup_mode); +struct node_host	*ifa_exists(const char *, int); +struct node_host	*ifa_lookup(const char *, int);  struct node_host	*host(const char *);  int			 append_addr(struct pfr_buffer *, char *, int); diff --git a/contrib/pf/pfctl/pfctl_qstats.c b/contrib/pf/pfctl/pfctl_qstats.c index 23c431ea1fd9..19ca600911bd 100644 --- a/contrib/pf/pfctl/pfctl_qstats.c +++ b/contrib/pf/pfctl/pfctl_qstats.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl_qstats.c,v 1.24 2003/07/31 09:46:08 kjc Exp $ */ +/*	$OpenBSD: pfctl_qstats.c,v 1.29 2004/03/15 15:25:44 dhartmei Exp $ */  /*   * Copyright (c) Henning Brauer <henning@openbsd.org> @@ -81,24 +81,36 @@ void			 pfctl_print_altq_nodestat(int,  void			 update_avg(struct pf_altq_node *);  int -pfctl_show_altq(int dev, int opts, int verbose2) +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); -	if (pfctl_update_qstats(dev, &root)) + +	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)  		return (-1); -	for (node = root; node != NULL; node = node->next) +	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) {  		printf("\n");  		fflush(stdout);  		sleep(STAT_INTERVAL); -		if (pfctl_update_qstats(dev, &root)) +		if (pfctl_update_qstats(dev, &root) == -1)  			return (-1); -		for (node = root; node != NULL; node = node->next) +		for (node = root; node != NULL; node = node->next) { +			if (iface != NULL && strcmp(node->altq.ifname, iface)) +				continue;  			pfctl_print_altq_node(dev, node, 0, opts); +		}  	}  	pfctl_free_altq_node(root);  	return (0); @@ -155,7 +167,7 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root)  			}  		}  	} -	return (0); +	return (mnr);  }  void @@ -245,12 +257,13 @@ pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,  		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))); +		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); +		pfctl_print_altq_node(dev, child, level + 1, opts);  }  void @@ -277,10 +290,10 @@ print_cbqstats(struct queue_stats cur)  {  	printf("  [ pkts: %10llu  bytes: %10llu  "  	    "dropped pkts: %6llu bytes: %6llu ]\n", -	    cur.data.cbq_stats.xmit_cnt.packets, -	    cur.data.cbq_stats.xmit_cnt.bytes, -	    cur.data.cbq_stats.drop_cnt.packets, -	    cur.data.cbq_stats.drop_cnt.bytes); +	    (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); @@ -298,10 +311,10 @@ print_priqstats(struct queue_stats cur)  {  	printf("  [ pkts: %10llu  bytes: %10llu  "  	    "dropped pkts: %6llu bytes: %6llu ]\n", -	    cur.data.priq_stats.xmitcnt.packets, -	    cur.data.priq_stats.xmitcnt.bytes, -	    cur.data.priq_stats.dropcnt.packets, -	    cur.data.priq_stats.dropcnt.bytes); +	    (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); @@ -318,10 +331,10 @@ print_hfscstats(struct queue_stats cur)  {  	printf("  [ pkts: %10llu  bytes: %10llu  "  	    "dropped pkts: %6llu bytes: %6llu ]\n", -	    cur.data.hfsc_stats.xmit_cnt.packets, -	    cur.data.hfsc_stats.xmit_cnt.bytes, -	    cur.data.hfsc_stats.drop_cnt.packets, -	    cur.data.hfsc_stats.drop_cnt.bytes); +	    (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); diff --git a/contrib/pf/pfctl/pfctl_radix.c b/contrib/pf/pfctl/pfctl_radix.c index 788522c530d9..04fb4872ef6d 100644 --- a/contrib/pf/pfctl/pfctl_radix.c +++ b/contrib/pf/pfctl/pfctl_radix.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl_radix.c,v 1.21 2003/09/24 09:12:35 cedric Exp $ */ +/*	$OpenBSD: pfctl_radix.c,v 1.24 2004/02/10 18:29:30 henning Exp $ */  /*   * Copyright (c) 2002 Cedric Berger @@ -259,7 +259,8 @@ pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,  {  	struct pfioc_table io; -	if (tbl == NULL || size == NULL || *size < 0 || (*size && addr == NULL)) { +	if (tbl == NULL || size == NULL || *size < 0 || +	    (*size && addr == NULL)) {  		errno = EINVAL;  		return (-1);  	} @@ -281,7 +282,8 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,  {  	struct pfioc_table io; -	if (tbl == NULL || size == NULL || *size < 0 || (*size && addr == NULL)) { +	if (tbl == NULL || size == NULL || *size < 0 || +	    (*size && addr == NULL)) {  		errno = EINVAL;  		return (-1);  	} @@ -454,11 +456,40 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,  	return (0);  } +/* interface management code */ + +int +pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags) +{ +	struct pfioc_iface io; + +	if (size == NULL || *size < 0 || (*size && buf == NULL)) { +		errno = EINVAL; +		return (-1); +	} +	bzero(&io, sizeof io); +	io.pfiio_flags = flags; +	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_if), sizeof(struct pfioc_trans_e)  };  /* diff --git a/contrib/pf/pfctl/pfctl_table.c b/contrib/pf/pfctl/pfctl_table.c index 57bdf19c6da5..5d4c3d9f5113 100644 --- a/contrib/pf/pfctl/pfctl_table.c +++ b/contrib/pf/pfctl/pfctl_table.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pfctl_table.c,v 1.50 2003/08/29 21:47:36 cedric Exp $ */ +/*	$OpenBSD: pfctl_table.c,v 1.59 2004/03/15 15:25:44 dhartmei Exp $ */  /*   * Copyright (c) 2002 Cedric Berger @@ -61,12 +61,19 @@ 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_if *, int); +static void	oprintf(int, int, const char *, int *, 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)) &&	\ @@ -115,12 +122,12 @@ int  pfctl_table(int argc, char *argv[], char *tname, const char *command,      char *file, const char *anchor, const char *ruleset, 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; +	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(); @@ -165,6 +172,10 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,  			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); @@ -243,7 +254,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,  					    opts & PF_OPT_USEDNS);  	} else if (!strcmp(command, "show")) {  		b.pfrb_type = (opts & PF_OPT_VERBOSE) ? -		    PFRB_ASTATS : PFRB_ADDRS; +			PFRB_ASTATS : PFRB_ADDRS;  		if (argc || file != NULL)  			usage();  		for (;;) { @@ -325,9 +336,9 @@ print_table(struct pfr_table *ta, int verbose, int debug)  		    (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',  		    ta->pfrt_name);  		if (ta->pfrt_anchor[0]) -		    printf("\t%s", ta->pfrt_anchor); +			printf("\t%s", ta->pfrt_anchor);  		if (ta->pfrt_ruleset[0]) -		    printf(":%s", ta->pfrt_ruleset); +			printf(":%s", ta->pfrt_ruleset);  		puts("");  	} else  		puts(ta->pfrt_name); @@ -348,13 +359,14 @@ print_tstats(struct pfr_tstats *ts, int debug)  	    ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],  	    ts->pfrts_refcnt[PFR_REFCNT_RULE]);  	printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n", -	    ts->pfrts_nomatch, ts->pfrts_match); +	    (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], -			    ts->pfrts_packets[dir][op], -			    ts->pfrts_bytes[dir][op]); +			    (unsigned long long)ts->pfrts_packets[dir][op], +			    (unsigned long long)ts->pfrts_bytes[dir][op]);  }  int @@ -431,8 +443,8 @@ print_astats(struct pfr_astats *as, int dns)  		for (op = 0; op < PFR_OP_ADDR_MAX; op++)  			printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",  			    stats_text[dir][op], -			    as->pfras_packets[dir][op], -			    as->pfras_bytes[dir][op]); +			    (unsigned long long)as->pfras_packets[dir][op], +			    (unsigned long long)as->pfras_bytes[dir][op]);  }  void @@ -449,12 +461,11 @@ pfctl_define_table(char *name, int flags, int addrs, const char *anchor,  	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, +	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) || -	    strlcpy(tbl.pfrt_ruleset, ruleset, -	    sizeof(tbl.pfrt_ruleset)) >= sizeof(tbl.pfrt_ruleset)) +	    strlcpy(tbl.pfrt_ruleset, ruleset, sizeof(tbl.pfrt_ruleset)) >= +	    sizeof(tbl.pfrt_ruleset))  		errx(1, "pfctl_define_table: strlcpy");  	tbl.pfrt_flags = flags; @@ -477,7 +488,7 @@ warn_namespace_collision(const char *filter)  		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"); +			err(1, "pfr_get_tables");  		if (b.pfrb_size <= b.pfrb_msize)  			break;  	} @@ -522,3 +533,79 @@ xprintf(int opts, const char *fmt, ...)  	else  		fprintf(stderr, ".\n");  } + + +/* interface stuff */ + +int +pfctl_show_ifaces(const char *filter, int opts) +{ +	struct pfr_buffer	 b; +	struct pfi_if		*p; +	int			 i = 0, f = PFI_FLAG_GROUP|PFI_FLAG_INSTANCE; + +	if (filter != NULL && *filter && !isdigit(filter[strlen(filter)-1])) +		f &= ~PFI_FLAG_INSTANCE; +	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, f)) { +			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_if *p, int opts) +{ +	time_t	tzero = p->pfif_tzero; +	int	flags = (opts & PF_OPT_VERBOSE) ? p->pfif_flags : 0; +	int	first = 1; +	int	i, af, dir, act; + +	printf("%s", p->pfif_name); +	oprintf(flags, PFI_IFLAG_INSTANCE, "instance", &first, 0); +	oprintf(flags, PFI_IFLAG_GROUP, "group", &first, 0); +	oprintf(flags, PFI_IFLAG_CLONABLE, "clonable", &first, 0); +	oprintf(flags, PFI_IFLAG_DYNAMIC, "dynamic", &first, 0); +	oprintf(flags, PFI_IFLAG_ATTACHED, "attached", &first, 1); +	printf("\n"); + +	if (!(opts & PF_OPT_VERBOSE2)) +		return; +	printf("\tCleared:     %s", ctime(&tzero)); +	printf("\tReferences:  [ States:  %-18d Rules: %-18d ]\n", +	    p->pfif_states, p->pfif_rules); +	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->pfif_packets[af][dir][act], +		    (unsigned long long)p->pfif_bytes[af][dir][act]); +	} +} + +void +oprintf(int flags, int flag, const char *s, int *first, int last) +{ +	if (flags & flag) { +		printf(*first ? "\t(%s" : ", %s", s); +		*first = 0; +	} +	if (last && !*first) +		printf(")"); +} + diff --git a/contrib/pf/pflogd/pflogd.8 b/contrib/pf/pflogd/pflogd.8 index ab632593ac7d..ac8fe78aa0a7 100644 --- a/contrib/pf/pflogd/pflogd.8 +++ b/contrib/pf/pflogd/pflogd.8 @@ -1,4 +1,4 @@ -.\"	$OpenBSD: pflogd.8,v 1.22 2003/06/03 13:16:08 jmc Exp $ +.\"	$OpenBSD: pflogd.8,v 1.24 2004/01/16 10:45:49 jmc Exp $  .\"  .\" Copyright (c) 2001 Can Erkin Acar.  All rights reserved.  .\" @@ -32,7 +32,7 @@  .Nd packet filter logging daemon  .Sh SYNOPSIS  .Nm pflogd -.Op Fl D +.Op Fl Dx  .Op Fl d Ar delay  .Op Fl f Ar filename  .Op Fl s Ar snaplen @@ -57,11 +57,11 @@ hopefully offline in case there are bugs in the packet parsing code of  .Pp  .Nm  closes and then re-opens the log file when it receives -.Va SIGHUP , +.Dv SIGHUP ,  permitting  .Xr newsyslog 8  to rotate logfiles automatically. -.Va SIGALRM +.Dv SIGALRM  causes  .Nm  to flush the current logfile buffers to the disk, thus making the most @@ -71,22 +71,32 @@ The buffers are also flushed every  seconds.  .Pp  If the log file contains data after a restart or a -.Va SIGHUP , +.Dv SIGHUP ,  new logs are appended to the existing file.  If the existing log file was created with a different snaplen,  .Nm  temporarily uses the old snaplen to keep the log file consistent.  .Pp +.Nm +tries to preserve the integrity of the log file against I/O errors. +Furthermore, integrity of an existing log file is verified before +appending. +If there is an invalid log file or an I/O error, logging is suspended until a +.Dv SIGHUP +or a +.Dv SIGALRM +is received. +.Pp  The options are as follows:  .Bl -tag -width Ds -.It Fl d Ar delay -Time in seconds to delay between automatic flushes of the file. -This may be specified with a value between 5 and 3600 seconds. -If not specified, the default is 60 seconds.  .It Fl D  Debugging mode.  .Nm  does not disassociate from the controlling terminal. +.It Fl d Ar delay +Time in seconds to delay between automatic flushes of the file. +This may be specified with a value between 5 and 3600 seconds. +If not specified, the default is 60 seconds.  .It Fl f Ar filename  Log output filename.  Default is @@ -98,6 +108,8 @@ bytes of data from each packet rather than the default of 96.  The default of 96 is adequate for IP, ICMP, TCP, and UDP headers but may  truncate protocol information for other protocols.  Other file parsers may desire a higher snaplen. +.It Fl x +Check the integrity of an existing log file, and return.  .It Ar expression  Selects which packets will be dumped, using the regular language of  .Xr tcpdump 8 . @@ -106,13 +118,13 @@ Selects which packets will be dumped, using the regular language of  .Bl -tag -width /var/run/pflogd.pid -compact  .It Pa /var/run/pflogd.pid  Process ID of the currently running -.Nm pflogd . +.Nm .  .It Pa /var/log/pflog  Default log file.  .El  .Sh EXAMPLES  Log specific tcp packets to a different log file with a large snaplen -(useful with a log-all rule to dump complete sessions) +(useful with a log-all rule to dump complete sessions):  .Bd -literal -offset indent  # pflogd -s 1600 -f suspicious.log port 80 and host evilhost  .Ed @@ -123,7 +135,8 @@ Display binary logs:  .Ed  .Pp  Display the logs in real time (this does not interfere with the -operation of pflogd): +operation of +.Nm ) :  .Bd -literal -offset indent  # tcpdump -n -e -ttt -i pflog0  .Ed @@ -133,7 +146,7 @@ structure defined in  .Aq Ar net/if_pflog.h .  Tcpdump can restrict the output  to packets logged on a specified interface, a rule number, a reason, -a direction, an ip family or an action. +a direction, an IP family or an action.  .Pp  .Bl -tag -width "reason match " -compact  .It ip @@ -141,9 +154,9 @@ Address family equals IPv4.  .It ip6  Address family equals IPv6.  .It ifname kue0 -Interface name equals "kue0" +Interface name equals "kue0".  .It on kue0 -Interface name equals "kue0" +Interface name equals "kue0".  .It rulenum 10  Rule number equals 10.  .It reason match diff --git a/contrib/pf/pflogd/pflogd.c b/contrib/pf/pflogd/pflogd.c index 9777f151627f..7e19ae66ebdb 100644 --- a/contrib/pf/pflogd/pflogd.c +++ b/contrib/pf/pflogd/pflogd.c @@ -1,4 +1,4 @@ -/*	$OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david Exp $	*/ +/*	$OpenBSD: pflogd.c,v 1.27 2004/02/13 19:01:57 otto Exp $	*/  /*   * Copyright (c) 2001 Theo de Raadt @@ -31,6 +31,7 @@   */  #include <sys/types.h> +#include <sys/ioctl.h>  #include <sys/file.h>  #include <sys/stat.h>  #include <stdio.h> @@ -45,21 +46,14 @@  #include <stdarg.h>  #include <fcntl.h>  #include <util.h> - -#define DEF_SNAPLEN 116		/* default plus allow for larger header of pflog */ -#define PCAP_TO_MS 500		/* pcap read timeout (ms) */ -#define PCAP_NUM_PKTS 1000	/* max number of packets to process at each loop */ -#define PCAP_OPT_FIL 0		/* filter optimization */ -#define FLUSH_DELAY 60		/* flush delay */ - -#define PFLOGD_LOG_FILE		"/var/log/pflog" -#define PFLOGD_DEFAULT_IF	"pflog0" +#include "pflogd.h"  pcap_t *hpcap; -pcap_dumper_t *dpcap; +static FILE *dpcap;  int Debug = 0; -int snaplen = DEF_SNAPLEN; +static int snaplen = DEF_SNAPLEN; +static int cur_snaplen = DEF_SNAPLEN;  volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; @@ -72,15 +66,43 @@ char errbuf[PCAP_ERRBUF_SIZE];  int log_debug = 0;  unsigned int delay = FLUSH_DELAY; -char *copy_argv(char * const *argv); +char *copy_argv(char * const *); +void  dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); +void  dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *); +int   flush_buffer(FILE *);  int   init_pcap(void); -void  logmsg(int priority, const char *message, ...); +void  logmsg(int, const char *, ...); +void  purge_buffer(void);  int   reset_dump(void); +int   scan_dump(FILE *, off_t); +int   set_snaplen(int); +void  set_suspended(int);  void  sig_alrm(int);  void  sig_close(int);  void  sig_hup(int);  void  usage(void); +/* buffer must always be greater than snaplen */ +static int    bufpkt = 0;	/* number of packets in buffer */ +static int    buflen = 0;	/* allocated size of buffer */ +static char  *buffer = NULL;	/* packet buffer */ +static char  *bufpos = NULL;	/* position in buffer */ +static int    bufleft = 0;	/* bytes left in buffer */ + +/* if error, stop logging but count dropped packets */ +static int suspended = -1; +static long packets_dropped = 0; + +void +set_suspended(int s) +{ +	if (suspended == s) +		return; + +	suspended = s; +	setproctitle("[%s] -s %d -f %s", +            suspended ? "suspended" : "running", cur_snaplen, filename); +}  char *  copy_argv(char * const *argv) @@ -125,7 +147,7 @@ logmsg(int pri, const char *message, ...)  __dead void  usage(void)  { -	fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] "); +	fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename] ");  	fprintf(stderr, "[-s snaplen] [expression]\n");  	exit(1);  } @@ -148,37 +170,60 @@ sig_alrm(int sig)  	gotsig_alrm = 1;  } -int -init_pcap(void) +void +set_pcap_filter(void)  {  	struct bpf_program bprog; -	pcap_t *oldhpcap = hpcap; +	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) +		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); +	else { +		if (pcap_setfilter(hpcap, &bprog) < 0) +			logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); +		pcap_freecode(&bprog); +	} +} + +int +init_pcap(void) +{  	hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);  	if (hpcap == NULL) {  		logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); -		hpcap = oldhpcap;  		return (-1);  	} -	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) -		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); -	else if (pcap_setfilter(hpcap, &bprog) < 0) -		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); -	if (filter != NULL) -		free(filter); -  	if (pcap_datalink(hpcap) != DLT_PFLOG) {  		logmsg(LOG_ERR, "Invalid datalink type");  		pcap_close(hpcap); -		hpcap = oldhpcap; +		hpcap = NULL;  		return (-1);  	} -	if (oldhpcap) -		pcap_close(oldhpcap); +	set_pcap_filter(); + +	cur_snaplen = snaplen = pcap_snapshot(hpcap); + +	/* lock */ +	if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) { +		logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); +		return (-1); +	} + +	return (0); +} + +int +set_snaplen(int snap) +{ +	if (priv_set_snaplen(snap)) +		return (1); + +	if (cur_snaplen > snap) +		purge_buffer(); + +	cur_snaplen = snap; -	snaplen = pcap_snapshot(hpcap);  	return (0);  } @@ -187,45 +232,51 @@ reset_dump(void)  {  	struct pcap_file_header hdr;  	struct stat st; -	int tmpsnap; +	int fd;  	FILE *fp;  	if (hpcap == NULL) -		return (1); +		return (-1); +  	if (dpcap) { -		pcap_dump_close(dpcap); -		dpcap = 0; +		flush_buffer(dpcap); +		fclose(dpcap); +		dpcap = NULL;  	}  	/*  	 * Basically reimplement pcap_dump_open() because it truncates  	 * files and duplicates headers and such.  	 */ -	fp = fopen(filename, "a+"); +	fd = priv_open_log(); +	if (fd < 0) +		return (1); + +	fp = fdopen(fd, "a+"); +  	if (fp == NULL) { -		snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", -		    filename, pcap_strerror(errno)); -		logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); +		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));  		return (1);  	}  	if (fstat(fileno(fp), &st) == -1) { -		snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", -		    filename, pcap_strerror(errno)); -		logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); +		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));  		return (1);  	} -	dpcap = (pcap_dumper_t *)fp; +	/* set FILE unbuffered, we do our own buffering */ +	if (setvbuf(fp, NULL, _IONBF, 0)) { +		logmsg(LOG_ERR, "Failed to set output buffers"); +		return (1); +	}  #define TCPDUMP_MAGIC 0xa1b2c3d4  	if (st.st_size == 0) { -		if (snaplen != pcap_snapshot(hpcap)) { +		if (snaplen != cur_snaplen) {  			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); -			if (init_pcap()) { -				logmsg(LOG_ERR, "Failed to initialize"); -				if (hpcap == NULL) return (-1); -				logmsg(LOG_NOTICE, "Using old settings"); +			if (set_snaplen(snaplen)) { +				logmsg(LOG_WARNING, +				    "Failed, using old settings");  			}  		}  		hdr.magic = TCPDUMP_MAGIC; @@ -237,58 +288,224 @@ reset_dump(void)  		hdr.linktype = hpcap->linktype;  		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { -			dpcap = NULL;  			fclose(fp); -			return (-1); +			return (1);  		} -		return (0); +	} else if (scan_dump(fp, st.st_size)) { +		/* XXX move file and continue? */ +		fclose(fp); +		return (1);  	} +	dpcap = fp; + +	set_suspended(0); +	flush_buffer(fp); + +	return (0); +} + +int +scan_dump(FILE *fp, off_t size) +{ +	struct pcap_file_header hdr; +	struct pcap_pkthdr ph; +	off_t pos; +  	/* -	 * XXX Must read the file, compare the header against our new +	 * Must read the file, compare the header against our new  	 * options (in particular, snaplen) and adjust our options so -	 * that we generate a correct file. +	 * that we generate a correct file. Furthermore, check the file +	 * for consistency so that we can append safely. +	 * +	 * XXX this may take a long time for large logs.  	 */  	(void) fseek(fp, 0L, SEEK_SET); -	if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) { -		if (hdr.magic != TCPDUMP_MAGIC || -		    hdr.version_major != PCAP_VERSION_MAJOR || -		    hdr.version_minor != PCAP_VERSION_MINOR || -		    hdr.linktype != hpcap->linktype) { -			logmsg(LOG_ERR, -			    "Invalid/incompatible log file, move it away"); -			fclose(fp); -			return (1); -		    } -		if (hdr.snaplen != snaplen) { + +	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { +		logmsg(LOG_ERR, "Short file header"); +		return (1); +	} + +	if (hdr.magic != TCPDUMP_MAGIC || +	    hdr.version_major != PCAP_VERSION_MAJOR || +	    hdr.version_minor != PCAP_VERSION_MINOR || +	    hdr.linktype != hpcap->linktype || +	    hdr.snaplen > PFLOGD_MAXSNAPLEN) { +		logmsg(LOG_ERR, "Invalid/incompatible log file, move it away"); +		return (1); +	} + +	pos = sizeof(hdr); + +	while (!feof(fp)) { +		off_t len = fread((char *)&ph, 1, sizeof(ph), fp); +		if (len == 0) +			break; + +		if (len != sizeof(ph)) +			goto error; +		if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN) +			goto error; +		pos += sizeof(ph) + ph.caplen; +		if (pos > size) +			goto error; +		fseek(fp, ph.caplen, SEEK_CUR); +	} + +	if (pos != size) +		goto error; + +	if (hdr.snaplen != cur_snaplen) { +		logmsg(LOG_WARNING, +		       "Existing file has different snaplen %u, using it", +		       hdr.snaplen); +		if (set_snaplen(hdr.snaplen)) {  			logmsg(LOG_WARNING, -			    "Existing file specifies a snaplen of %u, using it", -			    hdr.snaplen); -			tmpsnap = snaplen; -			snaplen = hdr.snaplen; -			if (init_pcap()) { -				logmsg(LOG_ERR, "Failed to re-initialize"); -				if (hpcap == 0) -					return (-1); -				logmsg(LOG_NOTICE, -					"Using old settings, offset: %llu", -					(unsigned long long)st.st_size); -			} -			snaplen = tmpsnap; +			       "Failed, using old settings, offset %llu", +			       (unsigned long long) size);  		}  	} -	(void) fseek(fp, 0L, SEEK_END);  	return (0); + + error: +	logmsg(LOG_ERR, "Corrupted log file."); +	return (1); +} + +/* dump a packet directly to the stream, which is unbuffered */ +void +dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) +{ +	FILE *f = (FILE *)user; + +	if (suspended) { +		packets_dropped++; +		return; +	} + +	if (fwrite((char *)h, sizeof(*h), 1, f) != 1) { +		/* try to undo header to prevent corruption */ +		off_t pos = ftello(f); +		if (pos < sizeof(*h) || +		    ftruncate(fileno(f), pos - sizeof(*h))) { +			logmsg(LOG_ERR, "Write failed, corrupted logfile!"); +			set_suspended(1); +			gotsig_close = 1; +			return; +		} +		goto error; +	} + +	if (fwrite((char *)sp, h->caplen, 1, f) != 1) +		goto error; + +	return; + +error: +	set_suspended(1); +	packets_dropped ++; +	logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno)); +} + +int +flush_buffer(FILE *f) +{ +	off_t offset; +	int len = bufpos - buffer; + +	if (len <= 0) +		return (0); + +	offset = ftello(f); +	if (offset == (off_t)-1) { +		set_suspended(1); +		logmsg(LOG_ERR, "Logging suspended: ftello: %s", +		    strerror(errno)); +		return (1); +	} + +	if (fwrite(buffer, len, 1, f) != 1) { +		set_suspended(1); +		logmsg(LOG_ERR, "Logging suspended: fwrite: %s", +		    strerror(errno)); +		ftruncate(fileno(f), offset); +		return (1); +	} + +	set_suspended(0); +	bufpos = buffer; +	bufleft = buflen; +	bufpkt = 0; + +	return (0); +} + +void +purge_buffer(void) +{ +	packets_dropped += bufpkt; + +	set_suspended(0); +	bufpos = buffer; +	bufleft = buflen; +	bufpkt = 0; +} + +/* append packet to the buffer, flushing if necessary */ +void +dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) +{ +	FILE *f = (FILE *)user; +	size_t len = sizeof(*h) + h->caplen; + +	if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) { +		logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped", +		       len, cur_snaplen, snaplen); +		packets_dropped++; +		return; +	} + +	if (len <= bufleft) +		goto append; + +	if (suspended) { +		packets_dropped++; +		return; +	} + +	if (flush_buffer(f)) { +		packets_dropped++; +		return; +	} + +	if (len > bufleft) { +		dump_packet_nobuf(user, h, sp); +		return; +	} + + append:	 +	memcpy(bufpos, h, sizeof(*h)); +	memcpy(bufpos + sizeof(*h), sp, h->caplen); + +	bufpos += len; +	bufleft -= len; +	bufpkt++; + +	return;  }  int  main(int argc, char **argv)  {  	struct pcap_stat pstat; -	int ch, np; +	int ch, np, Xflag = 0; +	pcap_handler phandler = dump_packet; -	while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) { +	closefrom(STDERR_FILENO + 1); + +	while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) {  		switch (ch) {  		case 'D':  			Debug = 1; @@ -305,6 +522,11 @@ main(int argc, char **argv)  			snaplen = atoi(optarg);  			if (snaplen <= 0)  				snaplen = DEF_SNAPLEN; +			if (snaplen > PFLOGD_MAXSNAPLEN) +				snaplen = PFLOGD_MAXSNAPLEN; +			break; +		case 'x': +			Xflag++;  			break;  		default:  			usage(); @@ -327,32 +549,57 @@ main(int argc, char **argv)  	(void)umask(S_IRWXG | S_IRWXO); -	signal(SIGTERM, sig_close); -	signal(SIGINT, sig_close); -	signal(SIGQUIT, sig_close); -	signal(SIGALRM, sig_alrm); -	signal(SIGHUP, sig_hup); -	alarm(delay); - +	/* filter will be used by the privileged process */  	if (argc) {  		filter = copy_argv(argv);  		if (filter == NULL)  			logmsg(LOG_NOTICE, "Failed to form filter expression");  	} +	/* initialize pcap before dropping privileges */  	if (init_pcap()) {  		logmsg(LOG_ERR, "Exiting, init failure");  		exit(1);  	} -	if (reset_dump()) { -		logmsg(LOG_ERR, "Failed to open log file %s", filename); -		pcap_close(hpcap); +	/* Privilege separation begins here */ +	if (priv_init()) { +		logmsg(LOG_ERR, "unable to privsep");  		exit(1);  	} +	setproctitle("[initializing]"); +	/* Process is now unprivileged and inside a chroot */ +	signal(SIGTERM, sig_close); +	signal(SIGINT, sig_close); +	signal(SIGQUIT, sig_close); +	signal(SIGALRM, sig_alrm); +	signal(SIGHUP, sig_hup); +	alarm(delay); + +	buffer = malloc(PFLOGD_BUFSIZE); + +	if (buffer == NULL) { +		logmsg(LOG_WARNING, "Failed to allocate output buffer"); +		phandler = dump_packet_nobuf; +	} else { +		bufleft = buflen = PFLOGD_BUFSIZE; +		bufpos = buffer; +		bufpkt = 0; +	} + +	if (reset_dump()) { +		if (Xflag) +			return (1); + +		logmsg(LOG_ERR, "Logging suspended: open error"); +		set_suspended(1); +	} else if (Xflag) +		return (0); +  	while (1) { -		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap); +		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, +		    dump_packet, (u_char *)dpcap);  		if (np < 0)  			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); @@ -360,38 +607,34 @@ main(int argc, char **argv)  			break;  		if (gotsig_hup) {  			if (reset_dump()) { -				logmsg(LOG_ERR, "Failed to open log file!"); -				break; +				logmsg(LOG_ERR, +				    "Logging suspended: open error"); +				set_suspended(1);  			} -			logmsg(LOG_NOTICE, "Reopened logfile");  			gotsig_hup = 0;  		}  		if (gotsig_alrm) { -			/* XXX pcap_dumper is an incomplete type which libpcap -			 * casts to a FILE* currently.  For now it is safe to -			 * make the same assumption, however this may change -			 * in the future. -			 */ -			if (dpcap) { -				if (fflush((FILE *)dpcap) == EOF) { -					break; -				} -			} +			if (dpcap) +				flush_buffer(dpcap);  			gotsig_alrm = 0;  			alarm(delay);  		}  	} -	logmsg(LOG_NOTICE, "Exiting due to signal"); -	if (dpcap) -		pcap_dump_close(dpcap); +	logmsg(LOG_NOTICE, "Exiting"); +	if (dpcap) { +		flush_buffer(dpcap); +		fclose(dpcap); +	} +	purge_buffer();  	if (pcap_stats(hpcap, &pstat) < 0)  		logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap));  	else -		logmsg(LOG_NOTICE, "%u packets received, %u dropped", -		    pstat.ps_recv, pstat.ps_drop); +		logmsg(LOG_NOTICE, +		    "%u packets received, %u/%u dropped (kernel/pflogd)", +		    pstat.ps_recv, pstat.ps_drop, packets_dropped);  	pcap_close(hpcap);  	if (!Debug) diff --git a/contrib/pf/pflogd/pflogd.h b/contrib/pf/pflogd/pflogd.h new file mode 100644 index 000000000000..3baecb66fe67 --- /dev/null +++ b/contrib/pf/pflogd/pflogd.h @@ -0,0 +1,47 @@ +/*	$OpenBSD: pflogd.h,v 1.2 2004/01/15 20:15:14 canacar Exp $ */ + +/* + * Copyright (c) 2003 Can Erkin Acar + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/limits.h> +#include <pcap.h> + +#define DEF_SNAPLEN 116		/* default plus allow for larger header of pflog */ +#define PCAP_TO_MS 500		/* pcap read timeout (ms) */ +#define PCAP_NUM_PKTS 1000	/* max number of packets to process at each loop */ +#define PCAP_OPT_FIL 1		/* filter optimization */ +#define FLUSH_DELAY 60		/* flush delay */ + +#define PFLOGD_LOG_FILE		"/var/log/pflog" +#define PFLOGD_DEFAULT_IF	"pflog0" + +#define PFLOGD_MAXSNAPLEN	INT_MAX +#define PFLOGD_BUFSIZE		65536	/* buffer size for incoming packets */ + +void  logmsg(int priority, const char *message, ...); + +/* Privilege separation */ +int	priv_init(void); +int	priv_set_snaplen(int snaplen); +int	priv_open_log(void); +pcap_t *pcap_open_live_fd(int fd, int snaplen, char *ebuf); + +void set_pcap_filter(void); +/* File descriptor send/recv */ +void send_fd(int, int); +int  receive_fd(int); + +extern int Debug; diff --git a/contrib/pf/pflogd/privsep.c b/contrib/pf/pflogd/privsep.c new file mode 100644 index 000000000000..50807ada4e45 --- /dev/null +++ b/contrib/pf/pflogd/privsep.c @@ -0,0 +1,305 @@ +/*	$OpenBSD: privsep.c,v 1.8 2004/03/14 19:17:05 otto Exp $	*/ + +/* + * Copyright (c) 2003 Can Erkin Acar + * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/bpf.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pcap.h> +#include <pcap-int.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "pflogd.h" + +enum cmd_types { +	PRIV_SET_SNAPLEN,	/* set the snaplength */ +	PRIV_OPEN_LOG		/* open logfile for appending */ +}; + +static int priv_fd = -1; +static volatile pid_t child_pid = -1; + +volatile sig_atomic_t gotsig_chld = 0; + +static void sig_pass_to_chld(int); +static void sig_chld(int); +static int  may_read(int, void *, size_t); +static void must_read(int, void *, size_t); +static void must_write(int, void *, size_t); +static int  set_snaplen(int snap); + +/* bpf filter expression common to parent and child */ +extern char *filter; +extern char *errbuf; +extern char *filename; +extern pcap_t *hpcap; + +/* based on syslogd privsep */ +int +priv_init(void) +{ +	int i, fd, socks[2], cmd; +	int snaplen, ret; +	struct passwd *pw; + +	for (i = 1; i < _NSIG; i++) +		signal(i, SIG_DFL); + +	/* Create sockets */ +	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) +		err(1, "socketpair() failed"); + +	pw = getpwnam("_pflogd"); +	if (pw == NULL) +		errx(1, "unknown user _pflogd"); +	endpwent(); + +	child_pid = fork(); +	if (child_pid < 0) +		err(1, "fork() failed"); + +	if (!child_pid) { +		gid_t gidset[1]; + +		/* Child - drop privileges and return */ +		if (chroot(pw->pw_dir) != 0) +			err(1, "unable to chroot"); +		if (chdir("/") != 0) +			err(1, "unable to chdir"); + +		gidset[0] = pw->pw_gid; +		if (setgroups(1, gidset) == -1) +			err(1, "setgroups() failed"); +		if (setegid(pw->pw_gid) == -1) +			err(1, "setegid() failed"); +		if (setgid(pw->pw_gid) == -1) +			err(1, "setgid() failed"); +		if (seteuid(pw->pw_uid) == -1) +			err(1, "seteuid() failed"); +		if (setuid(pw->pw_uid) == -1) +			err(1, "setuid() failed"); +		close(socks[0]); +		priv_fd = socks[1]; +		return 0; +	} + +	/* Father */ +	/* Pass ALRM/TERM/HUP through to child, and accept CHLD */ +	signal(SIGALRM, sig_pass_to_chld); +	signal(SIGTERM, sig_pass_to_chld); +	signal(SIGHUP,  sig_pass_to_chld); +	signal(SIGCHLD, sig_chld); + +	setproctitle("[priv]"); +	close(socks[1]); + +	while (!gotsig_chld) { +		if (may_read(socks[0], &cmd, sizeof(int))) +			break; +		switch (cmd) { +		case PRIV_SET_SNAPLEN: +			logmsg(LOG_DEBUG, +			    "[priv]: msg PRIV_SET_SNAPLENGTH received"); +			must_read(socks[0], &snaplen, sizeof(int)); + +			ret = set_snaplen(snaplen); +			if (ret) { +				logmsg(LOG_NOTICE, +				   "[priv]: set_snaplen failed for snaplen %d", +				   snaplen); +			} + +			must_write(socks[0], &ret, sizeof(int)); +			break; + +		case PRIV_OPEN_LOG: +			logmsg(LOG_DEBUG, +			    "[priv]: msg PRIV_OPEN_LOG received"); +			/* create or append logs but do not follow symlinks */ +			fd = open(filename, +			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, +			    0600); +			if (fd < 0) +				logmsg(LOG_NOTICE, +				    "[priv]: failed to open %s: %s", +				    filename, strerror(errno)); +			send_fd(socks[0], fd); +			close(fd); +			break; + +		default: +			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); +			_exit(1); +			/* NOTREACHED */ +		} +	} + +	_exit(1); +} + +/* this is called from parent */ +static int +set_snaplen(int snap) +{ +	if (hpcap == NULL) +		return (1); + +	hpcap->snapshot = snap; +	set_pcap_filter(); + +	return 0; +} + + +/* + * send the snaplength to privileged process + */ +int +priv_set_snaplen(int snaplen) +{ +	int cmd, ret; + +	if (priv_fd < 0) +		errx(1, "%s: called from privileged portion", __func__); + +	cmd = PRIV_SET_SNAPLEN; + +	must_write(priv_fd, &cmd, sizeof(int)); +	must_write(priv_fd, &snaplen, sizeof(int)); + +	must_read(priv_fd, &ret, sizeof(int)); + +	/* also set hpcap->snapshot in child */ +	if (ret == 0) +		hpcap->snapshot = snaplen; + +	return (ret); +} + +/* Open log-file */ +int +priv_open_log(void) +{ +	int cmd, fd; + +	if (priv_fd < 0) +		errx(1, "%s: called from privileged portion\n", __func__); + +	cmd = PRIV_OPEN_LOG; +	must_write(priv_fd, &cmd, sizeof(int)); +	fd = receive_fd(priv_fd); + +	return (fd); +} + +/* If priv parent gets a TERM or HUP, pass it through to child instead */ +static void +sig_pass_to_chld(int sig) +{ +	int oerrno = errno; + +	if (child_pid != -1) +		kill(child_pid, sig); +	errno = oerrno; +} + +/* if parent gets a SIGCHLD, it will exit */ +static void +sig_chld(int sig) +{ +	gotsig_chld = 1; +} + +/* Read all data or return 1 for error.  */ +static int +may_read(int fd, void *buf, size_t n) +{ +	char *s = buf; +	ssize_t res, pos = 0; + +	while (n > pos) { +		res = read(fd, s + pos, n - pos); +		switch (res) { +		case -1: +			if (errno == EINTR || errno == EAGAIN) +				continue; +		case 0: +			return (1); +		default: +			pos += res; +		} +	} +	return (0); +} + +/* Read data with the assertion that it all must come through, or + * else abort the process.  Based on atomicio() from openssh. */ +static void +must_read(int fd, void *buf, size_t n) +{ +	char *s = buf; +	ssize_t res, pos = 0; + +	while (n > pos) { +		res = read(fd, s + pos, n - pos); +		switch (res) { +		case -1: +			if (errno == EINTR || errno == EAGAIN) +				continue; +		case 0: +			_exit(0); +		default: +			pos += res; +		} +	} +} + +/* Write data with the assertion that it all has to be written, or + * else abort the process.  Based on atomicio() from openssh. */ +static void +must_write(int fd, void *buf, size_t n) +{ +	char *s = buf; +	ssize_t res, pos = 0; + +	while (n > pos) { +		res = write(fd, s + pos, n - pos); +		switch (res) { +		case -1: +			if (errno == EINTR || errno == EAGAIN) +				continue; +		case 0: +			_exit(0); +		default: +			pos += res; +		} +	} +} diff --git a/contrib/pf/pflogd/privsep_fdpass.c b/contrib/pf/pflogd/privsep_fdpass.c new file mode 100644 index 000000000000..166b6930b3bd --- /dev/null +++ b/contrib/pf/pflogd/privsep_fdpass.c @@ -0,0 +1,120 @@ +/*	$OpenBSD: privsep_fdpass.c,v 1.1 2003/10/22 18:51:55 canacar Exp $	*/ + +/* + * Copyright 2001 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Copyright (c) 2002 Matthieu Herrb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + *    - Redistributions of source code must retain the above copyright + *      notice, this list of conditions and the following disclaimer. + *    - Redistributions in binary form must reproduce the above + *      copyright notice, this list of conditions and the following + *      disclaimer in the documentation and/or other materials provided + *      with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "pflogd.h" + +void +send_fd(int sock, int fd) +{ +	struct msghdr msg; +	char tmp[CMSG_SPACE(sizeof(int))]; +	struct cmsghdr *cmsg; +	struct iovec vec; +	int result = 0; +	ssize_t n; + +	memset(&msg, 0, sizeof(msg)); + +	if (fd >= 0) { +		msg.msg_control = (caddr_t)tmp; +		msg.msg_controllen = CMSG_LEN(sizeof(int)); +		cmsg = CMSG_FIRSTHDR(&msg); +		cmsg->cmsg_len = CMSG_LEN(sizeof(int)); +		cmsg->cmsg_level = SOL_SOCKET; +		cmsg->cmsg_type = SCM_RIGHTS; +		*(int *)CMSG_DATA(cmsg) = fd; +	} else { +		result = errno; +	} + +	vec.iov_base = &result; +	vec.iov_len = sizeof(int); +	msg.msg_iov = &vec; +	msg.msg_iovlen = 1; + +	if ((n = sendmsg(sock, &msg, 0)) == -1) +		warn("%s: sendmsg(%d)", __func__, sock); +	if (n != sizeof(int)) +		warnx("%s: sendmsg: expected sent 1 got %ld", +		    __func__, (long)n); +} + +int +receive_fd(int sock) +{ +	struct msghdr msg; +	char tmp[CMSG_SPACE(sizeof(int))]; +	struct cmsghdr *cmsg; +	struct iovec vec; +	ssize_t n; +	int result; +	int fd; + +	memset(&msg, 0, sizeof(msg)); +	vec.iov_base = &result; +	vec.iov_len = sizeof(int); +	msg.msg_iov = &vec; +	msg.msg_iovlen = 1; +	msg.msg_control = tmp; +	msg.msg_controllen = sizeof(tmp); + +	if ((n = recvmsg(sock, &msg, 0)) == -1) +		warn("%s: recvmsg", __func__); +	if (n != sizeof(int)) +		warnx("%s: recvmsg: expected received 1 got %ld", +		    __func__, (long)n); +	if (result == 0) { +		cmsg = CMSG_FIRSTHDR(&msg); +		if (cmsg->cmsg_type != SCM_RIGHTS) +			warnx("%s: expected type %d got %d", __func__, +			    SCM_RIGHTS, cmsg->cmsg_type); +		fd = (*(int *)CMSG_DATA(cmsg)); +		return fd; +	} else { +		errno = result; +		return -1; +	} +} | 
