From 22ac3ead26f49483410317f82ce328741b1cf975 Mon Sep 17 00:00:00 2001 From: Max Laier Date: Wed, 16 Jun 2004 23:39:33 +0000 Subject: [PATCH] Commit userland part of pf version 3.5 from OpenBSD (OPENBSD_3_5_BASE). --- contrib/pf/authpf/authpf.c | 142 +++-- contrib/pf/ftp-proxy/ftp-proxy.8 | 45 +- contrib/pf/ftp-proxy/ftp-proxy.c | 19 +- contrib/pf/man/pf.4 | 228 +++++-- contrib/pf/man/pflog.4 | 15 +- contrib/pf/man/pfsync.4 | 168 ++++- contrib/pf/pfctl/parse.y | 996 +++++++++++++++++++++--------- contrib/pf/pfctl/pf_print_state.c | 9 + contrib/pf/pfctl/pfctl.c | 564 +++++++++++------ contrib/pf/pfctl/pfctl.h | 17 +- contrib/pf/pfctl/pfctl_altq.c | 44 +- contrib/pf/pfctl/pfctl_parser.c | 404 ++++++++++-- contrib/pf/pfctl/pfctl_parser.h | 33 +- contrib/pf/pfctl/pfctl_qstats.c | 33 +- contrib/pf/pfctl/pfctl_table.c | 122 +++- contrib/pf/pflogd/pflogd.c | 488 +++++++++++---- contrib/pf/pflogd/privsep.c | 16 +- sbin/pfctl/Makefile | 5 +- sbin/pflogd/Makefile | 2 +- usr.bin/kdump/mkioctls | 1 + 20 files changed, 2479 insertions(+), 872 deletions(-) diff --git a/contrib/pf/authpf/authpf.c b/contrib/pf/authpf/authpf.c index c14bcf78f693..097e8f085dda 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). @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include "pathnames.h" @@ -98,12 +99,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) { @@ -131,7 +126,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) { @@ -160,6 +154,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)) { @@ -242,15 +248,22 @@ main(int argc, char *argv[]) seteuid(getuid()); setuid(getuid()); - if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) - do_death(0); - openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON); - if (config == NULL || read_config(config)) - do_death(0); - if (remove_stale_rulesets()) + if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) { + syslog(LOG_INFO, "user %s prohibited", luser); do_death(0); + } + + if (config == NULL || read_config(config)) { + syslog(LOG_INFO, "bad or nonexistent %s", PATH_CONFFILE); + do_death(0); + } + + 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); @@ -260,7 +273,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); @@ -545,15 +558,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; @@ -585,14 +603,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; } @@ -624,18 +639,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) { @@ -646,6 +661,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: " @@ -658,16 +677,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); @@ -682,6 +695,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); @@ -761,37 +776,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); } @@ -851,6 +873,13 @@ pfctl_set_logif(struct pfctl *pf, char *ifname) return (1); } +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) { @@ -865,6 +894,13 @@ pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit) return (1); } +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) @@ -875,10 +911,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 3fb0c4d7577f..db043cd91fc4 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. @@ -38,10 +38,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 @@ -67,6 +68,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 @@ -82,14 +103,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. @@ -98,6 +111,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 @@ -175,8 +196,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 87e9a65c535b..344ca27ec9d5 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 @@ -151,6 +151,7 @@ char *Group; extern int Debug_Level; extern int Use_Rdns; +extern in_addr_t Bind_Addr; extern char *__progname; typedef enum { @@ -174,9 +175,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); } @@ -976,9 +976,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/man/pf.4 b/contrib/pf/man/pf.4 index 07170134cced..053da27b3d61 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. .\" @@ -75,11 +75,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; @@ -95,16 +91,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 { @@ -123,7 +120,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. @@ -136,26 +133,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 @@ -227,8 +214,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" @@ -249,8 +234,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 { @@ -262,14 +255,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 @@ -288,7 +286,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; }; @@ -528,19 +526,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] @@ -549,6 +534,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" @@ -626,6 +651,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/pflog.4 b/contrib/pf/man/pflog.4 index c3f717342a80..6fc278e09938 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. @@ -32,19 +32,20 @@ .Nm pflog .Nd packet filter logging interface .Sh SYNOPSIS -.Sy device pflog +.Cd "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 9b22f5782202..5375a52c4466 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. @@ -32,19 +32,48 @@ .Nm pfsync .Nd packet filter states table logging interface .Sh SYNOPSIS -.Sy device pfsync +.Cd "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 @@ -63,16 +92,133 @@ struct pfsync_header { u_int8_t count; }; .Ed -.Sh EXAMPLES +.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 up -# tcpdump -s1500 -evtni pfsync0 +# 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 +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 5a53bacc7f76..2c82bcd10602 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 @@ -75,6 +77,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, @@ -120,11 +123,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; @@ -159,6 +171,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 { @@ -219,23 +232,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 *, @@ -276,8 +303,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; @@ -315,7 +343,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; @@ -327,10 +354,6 @@ typedef struct { struct node_host *host; struct range rport; } *redirection; - struct { - int type; - struct pf_poolhashkey *key; - } pooltype; struct { int action; struct node_state_opt *options; @@ -339,6 +362,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; @@ -348,6 +372,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; @@ -365,6 +390,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 @@ -374,24 +403,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 STRING %token PORTBINARY %type interface if_list if_item_not if_item %type number icmptype icmp6type uid gid %type tos not yesno natpass -%type no dir log af fragcache -%type staticport unaryop +%type no dir log af fragcache sourcetrack +%type unaryop statelock %type action nataction flags flag blockspec %type port rport %type hashkey -%type pooltype %type proto proto_list proto_item %type icmpspec %type icmp_list icmp_item @@ -424,6 +453,7 @@ typedef struct { %type queue_opts queue_opt queue_opts_l %type scrub_opts scrub_opt scrub_opts_l %type table_opts table_opt table_opts_l +%type pool_opts pool_opt pool_opts_l %% ruleset : /* empty */ @@ -444,26 +474,45 @@ ruleset : /* empty */ ; option : SET OPTIMIZATION STRING { - if (check_rulestate(PFCTL_STATE_OPTION)) - YYERROR; - if (pfctl_set_optimization(pf, $3) != 0) { - yyerror("unknown optimization %s", $3); + 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; } } @@ -490,12 +539,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)) - YYERROR; - if (pfctl_file_fingerprints(pf->dev, pf->opts, $3)) { - yyerror("error loading fingerprints %s", $3); + 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); } ; @@ -513,19 +594,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); @@ -536,10 +630,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; @@ -553,10 +650,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; @@ -591,10 +691,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) { @@ -626,18 +729,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; } @@ -676,13 +782,6 @@ scrubrule : SCRUB dir logquick interface af proto fromto scrub_opts YYERROR; } - if ($4) { - if ($4->not) { - yyerror("scrub rules do not support " - "'! '"); - YYERROR; - } - } r.af = $5; if ($8.nodf) r.rule_flag |= PFRULE_NODF; @@ -712,7 +811,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); @@ -764,8 +863,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; @@ -814,10 +916,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)); @@ -829,11 +932,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); @@ -853,7 +956,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); @@ -876,6 +979,7 @@ antispoof_opt : label { not : '!' { $$ = 1; } | /* empty */ { $$ = 0; } + ; tabledef : TABLE '<' STRING '>' table_opts { struct node_host *h, *nh; @@ -884,11 +988,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) @@ -898,7 +1006,7 @@ tabledef : TABLE '<' STRING '>' table_opts { free(h); } nti = SIMPLEQ_NEXT(ti, entries); - free (ti); + free(ti); } } ; @@ -907,7 +1015,7 @@ table_opts : { bzero(&table_opts, sizeof table_opts); SIMPLEQ_INIT(&table_opts.init_nodes); } - table_opts_l + table_opts_l { $$ = table_opts; } | /* empty */ { @@ -926,8 +1034,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 '}' { @@ -935,7 +1046,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: @@ -1001,8 +1112,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)); @@ -1010,8 +1123,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; @@ -1038,7 +1153,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); @@ -1128,17 +1243,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; @@ -1184,8 +1303,10 @@ cbqflags_item : STRING { $$ = CBQCLF_RIO; else { yyerror("unknown cbq flag \"%s\"", $1); + free($1); YYERROR; } + free($1); } ; @@ -1204,8 +1325,10 @@ priqflags_item : STRING { $$ = PRCF_RIO; else { yyerror("unknown priq flag \"%s\"", $1); + free($1); YYERROR; } + free($1); } ; @@ -1213,7 +1336,7 @@ hfsc_opts : { bzero(&hfsc_opts, sizeof(struct node_hfsc_opts)); } - hfscopts_list { + hfscopts_list { $$ = hfsc_opts; } ; @@ -1287,8 +1410,10 @@ hfscopts_item : LINKSHARE bandwidth { hfsc_opts.flags |= HFCF_RIO; else { yyerror("unknown hfsc flag \"%s\"", $1); + free($1); YYERROR; } + free($1); } ; @@ -1311,22 +1436,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; @@ -1357,14 +1486,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; @@ -1394,7 +1523,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 @@ -1415,6 +1544,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 " @@ -1429,6 +1617,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; @@ -1457,17 +1659,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"); @@ -1501,7 +1710,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); @@ -1628,22 +1837,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; @@ -1687,19 +1906,29 @@ 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) { +#ifndef __FreeBSD__ yyerror("unknown interface %s", $1); + free($1); YYERROR; +#endif } $$ = calloc(1, sizeof(struct node_if)); if ($$ == NULL) err(1, "if_item: calloc"); if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >= sizeof($$->ifname)) { + free($1); free($$); yyerror("interface name too long"); YYERROR; } + free($1); +#ifdef __FreeBSD__ + if (n == NULL) + $$->ifa_flags = PF_IFA_FLAG_DYNAMIC; + else /* XXX ugly */ +#endif $$->ifa_flags = n->ifa_flags; $$->not = 0; $$->next = NULL; @@ -1710,6 +1939,7 @@ if_item : STRING { af : /* empty */ { $$ = 0; } | INET { $$ = AF_INET; } | INET6 { $$ = AF_INET6; } + ; proto : /* empty */ { $$ = NULL; } | PROTO proto_item { $$ = $2; } @@ -1731,6 +1961,7 @@ proto_item : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 255) { yyerror("protocol outside range"); + free($1); YYERROR; } pr = (u_int8_t)ulval; @@ -1740,10 +1971,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; @@ -1864,9 +2097,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 { @@ -1874,6 +2109,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); @@ -1892,7 +2128,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)); @@ -1903,6 +2140,7 @@ host : STRING { sizeof($$->addr.v.tblname)) >= sizeof($$->addr.v.tblname)) errx(1, "host: strlcpy"); + free($2); $$->next = NULL; $$->tail = $$; } @@ -1913,30 +2151,65 @@ 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) { - yyerror("interface %s does not exist", $2); + 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")) { +#ifndef __FreeBSD__ + yyerror("interface %s does not exist", $2); + free(op); + YYERROR; +#endif + } $$ = calloc(1, sizeof(struct node_host)); if ($$ == NULL) err(1, "address: calloc"); $$->af = 0; set_ipmask($$, 128); $$->addr.type = PF_ADDR_DYNIFTL; + $$->addr.iflags = flags; if (strlcpy($$->addr.v.ifname, $2, sizeof($$->addr.v.ifname)) >= sizeof($$->addr.v.ifname)) { + free(op); free($$); yyerror("interface name too long"); YYERROR; } + free(op); $$->next = NULL; $$->tail = $$; } @@ -2007,6 +2280,7 @@ port : STRING { if (p == NULL) { if (atoul($1, &ulval) == 0) { if (ulval > 65535) { + free($1); yyerror("illegal port value %d", ulval); YYERROR; @@ -2018,6 +2292,7 @@ port : STRING { s = getservbyname($1, "udp"); if (s == NULL) { yyerror("unknown port %s", $1); + free($1); YYERROR; } $$.a = s->s_port; @@ -2029,12 +2304,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); } ; @@ -2103,17 +2381,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); } ; @@ -2182,6 +2463,7 @@ gid : STRING { if ((grp = getgrnam($1)) == NULL) { yyerror("unknown group %s", $1); + free($1); YYERROR; } $$ = grp->gr_gid; @@ -2189,10 +2471,12 @@ gid : STRING { } else { if (ulval >= GID_MAX) { yyerror("illegal gid value %lu", ulval); + free($1); YYERROR; } $$ = ulval; } + free($1); } ; @@ -2201,8 +2485,10 @@ flag : STRING { if ((f = parse_flags($1)) < 0) { yyerror("bad flags %s", $1); + free($1); YYERROR; } + free($1); $$.b1 = f; } ; @@ -2249,6 +2535,7 @@ icmp_item : icmptype { if (atoul($3, &ulval) == 0) { if (ulval > 255) { + free($3); yyerror("illegal icmp-code %d", ulval); YYERROR; } @@ -2256,10 +2543,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"); @@ -2289,16 +2578,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"); @@ -2317,6 +2609,7 @@ icmptype : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 255) { yyerror("illegal icmp-type %d", ulval); + free($1); YYERROR; } $$ = ulval + 1; @@ -2324,10 +2617,12 @@ icmptype : STRING { if ((p = geticmptypebyname($1, AF_INET)) == NULL) { yyerror("unknown icmp-type %s", $1); + free($1); YYERROR; } $$ = p->type + 1; } + free($1); } ; @@ -2338,6 +2633,7 @@ icmp6type : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 255) { yyerror("illegal icmp6-type %d", ulval); + free($1); YYERROR; } $$ = ulval + 1; @@ -2345,10 +2641,12 @@ icmp6type : STRING { if ((p = geticmptypebyname($1, AF_INET6)) == NULL) { yyerror("unknown icmp6-type %s", $1); + free($1); YYERROR; } $$ = p->type + 1; } + free($1); } ; @@ -2365,8 +2663,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; } ; @@ -2374,7 +2690,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; } @@ -2405,6 +2721,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; @@ -2413,12 +2773,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"); @@ -2431,23 +2794,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; } ; @@ -2459,24 +2818,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); } ; @@ -2523,6 +2889,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; @@ -2535,6 +2902,7 @@ hashkey : /* empty */ &$$->key32[0], &$$->key32[1], &$$->key32[2], &$$->key32[3]) != 4) { free($$); + free($1); yyerror("invalid hex key"); YYERROR; } @@ -2553,38 +2921,67 @@ hashkey : /* empty */ HTONL($$->key32[2]); HTONL($$->key32[3]); } + free($1); } ; -pooltype : /* empty */ - { - $$.type = PF_POOL_NONE; - $$.key = NULL; - } - | BITMASK - { - $$.type = PF_POOL_BITMASK; - $$.key = NULL; - } - | RANDOM - { - $$.type = PF_POOL_RANDOM; - $$.key = NULL; - } - | SOURCEHASH hashkey - { - $$.type = PF_POOL_SRCHASH; - $$.key = $2; - } - | ROUNDROBIN - { - $$.type = PF_POOL_ROUNDROBIN; - $$.key = NULL; +pool_opts : { bzero(&pool_opts, sizeof pool_opts); } + pool_opts_l + { $$ = pool_opts; } + | /* empty */ { + bzero(&pool_opts, sizeof pool_opts); + $$ = pool_opts; } ; -staticport : /* empty */ { $$ = 0; } - | STATICPORT { $$ = 1; } +pool_opts_l : pool_opts_l pool_opt + | pool_opt + ; + +pool_opt : BITMASK { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_BITMASK; + } + | RANDOM { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_RANDOM; + } + | SOURCEHASH hashkey { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_SRCHASH; + pool_opts.key = $2; + } + | ROUNDROBIN { + if (pool_opts.type) { + yyerror("pool type cannot be redefined"); + YYERROR; + } + pool_opts.type = PF_POOL_ROUNDROBIN; + } + | STATICPORT { + if (pool_opts.staticport) { + yyerror("static-port cannot be redefined"); + YYERROR; + } + pool_opts.staticport = 1; + } + | STICKYADDRESS { + if (filter_opts.marker & POM_STICKYADDRESS) { + yyerror("sticky-address cannot be redefined"); + YYERROR; + } + pool_opts.marker |= POM_STICKYADDRESS; + pool_opts.opts |= PF_POOL_STICKYADDR; + } ; redirection : /* empty */ { $$ = NULL; } @@ -2626,8 +3023,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; @@ -2650,7 +3046,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); @@ -2686,14 +3082,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] = @@ -2709,30 +3108,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; - } } } @@ -2740,7 +3140,10 @@ natrule : nataction interface af proto fromto tag redirpool pooltype memcpy(&r.rpool.key, $8.key, sizeof(struct pf_poolhashkey)); - if ($9 != 0) { + 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"); @@ -2767,7 +3170,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; @@ -2798,11 +3201,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; @@ -2816,10 +3220,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) { @@ -2906,18 +3318,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); @@ -2925,12 +3337,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; @@ -2967,24 +3376,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; } @@ -2992,12 +3401,16 @@ route : /* empty */ { timeout_spec : STRING number { - if (check_rulestate(PFCTL_STATE_OPTION)) - YYERROR; - if (pfctl_set_timeout(pf, $1, $2, 0) != 0) { - yyerror("unknown timeout %s", $1); + 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); } ; @@ -3007,13 +3420,18 @@ timeout_list : timeout_list comma timeout_spec limit_spec : STRING number { - if (check_rulestate(PFCTL_STATE_OPTION)) - YYERROR; - if (pfctl_set_limit(pf, $1, $2) != 0) { - yyerror("unable to set limit %s %u", $1, $2); + 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 @@ -3027,9 +3445,13 @@ yesno : NO { $$ = 0; } | STRING { if (!strcmp($1, "yes")) $$ = 1; - else + else { + free($1); YYERROR; + } + free($1); } + ; unaryop : '=' { $$ = PF_OP_EQ; } | '!' '=' { $$ = PF_OP_NE; } @@ -3067,6 +3489,17 @@ disallow_table(struct node_host *h, const char *fmt) return (0); } +int +disallow_alias(struct node_host *h, const char *fmt) +{ + for (; h != NULL; h = h->next) + if (DYNIF_MULTIADDR(h->addr)) { + yyerror(fmt, h->addr.v.tblname); + return (1); + } + return (0); +} + int rule_consistent(struct pf_rule *r) { @@ -3104,10 +3537,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"); @@ -3124,22 +3553,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"); @@ -3149,12 +3566,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++; } @@ -3164,31 +3585,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) { @@ -3209,28 +3612,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); } @@ -3314,38 +3695,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]; @@ -3377,7 +3761,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); @@ -3391,14 +3775,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] = ""; @@ -3423,12 +3808,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]; @@ -3436,38 +3821,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 @@ -3733,14 +4118,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, @@ -3761,9 +4154,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) @@ -3771,18 +4164,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); @@ -3808,6 +4214,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; @@ -3914,6 +4327,7 @@ lookup(char *s) { "cbq", CBQ}, { "code", CODE}, { "crop", FRAGCROP}, + { "debug", DEBUG}, { "drop", DROP}, { "drop-ovl", FRAGDROP}, { "dup-to", DUPTO}, @@ -3921,13 +4335,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}, @@ -3941,6 +4360,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}, @@ -3948,6 +4369,7 @@ lookup(char *s) { "no", NO}, { "no-df", NODF}, { "no-route", NOROUTE}, + { "no-sync", NOSYNC}, { "on", ON}, { "optimization", OPTIMIZATION}, { "os", OS}, @@ -3974,11 +4396,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}, @@ -4202,10 +4628,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') { @@ -4220,7 +4645,7 @@ top: int parse_rules(FILE *input, struct pfctl *xpf) { - struct sym *sym; + struct sym *sym, *next; fin = input; pf = xpf; @@ -4236,13 +4661,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); @@ -4380,9 +4807,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"); @@ -4479,7 +4907,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; @@ -4488,7 +4916,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 b7cf5ca9ac5e..318bde756f31 100644 --- a/contrib/pf/pfctl/pf_print_state.c +++ b/contrib/pf/pfctl/pf_print_state.c @@ -30,8 +30,12 @@ * */ +#include +__FBSDID("$FreeBSD$"); + #include #include +#include #include #define TCPSTATES #include @@ -284,8 +288,13 @@ print_state(struct pf_state *s, int opts) printf("\n"); } if (opts & PF_OPT_VERBOSE2) { +#ifdef __FreeBSD__ + printf(" id: %016llx creatorid: %08x\n", + (long long)be64toh(s->id), ntohl(s->creatorid)); +#else printf(" id: %016llx creatorid: %08x\n", betoh64(s->id), ntohl(s->creatorid)); +#endif } } diff --git a/contrib/pf/pfctl/pfctl.c b/contrib/pf/pfctl/pfctl.c index 418b51ff76d6..6abbbeca020d 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 @@ -56,6 +57,10 @@ __FBSDID("$FreeBSD$"); #include "pfctl_parser.h" #include "pfctl.h" +#ifdef __FreeBSD__ +#define HTONL(x) (x) = htonl((__uint32_t)(x)) +#endif + void usage(void); int pfctl_enable(int, int); int pfctl_disable(int, int); @@ -63,17 +68,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); @@ -85,6 +92,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; @@ -93,6 +102,8 @@ int loadopt; int altqsupport; int dev = -1; +int first_title = 1; +int labels = 0; const char *infile; @@ -101,6 +112,7 @@ static const struct { int index; } pf_limits[] = { { "states", PF_LIMIT_STATES }, + { "src-nodes", PF_LIMIT_SRC_NODES }, { "frags", PF_LIMIT_FRAGS }, { NULL, 0 } }; @@ -159,12 +171,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[] = { @@ -182,12 +196,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); } @@ -246,7 +262,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; @@ -276,19 +292,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); @@ -297,7 +307,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; @@ -327,24 +337,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); @@ -353,32 +353,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)) - err(1, "DIOCCLRSTATES"); + if (ioctl(dev, DIOCCLRSRCNODES)) + err(1, "DIOCCLRSRCNODES"); if ((opts & PF_OPT_QUIET) == 0) - fprintf(stderr, "states cleared\n"); + fprintf(stderr, "source tracking entries cleared\n"); return (0); } int -pfctl_kill_states(int dev, int opts) +pfctl_clear_states(int dev, const char *iface, int opts) +{ + struct pfioc_state_kill psk; + + memset(&psk, 0, sizeof(psk)); + if (iface != NULL && strlcpy(psk.psk_ifname, iface, + sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) + errx(1, "invalid interface: %s", iface); + + if (ioctl(dev, DIOCCLRSTATES, &psk)) + err(1, "DIOCCLRSTATES"); + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "%d states cleared\n", psk.psk_af); + return (0); +} + +int +pfctl_kill_states(int dev, const char *iface, int opts) { struct pfioc_state_kill psk; struct addrinfo *res[2], *resp[2]; @@ -393,6 +411,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)); @@ -426,7 +447,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]; @@ -557,12 +579,21 @@ pfctl_print_rule_counters(struct pf_rule *rule, int opts) (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 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) { @@ -579,6 +610,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; @@ -595,11 +628,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; @@ -623,6 +670,8 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname, } 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); } @@ -656,6 +705,8 @@ pfctl_show_rules(int dev, int opts, int format, char *anchorname, } 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); } @@ -670,7 +721,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; @@ -718,6 +769,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); @@ -727,21 +782,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"); @@ -758,16 +856,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; @@ -775,16 +877,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; @@ -802,14 +908,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); @@ -846,7 +954,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: @@ -884,12 +993,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) @@ -922,26 +1038,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, @@ -957,46 +1078,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: " @@ -1004,57 +1132,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 @@ -1072,7 +1173,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)) { @@ -1190,6 +1291,55 @@ pfctl_set_logif(struct pfctl *pf, char *ifname) return (0); } +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) { @@ -1264,14 +1414,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 { @@ -1288,8 +1439,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)) @@ -1323,8 +1472,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; @@ -1353,6 +1502,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"); @@ -1387,6 +1539,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) { @@ -1436,14 +1591,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(); @@ -1482,11 +1631,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; @@ -1521,32 +1673,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; @@ -1557,6 +1715,9 @@ main(int argc, char *argv[]) pfctl_load_fingerprints(dev, opts); pfctl_show_fingerprints(opts); break; + case 'I': + pfctl_show_ifaces(ifaceopt, opts); + break; } } @@ -1572,7 +1733,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); @@ -1580,11 +1744,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); @@ -1595,7 +1762,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, @@ -1608,7 +1775,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 7ec0c3b52c46..2776f5e94310 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 @@ -34,7 +34,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 */ @@ -58,7 +59,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); @@ -75,13 +76,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); #ifdef __FreeBSD__ extern int altqsupport; @@ -116,5 +121,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 534902b967e1..eeed6483a8ab 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 @@ -89,8 +89,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) { @@ -165,14 +163,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] != '\0') { + 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 "); @@ -272,6 +270,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); @@ -421,8 +421,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); @@ -496,9 +494,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; @@ -506,10 +507,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); } @@ -604,9 +605,6 @@ eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) } } - if (pa->qid == 0) - pa->qid = ++max_qid; - return (0); } @@ -676,13 +674,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); @@ -729,7 +725,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); } @@ -740,7 +736,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); } @@ -749,7 +745,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"); @@ -760,7 +756,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"); @@ -1020,7 +1016,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_parser.c b/contrib/pf/pfctl/pfctl_parser.c index 61b47f5fae7b..203ec7e9d6eb 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 @@ -34,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -196,6 +198,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 } }; @@ -255,7 +258,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]); @@ -462,23 +465,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; @@ -488,22 +495,26 @@ 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"); @@ -539,6 +550,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], @@ -551,6 +576,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) { @@ -588,7 +664,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) @@ -707,7 +783,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]) @@ -718,6 +800,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) @@ -885,6 +1007,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"); @@ -903,7 +1027,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; @@ -925,6 +1050,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, @@ -936,6 +1065,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; } @@ -950,15 +1083,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(); @@ -966,14 +1142,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; @@ -983,23 +1173,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 (mode == PFCTL_IFLOOKUP_BCAST && p->af != AF_INET) + if ((flags & PFI_AFLAG_BROADCAST) && p->af != AF_INET) continue; - if (mode == PFCTL_IFLOOKUP_NET && p->ifindex > 0) + if ((flags & PFI_AFLAG_BROADCAST) && + !(p->ifa_flags & IFF_BROADCAST)) continue; + if ((flags & PFI_AFLAG_PEER) && + !(p->ifa_flags & IFF_POINTOPOINT)) + continue; + if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0) + continue; + if (last_if == NULL || strcmp(last_if, p->ifname)) + got4 = got6 = 0; + last_if = p->ifname; + if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET && got4) + continue; + if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6) + continue; + if (p->af == AF_INET) + got4 = 1; + else + got6 = 1; n = calloc(1, sizeof(struct node_host)); if (n == NULL) err(1, "address: calloc"); n->af = p->af; - if (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) { @@ -1024,9 +1235,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); } @@ -1084,29 +1292,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); } @@ -1120,27 +1338,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; -#if defined(__FreeBSD__) && (__FreeBSD_version <= 501106) - /* inet_net_pton acts strange w/ multicast addresses, RFC1112 */ - if (mask == -1 && h->addr.v.a.addr.addr8[0] >= 224 && - h->addr.v.a.addr.addr8[0] < 240) - bits = 32; -#endif - 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); } @@ -1179,12 +1397,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); @@ -1192,6 +1418,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"); @@ -1223,6 +1460,7 @@ host_dns(const char *s, int v4mask, int v6mask) } } freeaddrinfo(res0); + free(ps); return (h); } @@ -1296,3 +1534,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 6911a47fde9c..0756cc867b12 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 @@ -46,6 +46,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 @@ -67,19 +68,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; @@ -91,6 +86,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 */ @@ -99,6 +95,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; @@ -168,7 +168,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 *); @@ -179,15 +179,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 *); @@ -195,9 +198,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); @@ -242,8 +245,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 0e8b61f89794..a46603cdb3ef 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 @@ -84,28 +84,40 @@ 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); #ifdef __FreeBSD__ if (!altqsupport) return (-1); #endif - 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); @@ -162,7 +174,7 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root) } } } - return (0); + return (mnr); } void @@ -252,12 +264,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 diff --git a/contrib/pf/pfctl/pfctl_table.c b/contrib/pf/pfctl/pfctl_table.c index ad4d53abb9c7..4aa606d17b17 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 @@ -64,12 +64,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)) && \ @@ -118,12 +125,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(); @@ -168,6 +175,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); @@ -246,7 +257,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 (;;) { @@ -328,9 +339,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); @@ -453,12 +464,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; @@ -481,7 +491,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; } @@ -526,3 +536,83 @@ 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); +#ifdef __FreeBSD__ + first = 1; + oprintf(flags, PFI_IFLAG_PLACEHOLDER, "placeholder", &first, 1); +#endif + 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.c b/contrib/pf/pflogd/pflogd.c index e69c4ae2b22b..54a2572d8924 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 @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -53,20 +54,14 @@ __FBSDID("$FreeBSD$"); #include #endif -#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; @@ -79,15 +74,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) @@ -136,7 +159,7 @@ __dead void #endif 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); } @@ -159,37 +182,64 @@ sig_alrm(int sig) gotsig_alrm = 1; } -int -init_pcap(void) +void +set_pcap_filter(void) { struct bpf_program bprog; - pcap_t *oldhpcap = hpcap; - - 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); + 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); + return (-1); + } 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); + +#ifdef __FreeBSD__ + /* We can not lock bpf devices ... yet */ +#else + /* lock */ + if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) { + logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); + return (-1); + } +#endif + + 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); } @@ -198,45 +248,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; @@ -248,58 +304,228 @@ reset_dump(void) hdr.linktype = hpcap->linktype; if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { - dpcap = NULL; - fclose(fp); - return (-1); - } - return (0); - } - - /* - * XXX 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. - */ - (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) { + } + } 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; + + /* + * 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. 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) { + 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) { +#ifdef __FreeBSD__ + /* another ?paranoid? safety measure we do not have */ +#else + closefrom(STDERR_FILENO + 1); +#endif + + while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) { switch (ch) { case 'D': Debug = 1; @@ -316,6 +542,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(); @@ -338,6 +569,27 @@ main(int argc, char **argv) (void)umask(S_IRWXG | S_IRWXO); + /* 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); + } + + /* 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); @@ -345,25 +597,29 @@ main(int argc, char **argv) signal(SIGHUP, sig_hup); alarm(delay); - if (argc) { - filter = copy_argv(argv); - if (filter == NULL) - logmsg(LOG_NOTICE, "Failed to form filter expression"); - } + buffer = malloc(PFLOGD_BUFSIZE); - if (init_pcap()) { - logmsg(LOG_ERR, "Exiting, init failure"); - exit(1); + 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()) { - logmsg(LOG_ERR, "Failed to open log file %s", filename); - pcap_close(hpcap); - exit(1); - } + 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)); @@ -371,38 +627,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/privsep.c b/contrib/pf/pflogd/privsep.c index 50807ada4e45..3dfba32689bd 100644 --- a/contrib/pf/pflogd/privsep.c +++ b/contrib/pf/pflogd/privsep.c @@ -16,11 +16,13 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include + +#include +__FBSDID("$FreeBSD$"); + +#include #include #include -#include #include #include @@ -28,13 +30,13 @@ #include #include #include -#include -#include #include #include #include #include #include +#include +#include #include #include #include "pflogd.h" @@ -70,7 +72,11 @@ priv_init(void) int snaplen, ret; struct passwd *pw; +#ifdef __FreeBSD__ + for (i = 1; i < NSIG; i++) +#else for (i = 1; i < _NSIG; i++) +#endif signal(i, SIG_DFL); /* Create sockets */ diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile index 3c40c93cd193..3be40874c2ac 100644 --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -12,10 +12,11 @@ SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c CFLAGS+= -Wall -Wmissing-prototypes -Wno-uninitialized CFLAGS+= -Wstrict-prototypes -I${.CURDIR}/../../contrib/pf/pfctl +CFLAGS+= -I${.CURDIR}/../../sys/contrib/pf # XXX ALTQ -#CFLAGS+= -DENABLE_ALTQ -CFLAGS+= -I${.CURDIR}/missing +CFLAGS+= -DENABLE_ALTQ +#CFLAGS+= -I${.CURDIR}/missing YFLAGS= diff --git a/sbin/pflogd/Makefile b/sbin/pflogd/Makefile index 0855e8c41f6e..285387a18377 100644 --- a/sbin/pflogd/Makefile +++ b/sbin/pflogd/Makefile @@ -3,7 +3,7 @@ .PATH: ${.CURDIR}/../../contrib/pf/pflogd PROG= pflogd -SRCS= pflogd.c pidfile.c +SRCS= pflogd.c pidfile.c privsep.c privsep_fdpass.c MAN= pflogd.8 CFLAGS+=-Wall -Werror -Wmissing-prototypes -Wshadow diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls index ac132a3343e8..cbe751503009 100644 --- a/usr.bin/kdump/mkioctls +++ b/usr.bin/kdump/mkioctls @@ -41,6 +41,7 @@ BEGIN { print "#include " print "#include " print "#include " + print "#include " print "#include " print "#include " print "#include "